一. 什麼是AJAX? 這個名字代表了異步JavaScript+XMLHTTPRequest,並且意味著你可以在基於浏覽器的JavaScript和服務器之間建立套接字通訊。其實AJAX並不是一種新技術,而是已經成功地用於現代浏覽器中的若干成功技術的可能性組合。所有的AJAX應用程序實現了一種“豐富的”UI——這是通過JavaScript操作HTML文檔對象模型並且經由XMLHttpRequest實現的精確定位的數據檢索來實現的。典型的示例AJAX應用程序是Google Labs(http://labs.google.com)的Google Maps和Google Suggest。這些應用程序現場監視用戶輸入並且提供實時的頁面更新。最重要的是,在用戶通過地圖導航或輸入一個查找字符串的同時,這些事件不需要刷新頁面。
事實上,支持這些令人感到驚訝的應用的技術已經出現一段時間了,盡管它們要求復雜的技能以及使用浏覽器的技巧。一些專利產品就提供了相似的能力——如Macromedia Flash插件,Java Applets或.NET運行時——在達到實用上已經有一段時間了。把一種可與服務器通話的腳本組件引入到浏覽器中的思想早在IE 5.0中就已經存在。Firefox和其它流行的浏覽器也加入到浏覽器大軍中並以一種內置對象形式支持XMLHTTPRequest。隨著跨平台浏覽器的出現,這些技術得到了認可並在2004年3月一家稱為Adaptive Path的公司中正式提出了AJAX。
簡而言之,由於來自於Google的支持和安裝了一點可用的浏覽器技術,加上為了一種"更好的用戶體驗",每個人都在把客戶端技術添加到Web應用程序上。
二. AJAX與傳統應用程序的區別 一個傳統Web應用程序模型實際上是一種基本的事件——用戶被迫提交表單以實現頁面交換。也就是說,表單提交和頁面傳送無法得到保證:還有更壞的情形——用戶需要再次點擊。這與AJAX截然不同-——數據跨過線路而不是完整的HTML頁面傳輸。這種數據交換是經由特定的浏覽器對象:XMLHttpRequest實現的;再由適當的邏輯來處理每個數據請求的結果,頁面的特定區域而不是完整的頁面被更新。結果是更快的速度,更少的擁擠和更好的信息傳送控制。
傳統型"click-refresh"Web應用程序強迫用戶中斷工作過程而等待頁面的重裝。通過引入AJAX技術,一個客戶端腳本能夠異步地與服務器通話,而用戶仍能保持輸入數據。除了對用戶透明之外,這樣的異步意味著服務器可以有更多時間來處理請求。
傳統Web應用程序把所有的處理代理到服務器並且強迫服務器進行狀態管理。AJAX允許靈活劃分應用程序邏輯以及客戶和服務器之間的狀態管理。這就消除了一種"click-refresh"依賴性並且提供更好的服務器可伸縮性。當該狀態存儲在客戶端,你就不必跨越服務器來維持會話或保存/結束狀態-其使用期限是由客戶端來定義的。
三. AJAX——分布式的MVC 盡管AJAX應用程序依靠JavaScript來實現描述層,然而處理能力和知識庫仍然存在於服務器上。此時,AJAX應用程序大量的與J2EE服務器通訊——把數據輸入/輸出Web服務和servlets。具有基於AJAX的描述層的J2EE應用程序和標准J2EE應用程序之間的區別首先在於,MVC是通過線路分布的。通過使用AJAX,視圖是本地的,而模型和控制器是分布式的——這使得開發者能夠靈活地決定哪些部件會是基於客戶端的。具體地說,本地視圖通過巧妙地操作HTML DOM而生成圖形;控制器局部地處理用戶輸入並且根據開發者的判斷擴展到服務器的處理——經由HTTP請求(Web服務,XML/RPC或其它)實現;模型的遠程部分是根據客戶端需要而下載的以達到實時更新客戶端頁面;並且狀態是在客戶端收集的。
在以後的AJAX文章中,我們將比較深入地討論這裡的每一種組件並提供有關它們聯合在一起進行應用的示例。現在,先不多說,讓我們詳細地分析一個簡單的AJAX示例。
四. 郵政區號校驗和查詢 我們將創建一個包含三個INPUT字段(Zip,City和State)的HTML頁面。我們將保證,只要用戶輸入郵政區號的前三個數字,該頁面上的字段就會用第一個匹配的狀態值填充。一旦用戶輸入了所有五位郵政區號數,我們將立即決定和填充相應的城市。如果郵政區號無效(在服務器的數據庫沒有找到),那麼我們將把郵政區號的邊界設置為紅色。這樣的可視化線索有助於用戶並且在現代浏覽器中已經成為一種標准(作為一實例,當Firefox找到一個HTML頁面中的匹配關鍵字時,它會高亮與你在浏覽器查找域輸入的內容一致的部分)。
讓我們首先創建一個簡單的包含三個INPUT字段的HTML:zip,city和state。請注意,一旦一個字符輸入進郵政區號字段域中,即調用方法zipChanged()。JavaScript函數zipChanged()(見下)在當zip長度為3時調用函數updateState(),而在當zip長度為5時調用函數up-dateCity()。而updateCity()和updateState()把大部分的工作代理到另一個函數ask()。
Zip:<input id="zipcode" type="text" maxlength="5" onKeyUp="zipChanged()"
style="width:60"/>
City: <input id="city" disabled maxlength="32" style="width:160"/>
State:<input id="state" disabled maxlength="2" style="width:30"/>
<script src="xmlhttp.js"></script>
<script>
var zipField = null;
function zipChanged(){
zipField = document.getElementById("zipcode")
var zip = zipField.value;
zip.length == 3?updateState(zip):zip.length == 5?updateCity(zip):"";
}
function updateState(zip) {
var stateField = document.getElementById("state");
ask("resolveZip.jsp?lookupType=state&zip="+zip, stateField, zipField);
}
function updateCity(zip) {
var c