借助於 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() 方法的參數。資料類型通常有以下幾種:
非同步執行,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 的 3f1c4e4b6b16bbbd69b2ee476dc4f83a 标签却无此限制,它可以载入任何域下的脚本,jQuery 正是利用了这一点才得以拥有跨域执行 Ajax 的能力。
所谓 JSONP,就是其它域的服务端返回给我们的是 JavaScript 代码,这段代码可以被加载到 HTML 中的 3f1c4e4b6b16bbbd69b2ee476dc4f83a 标签中,这段 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 提供的簡單方法如下:
以上每種簡單方法中都可傳遞以下參數:
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(); });