一.XMLHttpRequest
Ajax技術核心是XMLHttpRequest對象(簡稱XHR),這是由微軟首先引入的一個特性,其他瀏覽器提供商后來都提供了相同的實現。在XHR出現之前,Ajax式的通信必須借助一些hack手段來實現,大多數是使用隱藏的框架或內嵌框架。
XHR的出現,提供了向服務器發送請求和解析服務器響應提供了流暢的接口。能夠以異步方式從服務器獲取更多的信息,這就意味着,用戶只要觸發某一事件,在不刷新網頁的情況下,更新服務器最新的數據。
雖然Ajax中的x代表的是XML,但Ajax通信和數據格式無關,也就是說這種技術不一定使用XML。
IE7+、Firefox、Opera、Chrome和Safari都支持原生的XHR對象,在這些瀏覽器中創建XHR對象可以直接實例化XMLHttpRequest即可。
var xhr = new XMLHttpRequest();
alert(xhr); //XMLHttpRequest
如果是IE6及以下,那么我們必須還需要使用ActiveX對象通過MSXML庫來實現。在低版本IE瀏覽器可能會遇到三種不同版本的XHR對象,即MSXML2.XMLHttp、MSXML2.XMLHttp.3.0、MSXML2.XMLHttp.6.0。我們可以編寫一個函數。
function createXHR() {
if (typeof XMLHttpRequest != 'undefined') {
return new XMLHttpRequest();
} else if (typeof ActiveXObject != 'undefined') {
var versions = [
'MSXML2.XMLHttp.6.0',
'MSXML2.XMLHttp.3.0',
'MSXML2.XMLHttp'
];
for (var i = 0; i < versions.length; i ++) {
try {
return new ActiveXObject(version[i]);
} catch (e) {
//跳過
}
}
} else {
throw new Error('您的瀏覽器不支持XHR對象!');
}
}
var xhr = new createXHR();
在使用XHR對象時,先必須調用open()方法,它接受三個參數:要發送的請求類型(get、post)、請求的URL和表示是否異步。
xhr.open('get', 'demo.php', false); //對於demo.php的get請求,false同步
PS:demo.php的代碼如下:
<?php echo Date('Y-m-d H:i:s')?> //一個時間
open()方法並不會真正發送請求,而只是啟動一個請求以備發送。通過send()方法進行發送請求,send()方法接受一個參數,作為請求主體發送的數據。如果不需要則,必須填null。執行send()方法之后,請求就會發送到服務器上。
xhr.send(null); //發送請求
當請求發送到服務器端,收到響應后,響應的數據會自動填充XHR對象的屬性。那么一共有四個屬性:
屬性名 |
說明 |
responseText |
作為響應主體被返回的文本 |
responseXML |
如果響應主體內容類型是"text/xml"或"application/xml",則返回包含響應數據的XML DOM文檔 |
status |
響應的HTTP狀態 |
statusText |
HTTP狀態的說明 |
接受響應之后,第一步檢查status屬性,以確定響應已經成功返回。一般而已HTTP狀態代碼為200作為成功的標志。除了成功的狀態代碼,還有一些別的:
HTTP狀態碼 |
狀態字符串 |
說明 |
200 |
OK |
服務器成功返回了頁面 |
400 |
Bad Request |
語法錯誤導致服務器不識別 |
401 |
Unauthorized |
請求需要用戶認證 |
404 |
Not found |
指定的URL在服務器上找不到 |
500 |
Internal Server Error |
服務器遇到意外錯誤,無法完成請求 |
503 |
ServiceUnavailable |
由於服務器過載或維護導致無法完成請求 |
我們判斷HTTP狀態值即可,不建議使用HTTP狀態說明,因為在跨瀏覽器的時候,可能會不太一致。
addEvent(document, 'click', function () {
var xhr = new createXHR();
xhr.open('get', 'demo.php?rand=' + Math.random(), false); //設置了同步
xhr.send(null);
if (xhr.status == 200) { //如果返回成功了
alert(xhr.responseText); //調出服務器返回的數據
} else {
alert('數據返回失敗!狀態代碼:' + xhr.status + '狀態信息:' + xhr.statusText);
}
});
以上的代碼每次點擊頁面的時候,返回的時間都是時時的,不同的,說明都是通過服務器及時加載回的數據。那么我們也可以測試一下在非Ajax情況下的情況,創建一個demo2.php文件,使用非Ajax。
<script type="text/javascript" src="base.js"></script>
<script type="text/javascript">
addEvent(document, 'click', function () {
alert("<?php echo Date('Y-m-d H:i:s')?>");
});
</script>
同步調用固然簡單,但使用異步調用才是我們真正常用的手段。使用異步調用的時候,需要觸發readystatechange事件,然后檢測readyState屬性即可。這個屬性有五個值:
值 |
狀態 |
說明 |
0 |
未初始化 |
尚未調用open()方法 |
1 |
啟動 |
已經調用open()方法,但尚未調用send()方法 |
2 |
發送 |
已經調用send()方法,但尚未接受響應 |
3 |
接受 |
已經接受到部分響應數據 |
4 |
完成 |
已經接受到全部響應數據,而且可以使用 |
addEvent(document, 'click', function () {
var xhr = new createXHR();
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
alert(xhr.responseText);
} else {
alert('數據返回失敗!狀態代碼:' + xhr.status + '狀態信息:'
+ xhr.statusText);
}
}
};
xhr.open('get', 'demo.php?rand=' + Math.random(), true);
xhr.send(null);
});
PS:使用abort()方法可以取消異步請求,放在send()方法之前會報錯。放在responseText之前會得到一個空值。
二.GET與POST
在提供服務器請求的過程中,有兩種方式,分別是:GET和POST。在Ajax使用的過程中,GET的使用頻率要比POST高。
在了解這兩種請求方式前,我們先了解一下HTTP頭部信息,包含服務器返回的響應頭信息和客戶端發送出去的請求頭信息。我們可以獲取響應頭信息或者設置請求頭信息。我們可以在Firefox瀏覽器的firebug查看這些信息。
//使用getResponseHeader()獲取單個響應頭信息
alert(xhr.getResponseHeader('Content-Type'));
//使用getAllResponseHeaders()獲取整個響應頭信息
alert(xhr.getAllResponseHeaders());
//使用setRequestHeader()設置單個請求頭信息
xhr.setRequestHeader('MyHeader', 'Lee'); //放在open方法之后,send方法之前
PS:我們只可以獲取服務器返回回來響應頭信息,無法獲取向服務器提交的請求頭信息,自然自定義的請求頭,在JavaScript端是無法獲取到的。
GET請求
GET請求是最常見的請求類型,最常用於向服務器查詢某些信息。必要時,可以將查詢字符串參數追加到URL的末尾,以便提交給服務器。
xhr.open('get', 'demo.php?rand=' + Math.random() + '&name=Koo', true);
通過URL后的問號給服務器傳遞鍵值對數據,服務器接收到返回響應數據。特殊字符傳參產生的問題可以使用encodeURIComponent()進行編碼處理,中文字符的返回及傳參,可以講頁面保存和設置為utf-8格式即可。
//一個通用的URL提交函數
function addURLParam(url, name, value) {
url += (url.indexOf('?') == -1 ? '?' : '&'); //判斷的url是否有已有參數
url += encodeURIComponent(name) + '=' + encodeURIComponent(value);
alert(url);
return url;
}
PS:當沒有encodeURIComponent()方法時,在一些特殊字符比如“&”,會出現錯誤導致無法獲取。
POST請求
POST請求可以包含非常多的數據,我們在使用表單提交的時候,很多就是使用的POST傳輸方式。
xhr.open('post', 'demo.php', true);
而發送POST請求的數據,不會跟在URL的尾巴上,而是通過send()方法向服務器提交數據。
xhr.send('name=Lee&age=100');
一般來說,向服務器發送POST請求由於解析機制的原因,需要進行特別的處理。因為POST請求和Web表單提交是不同的,需要使用XHR來模仿表單提交。
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
PS:從性能上來講POST請求比GET請求消耗更多一些,用相同數據比較,GET最多比POST快兩倍。
上一節課的JSON也可以使用Ajax來回調訪問。
var url = 'demo.json?rand=' + Math.random();
var box = JSON.parse(xhr.responseText);
三.封裝Ajax
因為Ajax使用起來比較麻煩,主要就是參數問題,比如到底使用GET還是POST;到底是使用同步還是異步等等,我們需要封裝一個Ajax函數,來方便我們調用。
function ajax(obj) {
var xhr = new createXHR();
obj.url = obj.url + '?rand=' + Math.random();
obj.data = params(obj.data);
if (obj.method === 'get') obj.url = obj.url.indexOf('?') == -1 ?
obj.url + '?' + obj.data : obj.url + '&' + obj.data;
if (obj.async === true) {
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) callback();
};
}
xhr.open(obj.method, obj.url, obj.async);
if (obj.method === 'post') {
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send(obj.data);
} else {
xhr.send(null);
}
if (obj.async === false) {
callback();
}
function callback () {
if (xhr.status == 200) {
obj.success(xhr.responseText); //回調
} else {
alert('數據返回失敗!狀態代碼:' + xhr.status + ',
狀態信息:' + xhr.statusText);
}
}
}
//調用ajax
addEvent(document, 'click', function () { //IE6需要重寫addEvent
ajax({
method : 'get',
url : 'demo.php',
data : {
'name' : 'Lee',
'age' : 100
},
success : function (text) {
alert(text);
},
async : true
});
});
//名值對編碼
function params(data) {
var arr = [];
for (var i in data) {
arr.push(encodeURIComponent(i) + '=' + encodeURIComponent(data[i]));
}
return arr.join('&');
}
PS:封裝Ajax並不是一開始就形成以上的形態,需要經過多次變化而成。