用AJAX開發(fā)智能Web應(yīng)用程序之基礎(chǔ)篇
一. 什么是AJAX?
這個(gè)名字代表了異步JavaScript+XMLHTTPRequest,,并且意味著你可以在基于瀏覽器的JavaScript和服務(wù)器之間建立套接字通訊,。其實(shí)AJAX并不是一種新技術(shù),而是已經(jīng)成功地用于現(xiàn)代瀏覽器中的若干成功技術(shù)的可能性組合,。所有的AJAX應(yīng)用程序?qū)崿F(xiàn)了一種“豐富的”UI——這是通過JavaScript操作HTML文檔對(duì)象模型并且經(jīng)由XMLHttpRequest實(shí)現(xiàn)的精確定位的數(shù)據(jù)檢索來實(shí)現(xiàn)的,。典型的示例AJAX應(yīng)用程序是Google Labs(http://labs.google.com)的Google Maps和Google Suggest。這些應(yīng)用程序現(xiàn)場監(jiān)視用戶輸入并且提供實(shí)時(shí)的頁面更新,。最重要的是,,在用戶通過地圖導(dǎo)航或輸入一個(gè)查找字符串的同時(shí),這些事件不需要刷新頁面,。 事實(shí)上,,支持這些令人感到驚訝的應(yīng)用的技術(shù)已經(jīng)出現(xiàn)一段時(shí)間了,盡管它們要求復(fù)雜的技能以及使用瀏覽器的技巧,。一些專利產(chǎn)品就提供了相似的能力——如Macromedia Flash插件,,Java Applets或.NET運(yùn)行時(shí)——在達(dá)到實(shí)用上已經(jīng)有一段時(shí)間了。把一種可與服務(wù)器通話的腳本組件引入到瀏覽器中的思想早在IE 5.0中就已經(jīng)存在,。Firefox和其它流行的瀏覽器也加入到瀏覽器大軍中并以一種內(nèi)置對(duì)象形式支持XMLHTTPRequest,。隨著跨平臺(tái)瀏覽器的出現(xiàn),這些技術(shù)得到了認(rèn)可并在2004年3月一家稱為Adaptive Path的公司中正式提出了AJAX,。 簡而言之,,由于來自于Google的支持和安裝了一點(diǎn)可用的瀏覽器技術(shù),加上為了一種"更好的用戶體驗(yàn)",,每個(gè)人都在把客戶端技術(shù)添加到Web應(yīng)用程序上,。 二. AJAX與傳統(tǒng)應(yīng)用程序的區(qū)別 一個(gè)傳統(tǒng)Web應(yīng)用程序模型實(shí)際上是一種基本的事件——用戶被迫提交表單以實(shí)現(xiàn)頁面交換。也就是說,,表單提交和頁面?zhèn)魉蜔o法得到保證:還有更壞的情形——用戶需要再次點(diǎn)擊,。這與AJAX截然不同-——數(shù)據(jù)跨過線路而不是完整的HTML頁面?zhèn)鬏敗_@種數(shù)據(jù)交換是經(jīng)由特定的瀏覽器對(duì)象:XMLHttpRequest實(shí)現(xiàn)的;再由適當(dāng)?shù)倪壿媮硖幚砻總€(gè)數(shù)據(jù)請求的結(jié)果,,頁面的特定區(qū)域而不是完整的頁面被更新,。結(jié)果是更快的速度,更少的擁擠和更好的信息傳送控制,。 傳統(tǒng)型"click-refresh"Web應(yīng)用程序強(qiáng)迫用戶中斷工作過程而等待頁面的重裝,。通過引入AJAX技術(shù),一個(gè)客戶端腳本能夠異步地與服務(wù)器通話,,而用戶仍能保持輸入數(shù)據(jù),。除了對(duì)用戶透明之外,,這樣的異步意味著服務(wù)器可以有更多時(shí)間來處理請求。 傳統(tǒng)Web應(yīng)用程序把所有的處理代理到服務(wù)器并且強(qiáng)迫服務(wù)器進(jìn)行狀態(tài)管理,。AJAX允許靈活劃分應(yīng)用程序邏輯以及客戶和服務(wù)器之間的狀態(tài)管理,。這就消除了一種"click-refresh"依賴性并且提供更好的服務(wù)器可伸縮性。當(dāng)該狀態(tài)存儲(chǔ)在客戶端,,你就不必跨越服務(wù)器來維持會(huì)話或保存/結(jié)束狀態(tài)-其使用期限是由客戶端來定義的,。 三. AJAX——分布式的MVC 盡管AJAX應(yīng)用程序依靠JavaScript來實(shí)現(xiàn)描述層,然而處理能力和知識(shí)庫仍然存在于服務(wù)器上,。此時(shí),,AJAX應(yīng)用程序大量的與J2EE服務(wù)器通訊——把數(shù)據(jù)輸入/輸出Web服務(wù)和servlets。具有基于AJAX的描述層的J2EE應(yīng)用程序和標(biāo)準(zhǔn)J2EE應(yīng)用程序之間的區(qū)別首先在于,,MVC是通過線路分布的,。通過使用AJAX,視圖是本地的,,而模型和控制器是分布式的——這使得開發(fā)者能夠靈活地決定哪些部件會(huì)是基于客戶端的,。具體地說,本地視圖通過巧妙地操作HTML DOM而生成圖形;控制器局部地處理用戶輸入并且根據(jù)開發(fā)者的判斷擴(kuò)展到服務(wù)器的處理——經(jīng)由HTTP請求(Web服務(wù),,XML/RPC或其它)實(shí)現(xiàn);模型的遠(yuǎn)程部分是根據(jù)客戶端需要而下載的以達(dá)到實(shí)時(shí)更新客戶端頁面;并且狀態(tài)是在客戶端收集的,。 在以后的AJAX文章中,我們將比較深入地討論這里的每一種組件并提供有關(guān)它們聯(lián)合在一起進(jìn)行應(yīng)用的示例?,F(xiàn)在,,先不多說,讓我們詳細(xì)地分析一個(gè)簡單的AJAX示例,。 四. 郵政區(qū)號(hào)校驗(yàn)和查詢 我們將創(chuàng)建一個(gè)包含三個(gè)INPUT字段(Zip,,City和State)的HTML頁面。我們將保證,,只要用戶輸入郵政區(qū)號(hào)的前三個(gè)數(shù)字,,該頁面上的字段就會(huì)用第一個(gè)匹配的狀態(tài)值填充。一旦用戶輸入了所有五位郵政區(qū)號(hào)數(shù),,我們將立即決定和填充相應(yīng)的城市。如果郵政區(qū)號(hào)無效(在服務(wù)器的數(shù)據(jù)庫沒有找到),,那么我們將把郵政區(qū)號(hào)的邊界設(shè)置為紅色,。這樣的可視化線索有助于用戶并且在現(xiàn)代瀏覽器中已經(jīng)成為一種標(biāo)準(zhǔn)(作為一實(shí)例,當(dāng)Firefox找到一個(gè)HTML頁面中的匹配關(guān)鍵字時(shí),,它會(huì)高亮與你在瀏覽器查找域輸入的內(nèi)容一致的部分),。 讓我們首先創(chuàng)建一個(gè)簡單的包含三個(gè)INPUT字段的HTML:zip,city和state,。請注意,,一旦一個(gè)字符輸入進(jìn)郵政區(qū)號(hào)字段域中,,即調(diào)用方法zipChanged()。JavaScript函數(shù)zipChanged()(見下)在當(dāng)zip長度為3時(shí)調(diào)用函數(shù)updateState(),,而在當(dāng)zip長度為5時(shí)調(diào)用函數(shù)up-dateCity(),。而updateCity()和updateState()把大部分的工作代理到另一個(gè)函數(shù)ask()。 Zip:<input id="zipcode" type="text" maxlength="5" onKeyUp="zipChanged()" HTTPRequest = function () { function ask(url,, fieldToFill,, lookupField) { function handleHttpResponse(http,, fieldToFill,, lookupField) { 為ask()所使用的HttpRequest()函數(shù)(見上)是一跨瀏覽器的XMLHTTPRequest的一個(gè)實(shí)例的構(gòu)造器;稍后我們將分析它。到目前為止,請注意對(duì)于handleResponse()的調(diào)用是如何用一匿名函數(shù)包裝的-這個(gè)函數(shù)是function(){handleHttpResponse(http,,fieldToFill,, lookupField)}。 該函數(shù)的代碼是動(dòng)態(tài)創(chuàng)建的并且在每次我們給http.onreadstatechange屬性賦值時(shí)被編譯,。結(jié)果,,JavaScript創(chuàng)建一個(gè)指向上下文(所有的變量都可以存取正在結(jié)束的方法-ask())的指針。這樣以來,,匿名函數(shù)和handleResponse()就能夠被保證充分存取所有的上下文宿主的變量,,直至到匿名函數(shù)的參考被垃圾回收站收集為止。換句話說,,無論何時(shí)我們的匿名函數(shù)被調(diào)用,,它都能無縫地參考request,fieldToFill和lookupField變量,,就象它們是全局的一樣,。而且,每次ask()調(diào)用都將創(chuàng)建環(huán)境的一個(gè)獨(dú)立拷貝,,并且此時(shí)這些變量中保存有該函數(shù)將結(jié)束時(shí)的值,。 現(xiàn)在,讓我們分析一下函數(shù)handleResponse(),。既然它能夠在請求處理的不同狀態(tài)下激活,,那么該函數(shù)將忽略所有的情形-除了該請求處理完成之外-這相應(yīng)于request.readyState屬性等于4("Completed")。此時(shí),,該函數(shù)讀取服務(wù)器的響應(yīng)文本。與它的名字所暗示的相反,,XmlHttpRequest的輸入和輸出都不必限于XML格式,。特別地,我們的resolveZip.jsp(見源碼中的列表1)返回普通文本,。如果返回值為"unknown",,那么該函數(shù)將假定郵政區(qū)號(hào)是無效的并且把查找字段(zip)邊界顏色置為紅色。否則,,返回值被用于填充字段state或city,,并且zip的邊界被賦予一種缺省顏色。 讓我們返回到我們的XMLHTTPRequest的跨瀏覽器實(shí)現(xiàn),。最后一個(gè)列表包含一個(gè)HttpRequest()函數(shù)-它向上兼容于IE5.0和Mozilla 1.8/FireFox,。為簡化起見,我們只創(chuàng)建一個(gè)微軟XMLHTTPRequest對(duì)象,,而且如果創(chuàng)建失敗,我們假定它是Firefox/Mozilla。 該函數(shù)的核心是XMLHTTPRequest-這是一個(gè)本機(jī)瀏覽器對(duì)象,,它為包括HTTP協(xié)議的任何東西與服務(wù)器之間的通訊提供方便,。它允許指定任何HTTP動(dòng)詞,頭部和有效載荷,,并且能夠以異步或同步方式工作,。不需要下載也不需要安裝任何插件-盡管在IE的情形下,XMLHTTPRequest是一個(gè)集成到瀏覽器內(nèi)部的ActiveX,。因而,,"Run ActiveX Control and Plugins"默認(rèn)IE權(quán)限應(yīng)該正好適合使用它。 最重要的是,,XMLHTTPRequest允許一個(gè)到服務(wù)器的RPC風(fēng)格的編程查詢而不需要任何頁面刷新,。它以一種可預(yù)測的,可控制的方式來實(shí)現(xiàn)此-提供了到HTTP協(xié)議的所有細(xì)節(jié)的完整存取-包括頭部和數(shù)據(jù)的任何定制格式,。在以后的文章中,,我們將向你展示其它一些業(yè)界協(xié)議-你可以在這些傳輸協(xié)議(如Web服務(wù)和XML-RPC)之上運(yùn)行-它們極大地簡化大規(guī)模應(yīng)用程序的開發(fā)和維護(hù)。 五.服務(wù)器端邏輯 最后,,服務(wù)器端的resolveZip.jsp被從函數(shù)ask()中調(diào)用(見所附源碼中的列表1),。這個(gè)resolveZip.jsp在兩種由當(dāng)前的郵政區(qū)號(hào)長度所區(qū)分的獨(dú)立的場所下被調(diào)用(見zipChanged()函數(shù))。請求參數(shù)lookupType的值或者是state或者是city,。為簡化起見,,我們將假定,兩個(gè)文件state.properties和city.properties都位于服務(wù)器中C驅(qū)動(dòng)器的根目錄下,。resolveZip.jsp邏輯負(fù)責(zé)用適當(dāng)?shù)念A(yù)裝載的文件返回查找值,。 我們的支持AJAX的頁面現(xiàn)在已經(jīng)準(zhǔn)備好了。 六.遠(yuǎn)程腳本技術(shù)-一種可選方法 一些更舊的AJAX實(shí)現(xiàn)是基于所謂的遠(yuǎn)程腳本技術(shù),。這種思想是,,用戶的行為導(dǎo)致經(jīng)由IFRAME對(duì)服務(wù)器進(jìn)行查詢,而服務(wù)器用JavaScript作出響應(yīng),,該腳本一旦到達(dá)客戶端立即被執(zhí)行,。這與XMLHttpRequest方法相比存在較大的區(qū)別,在后者情況下,,服務(wù)器響應(yīng)數(shù)據(jù)而客戶端解釋數(shù)據(jù),。其好處是這種解決方案支持更舊的瀏覽器。 基于IFRAME示例的HTML部分(見所附源碼中的列表2)與我們在XMLHTTPRequest場合下所用的極相似,,但是這次我們將引入另外一個(gè)IFRAME元素-controller: Zip:<input id="zipcode" type="text" maxlength="5" onKeyUp="zipChanged()" 我們保持每次擊鍵都調(diào)用zipChanged()一次,,但是這一次,從zipChanged()中被調(diào)用的函數(shù)ask()(見所附源碼中的列表3)負(fù)責(zé)設(shè)置IFRAME的src屬性,,而不是調(diào)用一個(gè)XMLHTTPRequest: function ask(url,, fieldToFill,, lookupField){ 服務(wù)器端邏輯由一個(gè)粗略的resolveZip.jsp(見所附源碼中的列表4)所描述。它與它的XMLHTTPRequest對(duì)應(yīng)物相區(qū)別-它返回JavaScript語句,,這些語句設(shè)置變量字段lookup和city的全局值,,而且一旦它到達(dá)瀏覽器即從全局窗口的執(zhí)行上下文中調(diào)用函數(shù)response()。 函數(shù)response()是一修改版本的handleResponse()-這一函數(shù)可以免于處理未完成的請求(詳見本文所附源碼中的列表2),。 七. 難題 為簡化起見,,讓我們"俯看"一下在我們的示例代碼中的一些重要的問題: 1.事實(shí)-XMLHTTPRequest對(duì)象實(shí)例和回調(diào)函數(shù)調(diào)用在被使用以后并沒被破壞-在每次調(diào)用后這有可能導(dǎo)致內(nèi)存泄漏。適當(dāng)編寫的代碼應(yīng)該破壞或重用對(duì)象池中的這些實(shí)例,。而且,,客戶端必須使用與服務(wù)器軟件相同的對(duì)象管理技術(shù)。 2.在大多數(shù)情況下,,錯(cuò)誤往往得不到有效處理,。例如,在方法ask()中對(duì)request.open()的調(diào)用可能引發(fā)一個(gè)異常,,這是必須要捕獲和處理的,,即使在瀏覽器中沒有設(shè)置JavaScript異常自動(dòng)捕獲功能。而handleResponse()函數(shù)又是另外一個(gè)例子,。它必須要為可能的服務(wù)器端和通訊錯(cuò)誤而檢查headers和responseText值,。在發(fā)生錯(cuò)誤的情況下,它必須盡力恢復(fù)并/或者報(bào)告錯(cuò)誤,。正確開發(fā)的AJAX應(yīng)用程序要盡可能避免"提交"松散的數(shù)據(jù),,因?yàn)橥嬖诰€路斷開和其它低級(jí)通訊的問題-所以這些程序必須建立一個(gè)強(qiáng)壯的和自恢復(fù)的框架為此提供支持。 3.當(dāng)前服務(wù)器端框架提供相當(dāng)多的功能-它們可以與一種自由刷新方法和諧相處,。例如,,讓我們考慮一個(gè)定制的在指定時(shí)間內(nèi)的服務(wù)器端認(rèn)證的問題。在這種情況下,,我們必須攔截到XMLHTTPRequest調(diào)用的安全系統(tǒng)響應(yīng),,顯示登錄屏幕,然后在用戶被認(rèn)證后重新發(fā)出請求,。 所有的這些問題只是一些典型的用低級(jí)API工作的任何應(yīng)用程序代碼,,而且所有這些問題都能被解決。好消息是,,解決這些問題所需要的技術(shù)十分相似于大多數(shù)Java開發(fā)技術(shù),,如Web服務(wù),定制標(biāo)簽和XML/XSLT,。唯一的區(qū)別在于,,現(xiàn)在這些技術(shù)以下列形式用于客戶端: ·Web服務(wù)-使用SOAP/REST/RPC等簡單通訊標(biāo)準(zhǔn) ·客戶端定制標(biāo)簽-打包豐富的客戶端控件并集成AJAX功能 ·數(shù)據(jù)操作-基于XML和基于XSLT技術(shù) 八. 小結(jié) AJAX方法能夠向人們提供一種與桌面應(yīng)用程序相同的豐富的互聯(lián)網(wǎng)體驗(yàn)。但是,,我們必須有選擇地使用AJAX技術(shù),,如當(dāng)你仍在線購物時(shí),,你絕對(duì)不想讓你的信用卡通過后臺(tái)處理就悄悄地開始付款。AJAX會(huì)成為一種持續(xù)的動(dòng)力嗎?我們當(dāng)然希望這樣,。在過去的五年時(shí)間內(nèi)我們一直在努力開發(fā)AJAX應(yīng)用程序并且能證明它是健全并且很有效的,。然而,它要求一個(gè)開發(fā)者必須精通大量技術(shù)而不是在傳統(tǒng)的"click-refresh"Web應(yīng)用程序中所使用的那些,。 |
|