查看單個文章
舊 2006-07-05, 05:49 AM   #5 (permalink)
psac
榮譽會員
 
psac 的頭像
榮譽勳章
UID - 3662
在線等級: 級別:30 | 在線時長:1048小時 | 升級還需:37小時級別:30 | 在線時長:1048小時 | 升級還需:37小時級別:30 | 在線時長:1048小時 | 升級還需:37小時級別:30 | 在線時長:1048小時 | 升級還需:37小時級別:30 | 在線時長:1048小時 | 升級還需:37小時
註冊日期: 2002-12-07
住址: 木柵市立動物園
文章: 17381
現金: 5253 金幣
資產: 33853 金幣
預設

用AJAX開發智慧式Web應用程式之基礎篇

  一. 什麼是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 cityField = document.getElementById("city");
 ask("resolveZip.jsp? lookupType=city&zip="+zip, cityField, zipField);
}
</script>

  函數ask()與服務器進行通訊並分配一個回調函數來處理服務器的響應(見下列代碼)。後面,我們將分析具有雙重特點的resolveZip.jsp的內容-它根據zip字段中的字元數查找city或state訊息。重要的是,ask()使用了具有異步特點的XmlHttpRequest,這樣填充state和city字段或著色zip字段邊界就可以不必減慢資料入口而得以實現。首先,我們調用request.open()-它用服務器打開套接字頻道,使用一個HTTP動詞(GET或POST)作為第一個參數並且以資料提供者的URL作為第二個參數。request.open()的最後一個參數被設置為true-它指示該請求的異步特性。注意,該請求還沒有被提交。隨著對request.send()的調用,開始提交-這可以為POST提供任何必要的有效載荷。在使用異步請求時,我們必須使用request.onreadystatechanged內容來分配請求的回調函數。(如果請求是同步的話,我們應該能夠在調用request.send之後立即處理結果,但是我們也有可能阻斷用戶,直到該請求完成為止。)

HTTPRequest = function () {
 var xmlhttp=null;
 try {
  xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
 } catch (_e) {
  try {
   xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
  } catch (_E) { }
 }
 if (!xmlhttp && typeof XMLHttpRequest != 'undefined') {
  try {
   xmlhttp = new XMLHttpRequest();
  } catch (e) {
   xmlhttp = false;
  }
 }
 return xmlhttp;
}

function ask(url, fieldToFill, lookupField) {
 var http = new HTTPRequest();
 http.open("GET", url, true);
 http.onreadystatechange = function (){ handleHttpResponse(http, fieldToFill,lookupField)};
 http.send(null);
}

function handleHttpResponse(http, fieldToFill, lookupField) {
 if (http.readyState == 4) {
  result = http.responseText;
  if ( -1 != result.search("null") ) {
   lookupField.style.borderColor = "red";
   fieldToFill.value = "";
  } else {
   lookupField.style.borderColor = "";
   fieldToFill.value = result;
  }
 }
}

  為ask()所使用的HttpRequest()函數(見上)是一跨瀏覽器的XMLHTTPRequest的一個實例的構造器;稍後我們將分析它。到目前為止,請注意對於handleResponse()的調用是如何用一匿名函數包裝的-這個函數是function(){handleHttpResponse(http,fieldToFill, lookupField)}。

  該函數的代碼是動態創建的並且在每次我們給http.onreadstatechange內容賦值時被編譯。結果,JavaScript創建一個指向上下文(所有的變數都可以存取正在結束的方法-ask())的指針。這樣以來,匿名函數和handleResponse()就能夠被保證充分存取所有的上下文宿主的變數,直至到匿名函數的參考被垃圾資源回收筒收集為止。換句話說,無論何時我們的匿名函數被調用,它都能無縫地參考request,fieldToFill和lookupField變數,就像它們是全局的一樣。而且,每次ask()調用都將創建環境的一個獨立拷貝,並且此時這些變數中儲存有該函數將結束時的值。

  現在,讓我們分析一下函數handleResponse()。既然它能夠在請求處理的不同狀態下啟動,那麼該函數將忽略所有的情形-除了該請求處理完成之外-這相應於request.readyState內容等於4("Completed")。此時,該函數讀取服務器的響應文本。與它的名字所暗示的相反,XmlHttpRequest的輸入和輸出都不必限於XML格式。特別地,我們的resolveZip.jsp(見源碼中的列表1)返回普通文本。如果返回值為"unknown",那麼該函數將假定郵政區號是無效的並且把查找字段(zip)邊界顏色置為紅色。否則,返回值被用於填充字段state或city,並且zip的邊界被賦予一種預設顏色。

  XMLHttpRequest-傳輸對像

  讓我們返回到我們的XMLHTTPRequest的跨瀏覽器實現。最後一個列表包含一個HttpRequest()函數-它向上相容於IE5.0和Mozilla 1.8/FireFox。為簡化起見,我們只創建一個微軟XMLHTTPRequest對象,而且如果創建失敗,我們假定它是Firefox/Mozilla。

  該函數的核心是XMLHTTPRequest-這是一個本機瀏覽器對象,它為包括HTTP協議的任何東西與服務器之間的通訊提供方便。它允許指定任何HTTP動詞,頭部和有效載荷,並且能夠以異步或同步方式工作。不需要下載也不需要安裝任何插件-儘管在IE的情形下,XMLHTTPRequest是一個整合到瀏覽器內部的ActiveX。因而,"Run ActiveX Control and Plugins"預定IE權限應該正好適合使用它。

  最重要的是,XMLHTTPRequest允許一個到服務器的RPC風格的編程查詢而不需要任何網頁面重新整理。它以一種可預測的,可控制的方式來實現此-提供了到HTTP協議的所有細節的完整存取-包括頭部和資料的任何定制格式。在以後的文章中,我們將向你展示其它一些業界協議-你可以在這些傳輸協議(如Web服務和XML-RPC)之上執行-它們極大地簡化大規模應用程式的開發和維護。
__________________
http://bbsimg.qianlong.com/upload/01/08/29/68/1082968_1136014649812.gif
psac 目前離線  
送花文章: 3, 收花文章: 1631 篇, 收花: 3205 次