天天看點

AJAX 原理

【導讀】本文的作者是一位 Ajax 專家,他示範了這些技術如何協同工作 —— 從總體概述到細節的讨論 —— 使高效的 Web 開發成為現實。他還揭開了 Ajax 核心概念的神秘面紗,包括 XMLHttpRequest 對象。 

Ajax 由 HTML、JavaScript? 技術、DHTML 和 DOM 組成,這一傑出的方法可以将笨拙的 Web 界面轉化成互動性的 Ajax 應用程式。五年前,如果不知道 XML,您就是一隻無人重視的醜小鴨。十八個月前,Ruby 成了關注的中心,不知道 Ruby 的程式員隻能坐冷闆凳了。今天,如果想跟上最新的技術時尚,那您的目标就是 Ajax。 

但是,Ajax 不僅僅是一種時尚,它是一種建構網站的強大方法,而且不像學習一種全新的語言那樣困難。 

但在詳細探讨 Ajax 是什麼之前,先讓我們花幾分鐘了解 Ajax 做什麼。目前,編寫應用程式時有兩種基本的選擇:桌面應用程式和Web 應用程式。 

兩者是類似的,桌面應用程式通常以 CD 為媒體(有時候可從網站下載下傳)并完全安裝到您的計算機上。桌面應用程式可能使用網際網路下載下傳更新,但運作這些應用程式的代碼在桌面計算機上。Web 應用程式運作在某處的 Web 伺服器上 —— 毫不奇怪,要通過 Web 浏覽器通路這種應用程式。 

不過,比這些應用程式的運作代碼放在何處更重要的是,應用程式如何運轉以及如何與其進行互動。桌面應用程式一般很快(就在您的計算機上運作,不用等待網際網路連接配接),具有漂亮的使用者界面(通常和作業系統有關)和非凡的動态性。可以單擊、選擇、輸入、打開菜單和子菜單、到處巡遊,基本上不需要等待。 

另一方面,Web 應用程式是最新的潮流,它們提供了在桌面上不能實作的服務(比如 Amazon.com 和 eBay)。但是,伴随着Web 的強大而出現的是等待,等待伺服器響應,等待螢幕重新整理,等待請求傳回和生成新的頁面。 

顯然這樣說過于簡略了,但基本的概念就是如此。您可能已經猜到,Ajax 嘗試建立桌面應用程式的功能和互動性,與不斷更新的Web 應用程式之間的橋梁。可以使用像桌面應用程式中常見的動态使用者界面和漂亮的控件,不過是在 Web 應用程式中。 

還等什麼呢?我們來看看 Ajax 如何将笨拙的 Web 界面轉化成能迅速響應的 Ajax 應用程式吧。 

老技術,新技巧 

在談到 Ajax 時,實際上涉及到多種技術,要靈活地運用它必須深入了解這些不同的技術(本系列的頭幾篇文章将分别讨論這些技術)。好消息是您可能已經非常熟悉其中的大部分技術,更好的是這些技術都很容易學習,并不像完整的程式設計語言(如 Java或 Ruby)那樣困難。 

Ajax 的定義:Ajax 是 Asynchronous JavaScript and XML(以及 DHTML 等)的縮寫。這個短語是 Adaptive Path 的 Jesse James Garrett 發明的(請參閱參考資料),按照 Jesse 的解釋,這不是個首字母縮寫詞。 

下面是 Ajax 應用程式所用到的基本技術: 

HTML 用于建立 Web 表單并确定應用程式其他部分使用的字段。 

JavaScript 代碼是運作 Ajax 應用程式的核心代碼,幫助改進與伺服器應用程式的通信。 

DHTML 或 Dynamic HTML,用于動态更新表單。我們将使用div、span和其他動态 HTML 元素來标記 HTML。 

文檔對象模型 DOM 用于(通過 JavaScript 代碼)處理 HTML 結構和(某些情況下)伺服器傳回的 XML。 

我們來進一步分析這些技術的職責。以後的文章中我将深入讨論這些技術,目前隻要熟悉這些元件和技術就可以了。對這些代碼越熟悉,就越容易從對這些技術的零散了解轉變到真正把握這些技術(同時也真正打開了 Web 應用程式開發的大門)。 

XMLHttpRequest 對象 

要了解的一個對象可能對您來說也是最陌生的,即XMLHttpRequest。這是一個 JavaScript 對象,建立該對象很簡單,如清單 1所示。 

清單 1. 建立新的 XMLHttpRequest 對象 

<script language="javascript" type="text/javascript"> 

var xmlHttp = new XMLHttpRequest(); 

</script> 

現在要知道這是處理所有伺服器通信的對象。繼續閱讀之前,先停下來想一想:通過XMLHttpRequest對象與伺服器進行對話的是 JavaScript 技術。這不是一般的應用程式流,這恰恰是 Ajax 的強大功能的來源。 

在一般的 Web 應用程式中,使用者填寫表單字段并單擊 Submit 按鈕。然後整個表單發送到伺服器,伺服器将它轉發給處理表單的腳本(通常是 PHP 或 Java,也可能是 CGI 程序或者類似的東西),腳本執行完成後再發送回全新的頁面。該頁面可能是帶有已經填充某些資料的新表單的 HTML,也可能是确認頁面,或者是具有根據原來表單中輸入資料選擇的某些選項的頁面。當然,在伺服器上的腳本或程式處理和傳回新表單時使用者必須等待。螢幕變成一片空白,等到伺服器傳回資料後再重新繪制。這就是互動性差的原因,使用者得不到立即回報,是以感覺不同于桌面應用程式。 

Ajax 基本上就是把 JavaScript 技術和XMLHttpRequest對象放在 Web 表單和伺服器之間。當使用者填寫表單時,資料發送給一些JavaScript 代碼而不是直接發送給伺服器。相反,JavaScript 代碼捕獲表單資料并向伺服器發送請求。同時使用者螢幕上的表單也不會閃爍、消失或延遲。換句話說,JavaScript 代碼在幕後發送請求,使用者甚至不知道請求的發出。更好的是,請求是異步發送的,就是說 JavaScript 代碼(和使用者)不用等待伺服器的響應。是以使用者可以繼續輸入資料、滾動螢幕和使用應用程式。 

然後,伺服器将資料傳回 JavaScript 代碼(仍然在 Web 表單中),後者決定如何處理這些資料。它可以迅速更新表單資料,讓人感覺應用程式是立即完成的,表單沒有送出或重新整理而使用者得到了新資料。 JavaScript 代碼甚至可以對收到的資料執行某種計算,再發送另一個請求,完全不需要使用者幹預!這就是XMLHttpRequest的強大之處。它可以根據需要自行與伺服器進行互動,使用者甚至可以完全不知道幕後發生的一切。結果就是類似于桌面應用程式的動态、快速響應、高互動性的體驗,但是背後又擁有網際網路的全部強大力量。 

加入一些 JavaScript 

得到XMLHttpRequest的句柄後,其他的 JavaScript 代碼就非常簡單了。事實上,我們将使用 JavaScript 代碼完成非常基本的任務: 

擷取表單資料:JavaScript 代碼很容易從 HTML 表單中抽取資料并發送到伺服器。 

修改表單上的資料:更新表單也很簡單,從設定字段值到迅速替換圖像。 

解析 HTML 和 XML:使用 JavaScript 代碼操縱 DOM(請參閱下一節),處理 HTML 表單伺服器傳回的 XML 資料的結構。 

對于前兩點,需要非常熟悉getElementById()方法,如清單 2 所示。 

清單 2. 用 JavaScript 代碼捕獲和設定字段值 

// Get the value of the "phone" field and stuff it in a variable called phone 

var phone = document.getElementById("phone").value; 

// Set some values on a form using an array called response 

document.getElementById("order").value = response[0]; 

document.getElementById("address").value = response[1]; 

這裡沒有特别需要注意的地方,真是好極了!您應該認識到這裡并沒有非常複雜的東西。隻要掌握了XMLHttpRequest,Ajax 應用程式的其他部分就是如清單 2 所示的簡單 JavaScript 代碼了,混合有少量的 HTML。同時,還要用一點兒 DOM,我們就來看看吧 

以 DOM 結束 

最後還有 DOM,即文檔對象模型。可能對有些讀者來說 DOM 有點兒令人生畏,HTML 設計者很少使用它,即使 JavaScript 程式員也不大用到它,除非要完成某項高端程式設計任務。大量使用 DOM 的是複雜的 Java 和 C/C++ 程式,這可能就是 DOM 被認為難以學習的原因。 

幸運的是,在 JavaScript 技術中使用 DOM 很容易,也非常直覺。現在,按照正常也許應該說明如何使用 DOM,或者至少要給出一些示例代碼,但這樣做也可能誤導您。即使不理會 DOM,仍然能深入地探讨 Ajax,這也是我準備采用的方法。以後的文章将再次讨論 DOM,現在隻要知道可能需要 DOM 就可以了。當需要在 JavaScript 代碼和伺服器之間傳遞 XML 和改變 HTML 表單的時候,我們再深入研究 DOM。沒有它也能做一些有趣的工作,是以現在就把 DOM 放到一邊吧。 

擷取 Request 對象 

有了上面的基礎知識後,我們來看看一些具體的例子。XMLHttpRequest是 Ajax 應用程式的核心,而且對很多讀者來說可能還比較陌生,我們就從這裡開始吧。從清單 1 可以看出,建立和使用這個對象非常簡單,不是嗎?等一等。 

還記得幾年前的那些讨厭的浏覽器戰争嗎?沒有一樣東西在不同的浏覽器上得到同樣的結果。不管您是否相信,這些戰争仍然在繼續,雖然規模較小。但令人奇怪的是,XMLHttpRequest成了這場戰争的犧牲品之一。是以獲得XMLHttpRequest對象可能需要采用不同的方法。下面我将詳細地進行解釋。 

使用 Microsoft 浏覽器 

Microsoft 浏覽器 Internet Explorer 使用 MSXML 解析器處理 XML(可以通過參考資料進一步了解 MSXML)。是以如果編寫的 Ajax應用程式要和 Internet Explorer 打交道,那麼必須用一種特殊的方式建立對象。 

但并不是這麼簡單。根據 Internet Explorer 中安裝的 JavaScript 技術版本不同,MSXML 實際上有兩種不同的版本,是以必須對這兩種情況分别編寫代碼。請參閱清單 3,其中的代碼在 Microsoft 浏覽器上建立了一個XMLHttpRequest。 

清單 3. 在 Microsoft 浏覽器上建立 XMLHttpRequest 對象 

var xmlHttp = false; 

try { 

xmlHttp = new ActiveXObject("Msxml2.XMLHTTP"); 

} catch (e) { 

xmlHttp = new ActiveXObject("Microsoft.XMLHTTP"); 

} catch (e2) { 

xmlHttp = false; 

您對這些代碼可能還不完全了解,但沒有關系。當本系列文章結束的時候,您将對 JavaScript 程式設計、錯誤處理、條件編譯等有更深的了解。現在隻要牢牢記住其中的兩行代碼: 

xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");和xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");。 

這兩行代碼基本上就是嘗試使用一個版本的 MSXML 建立對象,如果失敗則使用另一個版本建立該對象。不錯吧?如果都不成功,則将xmlHttp變量設為 false,告訴您的代碼出現了問題。如果出現這種情況,可能是因為安裝了非 Microsoft 浏覽器,需要使用不同的代碼。 

處理 Mozilla 和非 Microsoft 浏覽器 

如果選擇的浏覽器不是 Internet Explorer,或者為非 Microsoft 浏覽器編寫代碼,就需要使用不同的代碼。事實上就是清單 1 所示的一行簡單代碼: 

var xmlHttp = new XMLHttpRequest object;。 

這行簡單得多的代碼在 Mozilla、Firefox、Safari、Opera 以及基本上所有以任何形式或方式支援 Ajax 的非 Microsoft 浏覽器中,建立了XMLHttpRequest對象。 

結合起來 

關鍵是要支援所有浏覽器。誰願意編寫一個隻能用于 Internet Explorer 或者非 Microsoft 浏覽器的應用程式呢?或者更糟,要編寫一個應用程式兩次?當然不!是以代碼要同時支援 Internet Explorer 和非 Microsoft 浏覽器。清單 4 顯示了這樣的代碼。 

清單 4. 以支援多種浏覽器的方式建立 XMLHttpRequest 對象 

/* Create a new XMLHttpRequest object to talk to the Web server */ 

/*@cc_on @*/ 

/*@if (@_jscript_version >= 5) 

@end @*/ 

if (!xmlHttp && typeof XMLHttpRequest != 'undefined') { 

xmlHttp = new XMLHttpRequest(); 

現在先不管那些注釋掉的奇怪符号,如@cc_on,這是特殊的 JavaScript 編譯器指令。這段代碼的核心分為三步: 

1. 建立一個變量xmlHttp來引用即将建立的XMLHttpRequest對象。 

2. 嘗試在 Microsoft 浏覽器中建立該對象: 

o 嘗試使用Msxml2.XMLHTTP對象建立它。 

o 如果失敗,再嘗試Microsoft.XMLHTTP對象。 

3. 如果仍然沒有建立xmlHttp,則以非 Microsoft 的方式建立該對象。 

最後,xmlHttp應該引用一個有效的XMLHttpRequest對象,無論運作什麼樣的浏覽器。 

關于安全性的一點說明 

安全性如何呢?現在浏覽器允許使用者提高他們的安全等級,關閉 JavaScript 技術,禁用浏覽器中的任何選項。在這種情況下,代碼無論如何都不會工作。此時必須适當地處理問題,這需要單獨的一篇文章來讨論,要放到以後了(這個系列夠長了吧?不用擔心,讀完之前也許您就掌握了)。現在要編寫一段健壯但不夠完美的代碼,對于掌握 Ajax 來說就很好了。以後我們還将讨論更多的細節。 

Ajax 世界中的請求/響應 

現在我們介紹了 Ajax,對XMLHttpRequest對象以及如何建立它也有了基本的了解。如果閱讀得很仔細,您可能已經知道與伺服器上的 Web 應用程式打交道的是 JavaScript 技術,而不是直接送出給那個應用程式的 HTML 表單。 

還缺少什麼呢?到底如何使用XMLHttpRequest。因為這段代碼非常重要,您編寫的每個 Ajax 應用程式都要以某種形式使用它,先看看 Ajax 的基本請求/響應模型是什麼樣吧。 

送出請求 

您已經有了一個嶄新的XMLHttpRequest對象,現在讓它幹點活兒吧。首先需要一個 Web 頁面能夠調用的 JavaScript 方法(比如當使用者輸入文本或者從菜單中選擇一項時)。接下來就是在所有 Ajax 應用程式中基本都雷同的流程: 

1. 從 Web 表單中擷取需要的資料。 

2. 建立要連接配接的 URL。 

3. 打開到伺服器的連接配接。 

4. 設定伺服器在完成後要運作的函數。 

5. 發送請求。 

清單 5 中的示例 Ajax 方法就是按照這個順序組織的: 

清單 5. 發出 Ajax 請求 

function callServer() { 

// Get the city and state from the web form 

var city = document.getElementById("city").value; 

var state = document.getElementById("state").value; 

// Only go on if there are values for both fields 

if ((city == null) || (city == "")) return; 

if ((state == null) || (state == "")) return; 

// Build the URL to connect to 

var url = "/scripts/getZipCode.php?city=" + escape(city) + "&state=" + escape(state); 

// Open a connection to the server 

xmlHttp.open("GET", url, true); 

// Setup a function for the server to run when it's done 

xmlHttp.onreadystatechange = updatePage; 

// Send the request 

xmlHttp.send(null); 

其中大部分代碼意義都很明确。開始的代碼使用基本 JavaScript 代碼擷取幾個表單字段的值。然後設定一個 PHP 腳本作為連結的目标。要注意腳本 URL 的指定方式,city 和 state(來自表單)使用簡單的 GET 參數附加在 URL 之後。 

然後打開一個連接配接,這是您第一次看到使用XMLHttpRequest。其中指定了連接配接方法(GET)和要連接配接的 URL。最後一個參數如果設為true,那麼将請求一個異步連接配接(這就是 Ajax 的由來)。如果使用false,那麼代碼送出請求後将等待伺服器傳回的響應。如果設為true,當伺服器在背景處理請求的時候使用者仍然可以使用表單(甚至調用其他 JavaScript 方法)。 

xmlHttp(要記住,這是XMLHttpRequest對象執行個體)的onreadystatechange屬性可以告訴伺服器在運作完成後(可能要用五分鐘或者五個小時)做什麼。因為代碼沒有等待伺服器,必須讓伺服器知道怎麼做以便您能作出響應。在這個示例中,如果伺服器處理完了請求,一個特殊的名為updatePage()的方法将被觸發。 

最後,使用值null調用send()。因為已經在請求 URL 中添加了要發送給伺服器的資料(city 和 state),是以請求中不需要發送任何資料。這樣就發出了請求,伺服器按照您的要求工作。 

如果沒有發現任何新鮮的東西,您應該體會到這是多麼簡單明了!除了牢牢記住 Ajax 的異步特性外,這些内容都相當簡單。應該感激 Ajax 使您能夠專心編寫漂亮的應用程式和界面,而不用擔心複雜的 HTTP 請求/響應代碼。 

清單 5 中的代碼說明了 Ajax 的易用性。資料是簡單的文本,可以作為請求 URL 的一部分。用 GET 而不是更複雜的 POST 發送請求。沒有 XML 和要添加的内容頭部,請求體中沒有要發送的資料;換句話說,這就是 Ajax 的烏托邦。 

不用擔心,随着本系列文章的展開,事情會變得越來越複雜。您将看到如何發送 POST 請求、如何設定請求頭部和内容類型、如何在消息中編碼 XML、如何增加請求的安全性,可以做的工作還有很多!暫時先不用管那些難點,掌握好基本的東西就行了,很快我們就會建立一整套的 Ajax 工具庫。 

處理響應 

現在要面對伺服器的響應了。現在隻要知道兩點: 

什麼也不要做,直到xmlHttp.readyState屬性的值等于 4。 

伺服器将把響應填充到xmlHttp.responseText屬性中。 

其中的第一點,即就緒狀态,将在下一篇文章中詳細讨論,您将進一步了解 HTTP 請求的階段,可能比您設想的還多。現在隻要檢查一個特定的值(4)就可以了(下一期文章中還有更多的值要介紹)。第二點,使用 xmlHttp.responseText屬性獲得伺服器的響應,這很簡單。清單 6 中的示例方法可供伺服器根據清單 5 中發送的資料調用。 

清單 6. 處理伺服器響應 

function updatePage() { 

if (xmlHttp.readyState == 4) { 

var response = xmlHttp.responseText; 

document.getElementById("zipCode").value = response; 

這些代碼同樣既不難也不複雜。它等待伺服器調用,如果是就緒狀态,則使用伺服器傳回的值(這裡是使用者輸入的城市和州的 ZIP編碼)設定另一個表單字段的值。于是包含 ZIP 編碼的zipCode字段突然出現了,而使用者沒有按任何按鈕!這就是前面所說的桌面應用程式的感覺。快速響應、動态感受等等,這些都隻因為有了小小的一段 Ajax 代碼。 

細心的讀者可能注意到zipCode是一個普通的文本字段。一旦伺服器傳回 ZIP 編碼,updatePage()方法就用城市/州的 ZIP 編碼設定那個字段的值,使用者就可以改寫該值。這樣做有兩個原因:保持例子簡單,說明有時候可能希望使用者能夠修改伺服器傳回的資料。要記住這兩點,它們對于好的使用者界面設計來說很重要。 

連接配接 Web 表單 

還有什麼呢?實際上沒有多少了。一個 JavaScript 方法捕捉使用者輸入表單的資訊并将其發送到伺服器,另一個 JavaScript 方法監聽和處理響應,并在響應傳回時設定字段的值。所有這些實際上都依賴于調用第一個 JavaScript 方法,它啟動了整個過程。最明顯的辦法是在 HTML 表單中增加一個按鈕,但這是 2001 年的辦法,您不這樣認為嗎?還是像清單 7 這樣利用 JavaScript 技術吧。 

清單 7. 啟動一個 Ajax 過程 

如果感覺這像是一段相當普通的代碼,那就對了,正是如此!當使用者在 city 或 state 字段中輸入新的值時,callServer()方法就被觸發,于是 Ajax 開始運作了。有點兒明白怎麼回事了吧?好,就是如此!

繼續閱讀