藉助於 XMLHttpRequest,瀏覽器可以在整個頁面不重新整理的情況下與服務端進行互動,這就是所謂的 Ajax(Asynchronous JavaScript and XML)。Ajax 可以為使用者提供更為豐富的使用者體驗。
Ajax 請求由 JavaScript 驅動,通過 JavaScript 代碼向 URL 發送一個請求,待服務端有響應時會觸發一個回呼函數,可以在這裡回呼函數裡面處理服務端返回的資訊。由於整個發送請求和響應的過程是非同步,所以在此期間頁面中其它 Javascript 代碼仍然繼續執行,不會中斷。
jQuery 對 Ajax 當然也提供了很好的支援,而且還抽象了各種瀏覽器對於 Ajax 支援方面另人痛苦的差異。它不但提供了全功能的 $.ajax() 方法,還有諸如 $.get(),$.getScript(),$.getJSON(),$.post() 和 $().load() 等更為簡便的方法。
儘管被命名為 Ajax,但是很多 Ajax 應用並沒有使用 XML,特別是 jQuery 方面的 Ajax 應用,大多數都沒有使用 XML;反而用得比較多的情況是:純文字、HTML 以及 JSON(JavaScript Object Notation)。
一般情況下,由於同源策略(同協議,同網域名稱,同連接埠)的限制,Ajax 並不能跨域執行請求,除非使用諸如 JSONP(JSON with Padding) 之類的方案,才能實現一些受限的跨域功能。
關於 Ajax 的一些重要概念
GET vs POST,這是兩種最常用的向服務端發送請求的方法,正確理解這兩種方法的區別對於 Ajax 開發非常重要。
GET 方法通常用於執行一些非破壞性的操作(就是說,只從服務端擷取資訊,不修改服務端上的資訊)。例如,搜尋查詢服務一般會使用 GET 請求。另外,GET 請求還可能會被瀏覽器緩衝,這可能會導致一些不可預知的問題。一般情況下 GET 請求只能通過查詢字串的方式向服務端發送資料。
POST 方法通常用於在服務端上執行一些破壞性的操作(就是說,會修改服務端上的資料)。例如,當你發表一篇部落格的時候,用的應該就是 POST 請求。和 GET 請求不一樣,POST 請求不存在緩衝問題。POST 請求中,查詢字串作為 URL 的一部分也能向服務端提交資料,但不推薦這種方法,所有資料應該跟 URL 分開單獨發送。
資料類型,jQuery 通常要求指明服務端返回的資料類型,某些情況寫資料類型可能已經包含在方法名稱中了,如 $.getJSON(),除此之外,它都會被作為一個可配置的對象的一部分,該對象最終會作為 $.ajax() 方法的參數。資料類型通常有以下幾種:
- text :純文字,用於傳輸簡單的字串。
- html :用於傳輸一段 HTML。
- script :向頁面中添加指令碼。
- json :傳輸已格式化的 JSON 對象,它可以包含字串、數組或對象。
- jsonp :用於傳輸從其他域下返回的 JSON 資料。
- xml :用於傳輸自訂的 XML 格式資料。
非同步執行,Ajax 中的 A 指的是非同步(Asynchronous)。說到這裡可能很多 jQuery 初學者一下子很難理解什麼叫非同步,因為預設情況下 Ajax 請求就是非同步,服務端返回的資訊並非馬上就能擷取到。所有服務端返回的資訊只能在一個回呼函數中處理。例如以下這段代碼,是錯誤的:
var response;$.get('foo.php', function(r) { response = r; });console.log(response); // undefined!
正確的做法應該是在回呼函數中處理服務端返回的資料,回呼函數在 Ajax 請求成功完成時才被執行,這個時候才能擷取到來自服務端的資料:
$.get('foo.php', function(response) { console.log(response); });
同源策略及 JSONP,前面已經說過,一般情況下 Ajax 的請求會被限制在相同協議(http 或 https)、相同連接埠、相同網域名稱下才能正確執行,但是 HTML 的 <script> 標籤卻無此限制,它可以載入任何域下的指令碼,jQuery 正是利用了這一點才得以擁有跨域執行 Ajax 的能力。
所謂 JSONP,就是其它域的服務端返回給我們的是 JavaScript 代碼,這段代碼可以被載入到 HTML 中的 <script> 標籤中,這段 JavaScript 代碼中包含有從其它域下的服務端返回的 JSON 資料,並以回呼函數的形式提供。這樣一來 jQuery 就迴避了同源策略的限制,曲線擁有了跨域執行 Ajax 的能力。
Ajax 調試工具,現在比較新的瀏覽器如 Chrome 和 Safari,甚至 IE 都內建了調試工具,Firefox 也有無比強大 FireBug 外掛程式,藉助於這些調試工具,可以非常詳細的查看 Ajax 的執行過程。
和 Ajax 相關的一些方法
jQuery 提供了好多種簡便的 Ajax 方法,但是它們的核心都是 $.ajax,所以必須正確理解 $.ajax。
jQuery 的 $.ajax 是建立 Ajax 請求中最直接也是最有效方法,它的參數是一個 JavaScript 對象,我們可以在這個對象中對 Ajax 作非常詳細的配置。另外,$.ajax 方法還可以分別定義 Ajax 請求成功和失敗時的回呼函數;而且它以一個可配置的對象作為參數的特性,使得我們可以在 Ajax 方法外配置這個對象,然後再傳進來,這非常有助於實現代碼複用。關於這個方法的詳細文檔:http://api.jquery.com/jQuery.ajax/
一個典型的樣本如下:
$.ajax({ // 要請求的 URL url : 'post.php', // 要發給服務端的資料 // (將會轉化為查詢字串,如:?id=123) data : { id : 123 }, // 定義此 Ajax 請求是 POST 還是 GET type : 'GET', // 服務端返回的資料類型 dataType : 'json', // Ajax 成功執行時的回呼函數; // 回呼函數的參數即為服務端返回的資料 success : function(json) { $('<h1/>').text(json.title).appendTo('body'); $('<div class="content"/>') .html(json.html).appendTo('body'); }, // 如果 Ajax 執行失敗; // 將返回原始錯誤資訊以及狀態代碼 // 傳入這個回呼函數中 error : function(xhr, status) { alert('Sorry, there was a problem!'); }, // 這裡是無論 Ajax 是否成功執行都會觸發的回呼函數 complete : function(xhr, status) { alert('The request is complete!'); }});
備忘:
關於 dataType :如果這裡定義的 dataType 跟服務端返回的資料格式不一樣,我們的代碼就可能會執行失敗,並且很難查出原因,因為 HTTP 返回的狀態代碼並沒有顯示出錯。所以在執行 Ajax 請求的時候,一定要確保服務端返回的資料格式跟事先定義定義的一致。通常情況下驗證 HTTP 頭資訊中的 Content-type 是行之有效辦法,對於 JSON 而言,對應的 Content-type 應該是 application/json。
$.ajax 的一些自訂選項
$.ajax 方法的自訂選項非常多,這也是此方法功能強大的原因所在。若要查閱所有自訂選項,可參考官方文檔:http://api.jquery.com/jQuery.ajax/,下面只列出一些常用的選項:
async :預設值是 true,如果需要 Ajax 的執行方式為同步,可將其設為 false。請注意,如果把這個值設為 false 了,那麼你的其它 JavaScript 代碼將會被中斷執行,直到此次 Ajax 請求完畢,接受到服務端返回的資料為止才會恢複。所以,請慎用此選項。
cache :設定是否快取服務端發回的資料。對於 “script” 和 “jsonp” 之外的其它格式的資料而言,預設值是 true。如果被設定為 false,向伺服器發送請求的時候,URL 中會被加入一個查詢字串,字串的值是當前的時間戳記,以確保每次請求的 URL 都是不同的,當然也就不存在緩衝問題了。JavaScript 中擷取時間戳記的方法為 new Date().getTime()。
complete :Ajax 請求執行完成時觸發的回呼函數,無論此次執行成功與否,該回呼函數都會被觸發。該回呼函數可以接受服務端返回的原始資訊及狀態代碼。
context :定義回呼函數執行時的範圍(回呼函數 function(s) {alert(this)} 中的 this 指向誰?)。預設情況下,回呼函數中的 this 指向傳遞給 $.ajax 方法的參數,也就是那個大對象。
data :要發送給服務端的資料,其值可以是一個對象或者查詢字串,如 foo=bar&baz=bim。
dataType :服務端返回資料的類型。如果不設定這個選項,jQuery 會根據服務端返回資料的 MIME 類型自行判斷。
error :當 Ajax 執行錯誤時將會觸發的回呼函數,該函數接受原始的請求資訊及狀態代碼。
jsonp :執行 JSONP 請求時需要制定的回呼函數名稱,預設值是“callback”。
success :Ajax 成功執行時將會觸發的回呼函數。在函數中可擷取服務端返回的資訊(如果 dataType 被設定成 JSON,返回的資料應該是一個 JavaScript 對象),當然也可擷取服務端返回的未經處理資料資訊及狀態代碼。
timeout :給 Ajax 請求設定一個逾時是時間,單位是毫秒。
type :指定請求的方式,GET 或者 POST,預設值是 GET。其它如 PUT 和 DELETE 方式也可以用,但並不是所有瀏覽器都支援。
url :要請求的 URL。
其中 url 選項是所有選項中唯一的一個必選項,其它選項都是可選的。
一些簡便方法
如果你不需要那麼多可配置的選項,也不關心 Ajax 執行錯誤時候的相關處理,jQuery 同樣提供了一些非常有用的簡便方法,以更簡潔的方式完成 Ajax 請求。其實這些簡便寫法只是封裝了 $.ajax,並把某些選項預先設定好。
jQuery 提供的簡便方法如下:
- $.get :對給定的 URL 執行 GET 請求。
- $.post :對給定的 URL 執行 POST 請求。
- $.getScript :向頁面中添加指令碼。
- $.getJSON :執行一個 GET 請求,服務端返回的資訊應為 JSON。
以上每種簡便方法中都可傳遞如下參數:
url :所請求的 URL,必須提供。
data :向服務端發送的資料,可選。可以是一個對象,亦或查詢字串,如 foo=bar&baz=bim。
一個回呼函數 :此回呼函數在請求成功執行後被觸發。可選。該回呼函數接受服務端返回的資料,也包括請求的狀態代碼及原始對象。
資料類型 :服務端返回資料的類型。可選。
- 備忘:此選項只適用於那些在其名稱中沒指定資料類型的方法。
下面是三個簡便方法的樣本:
// 擷取純文字或者 html$.get('/users.php', { userId : 1234 }, function(resp) { console.log(resp);});// 向頁面中添加指令碼,然後執行指令碼中定義的函數。$.getScript('/static/js/myScript.js', function() { functionFromMyScript();});// 從服務端擷取 JSON 格式的資料。$.getJSON('/details.php', function(resp) { $.each(resp, function(k, v) { console.log(k + ' : ' + v); });});
$.fn.load
$.fn.load 方法是 jQuery 所有 Ajax 方法中唯一在選取器結果集上調用的方法。$.fn.load 方法從給定的 URL 上擷取資訊,然後填充到選取器結果集包含的元素中。另外,在 URL 後面還可以附加一些選取器,jQuery 最終只會把跟選取器相匹配的內容填充到對應的 HTML 元素中。
下面是樣本:
$('#newContent').load('/foo.html');// 或$('#newContent').load('/foo.html #myDiv h1:first', function(html) { alert('載入完畢!');});
Ajax 和 表單
在跟表單打交道方面,jQuery 的 Ajax 更顯神威。最為有用的兩個方法就是 $.fn.serialize 和 $.fn.serializeArray,下面是它們的用法:
// 將表單中資料轉化為查詢字串$('#myForm').serialize();$('#myForm').serializeArray();// 將表單中資料轉化為對象數組,如:[ { name : 'field1', value : 123 }, { name : 'field2', value : 'hello world' }]
使用 JSONP
JSON 的本質其實是一種跨網站指令碼注入。現在有很多比較好的網站都提供了 JSONP 服務,允許我們用他們預先定義好的 API 擷取他們的資料。下面是一個樣本,來源於 http://www.giantflyingsaucer.com/blog/?p=2682
服務端代碼:
<?php header("content-type: text/javascript"); if(isset($_GET['name']) && isset($_GET['callback'])) { $obj->name = $_GET['name']; $obj->message = "Hello " . $obj->name; echo $_GET['callback']. '(' . json_encode($obj) . ');'; }?>
用戶端代碼:
$.ajax({ url: 'http://otherDomail.com:8080/JSONP/jsonp-demo.php', data: {name: 'Super man'}, dataType: 'jsonp', jsonp: 'callback', success: function( response ) { console.log( response.message ); }});
jQuery 把 JSONP 的實現細節隱藏在幕後,我們要做的就是告訴 jQuery 服務端定義好的函數名以及我們請求的資料類型是 JSONP,其它方面和普通的 Ajax 請求沒什麼區別。
Ajax 事件
很多時候我們都需要在 Ajax 請求開始或結束時做一些操作,例如顯示或隱藏一個 loading… 圖片。這些工作本可以在每個 Ajax 請求中各自實現,但是 jQuery 提供了更好的方法,你可以像綁定普通事件一樣綁定 Ajax 事件。若要參閱全部事件列表,可訪問 http://docs.jquery.com/Ajax_Events。下面是簡單樣本:
$('#loading_indicator') .ajaxStart(function() { $(this).show(); }) .ajaxStop(function() { $(this).hide(); });