本文目錄:
- 1.什么是AJAX
- 2.AJAX的常用屬性
- 3.AJAX的常用方法
- 4.原生AJAX封裝與調(diào)用
- 5.用promise手寫實(shí)現(xiàn)AJAX
1.什么是AJAX
瀏覽器與服務(wù)器之間忿项,采用 HTTP 協(xié)議通信间聊。用戶在瀏覽器地址欄鍵入一個(gè)網(wǎng)址饲齐,或者通過網(wǎng)頁表單向服務(wù)器提交內(nèi)容,這時(shí)瀏覽器就會向服務(wù)器發(fā)出 HTTP 請求琅坡。
1999年,微軟公司發(fā)布 IE 瀏覽器5.0版檩赢,第一次引入新功能:允許 JavaScript 腳本向服務(wù)器發(fā)起 HTTP 請求结序。這個(gè)功能當(dāng)時(shí)并沒有引起注意,直到2004年 Gmail 發(fā)布和2005年 Google Map 發(fā)布辱士,才引起廣泛重視泪掀。2005年2月,AJAX 這個(gè)詞第一次正式提出识补,它是 Asynchronous JavaScript and XML 的縮寫族淮,指的是通過 JavaScript 的異步通信,從服務(wù)器獲取 XML 文檔從中提取數(shù)據(jù)凭涂,再更新當(dāng)前網(wǎng)頁的對應(yīng)部分,而不用刷新整個(gè)網(wǎng)頁贴妻。后來切油,AJAX 這個(gè)詞就成為 JavaScript 腳本發(fā)起 HTTP 通信的代名詞,也就是說名惩,只要用腳本發(fā)起通信澎胡,就可以叫做 AJAX 通信。W3C 也在2006年發(fā)布了它的國際標(biāo)準(zhǔn)娩鹉。
具體來說攻谁,AJAX 包括以下幾個(gè)步驟。
- 創(chuàng)建 XMLHttpRequest 實(shí)例
- 發(fā)出 HTTP 請求
- 接收服務(wù)器傳回的數(shù)據(jù)
- 更新網(wǎng)頁數(shù)據(jù)
概括起來弯予,就是一句話戚宦,AJAX是異步 JavaScript 和 XML,是一種用于創(chuàng)建快速動(dòng)態(tài)網(wǎng)頁的技術(shù)锈嫩。AJAX 通過原生的XMLHttpRequest對象發(fā)出 HTTP 請求受楼,得到服務(wù)器返回的數(shù)據(jù)后垦搬,再進(jìn)行處理。現(xiàn)在艳汽,服務(wù)器返回的都是 JSON 格式的數(shù)據(jù)猴贰,XML 格式已經(jīng)過時(shí)了,但是 AJAX 這個(gè)名字已經(jīng)成了一個(gè)通用名詞河狐,字面含義已經(jīng)消失了米绕。
XMLHttpRequest對象是 AJAX 的主要接口,用于瀏覽器與服務(wù)器之間的通信馋艺。盡管名字里面有XML和Http栅干,它實(shí)際上可以使用多種協(xié)議(比如file或ftp),發(fā)送任何格式的數(shù)據(jù)(包括字符串和二進(jìn)制)丈钙。
通過在后臺與服務(wù)器進(jìn)行少量數(shù)據(jù)交換非驮,AJAX 可以使網(wǎng)頁實(shí)現(xiàn)異步更新。這意味著可以在不重新加載整個(gè)網(wǎng)頁的情況下雏赦,對網(wǎng)頁的某部分進(jìn)行更新劫笙。
傳統(tǒng)的網(wǎng)頁(不使用 AJAX)如果需要更新內(nèi)容,必需重載整個(gè)網(wǎng)頁面星岗。
XMLHttpRequest本身是一個(gè)構(gòu)造函數(shù)填大,可以使用new命令生成實(shí)例。它沒有任何參數(shù)俏橘。
var xhr = new XMLHttpRequest();
一旦新建實(shí)例允华,就可以使用open()方法指定建立 HTTP 連接的一些細(xì)節(jié)。xhr.open('GET', 'http://www.example.com/page.php', true);
上面代碼指定使用 GET 方法寥掐,跟指定的服務(wù)器網(wǎng)址建立連接靴寂。第三個(gè)參數(shù)true,表示請求是異步的召耘。
然后百炬,指定回調(diào)函數(shù),監(jiān)聽通信狀態(tài)(readyState屬性)的變化污它。
xhr.onreadystatechange = handleStateChange;
function handleStateChange() {
// ...
}
上面代碼中剖踊,一旦XMLHttpRequest實(shí)例的狀態(tài)發(fā)生變化,就會調(diào)用監(jiān)聽函數(shù)handleStateChange
最后使用send()方法衫贬,實(shí)際發(fā)出請求德澈。
xhr.send(null);
上面代碼中,send()的參數(shù)為null固惯,表示發(fā)送請求的時(shí)候梆造,不帶有數(shù)據(jù)體。如果發(fā)送的是 POST 請求缝呕,這里就需要指定數(shù)據(jù)體澳窑。
一旦拿到服務(wù)器返回的數(shù)據(jù)斧散,AJAX 不會刷新整個(gè)網(wǎng)頁,而是只更新網(wǎng)頁里面的相關(guān)部分摊聋,從而不打斷用戶正在做的事情鸡捐。
注意,AJAX 只能向同源網(wǎng)址(協(xié)議麻裁、域名箍镜、端口都相同)發(fā)出 HTTP 請求,如果發(fā)出跨域請求煎源,就會報(bào)錯(cuò)色迂。
下面是XMLHttpRequest對象簡單用法的完整例子。
1:創(chuàng)建Ajax對象
var xhr = window.XMLHttpRequest?new XMLHttpRequest():new ActiveXObject('Microsoft.XMLHTTP');// 兼容IE6及以下版本
如果不需要考慮兼容低版本IE手销,則可以直接寫成下面這樣:
var xhr = new XMLHttpRequest();
2:配置 Ajax請求地址
xhr.open('get','index.xml',true);
3:發(fā)送請求
xhr.send(null);
4:監(jiān)聽請求歇僧,接受響應(yīng)
xhr.onreadysatechange=function(){
if(xhr.readySates==4&&xhr.status==200 || xhr.status==304 )
console.log(xhr.responsetXML)
}
2.AJAX的常用屬性
AJAX的工作原理就是利用實(shí)例化的XMLHTTPRequest對象向服務(wù)器發(fā)出請求,然后根據(jù)服務(wù)器的響應(yīng)數(shù)據(jù)進(jìn)行響應(yīng)的處理锋拖。
AJAX的核心是XMLHTTPRequest對象(是對象,就有屬性和方法)
常用的屬性:
2.1.XMLHttpRequest.readyState
XMLHttpRequest.readyState返回一個(gè)整數(shù)兽埃,表示實(shí)例對象的當(dāng)前狀態(tài)。該屬性只讀舷夺。它可能返回以下值。
- 0售貌,表示 XMLHttpRequest 實(shí)例已經(jīng)生成,但是實(shí)例的open()方法還沒有被調(diào)用颂跨。
- 1,表示open()方法已經(jīng)調(diào)用毫捣,但是實(shí)例的send()方法還沒有調(diào)用,仍然可以使用實(shí)例的setRequestHeader()方法帝际,設(shè)定 HTTP 請求的頭信息。
- 2蹲诀,表示實(shí)例的send()方法已經(jīng)調(diào)用,并且服務(wù)器返回的頭信息和狀態(tài)碼已經(jīng)收到则北。
- 3,表示正在接收服務(wù)器傳來的數(shù)據(jù)體(body 部分)涌矢。這時(shí)快骗,如果實(shí)例的responseType屬性等于text或者空字符串,responseText屬性就會包含已經(jīng)收到的部分信息名秀。
- 4藕溅,表示服務(wù)器返回的數(shù)據(jù)已經(jīng)完全接收巾表,或者本次接收已經(jīng)失敗。
通信過程中调塌,每當(dāng)實(shí)例對象發(fā)生狀態(tài)變化惠猿,它的readyState屬性的值就會改變。這個(gè)值每一次變化姜凄,都會觸發(fā)readyStateChange事件趾访。
var xhr = new XMLHttpRequest();
if (xhr.readyState === 4) {
// 請求結(jié)束,處理服務(wù)器返回的數(shù)據(jù)
} else {
// 顯示提示“加載中……”
}
上面代碼中申鱼,xhr.readyState等于4時(shí)云头,表明腳本發(fā)出的 HTTP 請求已經(jīng)完成溃槐。其他情況,都表示 HTTP 請求還在進(jìn)行中猴鲫。
2.2.XMLHttpRequest.onreadystatechange
XMLHttpRequest.onreadystatechange屬性指向一個(gè)監(jiān)聽函數(shù)。readystatechange事件發(fā)生時(shí)(實(shí)例的readyState屬性變化)拂共,就會執(zhí)行這個(gè)屬性匣缘。
另外,如果使用實(shí)例的abort()方法培慌,終止 XMLHttpRequest 請求柑爸,也會造成readyState屬性變化,導(dǎo)致調(diào)用XMLHttpRequest.onreadystatechange屬性馅而。
下面是一個(gè)例子譬圣。
var xhr = new XMLHttpRequest();
xhr.open( 'GET', 'http://example.com' , true );
xhr.onreadystatechange = function () {
if (xhr.readyState !== 4 || xhr.status !== 200) {
return;
}
console.log(xhr.responseText);
};
xhr.send();
2.3.XMLHttpRequest.response
XMLHttpRequest.response屬性表示服務(wù)器返回的數(shù)據(jù)體(即 HTTP 回應(yīng)的 body 部分)厘熟。它可能是任何數(shù)據(jù)類型,比如字符串绳姨、對象飘庄、二進(jìn)制對象等等,具體的類型由XMLHttpRequest.responseType屬性決定谴仙。該屬性只讀碾盐。
如果本次請求沒有成功或者數(shù)據(jù)不完整廓旬,該屬性等于null。但是涩盾,如果responseType屬性等于text或空字符串励背,在請求沒有結(jié)束之前(readyState等于3的階段)叶眉,response屬性包含服務(wù)器已經(jīng)返回的部分?jǐn)?shù)據(jù)。
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
handler(xhr.response);
}
}
2.4.XMLHttpRequest.responseType
XMLHttpRequest.responseType屬性是一個(gè)字符串莲趣,表示服務(wù)器返回?cái)?shù)據(jù)的類型饱溢。這個(gè)屬性是可寫的绩郎,可以在調(diào)用open()方法之后、調(diào)用send()方法之前溉仑,設(shè)置這個(gè)屬性的值状植,告訴服務(wù)器返回指定類型的數(shù)據(jù)浅萧。如果responseType設(shè)為空字符串,就等同于默認(rèn)值text吩案。
XMLHttpRequest.responseType屬性可以等于以下值帝簇。
- ""(空字符串):等同于text丧肴,表示服務(wù)器返回文本數(shù)據(jù)。
- "arraybuffer":ArrayBuffer 對象抱环,表示服務(wù)器返回二進(jìn)制數(shù)組。
- "blob":Blob 對象眶痰,表示服務(wù)器返回二進(jìn)制對象梯啤。
- "document":Document 對象因宇,表示服務(wù)器返回一個(gè)文檔對象。
- "json":JSON 對象打厘。
- "text":字符串杭棵。
上面幾種類型之中魂爪,text類型適合大多數(shù)情況,而且直接處理文本也比較方便蒋川。document類型適合返回 HTML / XML 文檔的情況捺球,這意味著夕冲,對于那些打開 CORS 的網(wǎng)站,可以直接用 Ajax 抓取網(wǎng)頁泣栈,然后不用解析 HTML 字符串南片,直接對抓取回來的數(shù)據(jù)進(jìn)行 DOM 操作庭敦。blob類型適合讀取二進(jìn)制數(shù)據(jù)秧廉,比如圖片文件拣帽。
2.5.XMLHttpRequest.timeout赔癌,XMLHttpRequestEventTarget.ontimeout
XMLHttpRequest.timeout屬性返回一個(gè)整數(shù)灾票,表示多少毫秒后茫虽,如果請求仍然沒有得到結(jié)果濒析,就會自動(dòng)終止。如果該屬性等于0婴氮,就表示沒有時(shí)間限制主经。
XMLHttpRequestEventTarget.ontimeout屬性用于設(shè)置一個(gè)監(jiān)聽函數(shù)庭惜,如果發(fā)生 timeout 事件护赊,就會執(zhí)行這個(gè)監(jiān)聽函數(shù)。
下面是一個(gè)例子节吮。
var xhr = new XMLHttpRequest();
var url = '/server';
xhr.ontimeout = function () {
console.error('The request for ' + url + ' timed out.');
};
xhr.onload = function() {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
// 處理服務(wù)器返回的數(shù)據(jù)
} else {
console.error(xhr.statusText);
}
}
};
xhr.open('GET', url, true);
// 指定 10 秒鐘超時(shí)
xhr.timeout = 10 * 1000;
xhr.send(null);
3.AJAX的常用方法
3.1.XMLHttpRequest.open()
XMLHttpRequest.open()方法用于指定 HTTP 請求的參數(shù)透绩,或者說初始化 XMLHttpRequest 實(shí)例對象祈秕。它一共可以接受五個(gè)參數(shù)请毛。
open("method", "url", "async","user","password")
- method:表示 HTTP 動(dòng)詞方法,比如GET固棚、POST、PUT厂汗、DELETE娶桦、HEAD等汁汗。
- url: 表示請求發(fā)送目標(biāo) URL知牌。
- async: 布爾值,表示請求是否為異步菩混,默認(rèn)為true沮峡。如果設(shè)為false纹磺,則send()方法只有等到收到服務(wù)器返回了結(jié)果,才會進(jìn)行下一步操作秘症。該參數(shù)可選乡摹。由于同步 AJAX 請求會造成瀏覽器失去響應(yīng)采转,許多瀏覽器已經(jīng)禁止在主線程使用故慈,只允許 Worker 里面使用。所以干签,這個(gè)參數(shù)輕易不應(yīng)該設(shè)為false容劳。
- user:表示用于認(rèn)證的用戶名,默認(rèn)為空字符串蚜印。該參數(shù)可選窄赋。
- password:表示用于認(rèn)證的密碼楼熄,默認(rèn)為空字符串孝赫。該參數(shù)可選红符。
注意预侯,如果對使用過open()方法的 AJAX 請求,再次使用這個(gè)方法双戳,等同于調(diào)用abort()飒货,即終止請求塘辅。
下面發(fā)送 POST 請求的例子皆撩。
var xhr = new XMLHttpRequest();
xhr.open('POST', encodeURI('someURL'));
3.2.XMLHttpRequest.send()
XMLHttpRequest.send()方法用于實(shí)際發(fā)出 HTTP 請求扛吞。它的參數(shù)是可選的,如果不帶參數(shù)亚脆,就表示 HTTP 請求只有一個(gè) URL型酥,沒有數(shù)據(jù)體,典型例子就是 GET 請求郁竟;如果帶有參數(shù)棚亩,就表示除了頭信息虏杰,還帶有包含具體數(shù)據(jù)的信息體纺阔,典型例子就是 POST 請求。
下面是 GET 請求的例子质况。
var xhr = new XMLHttpRequest();
xhr.open('GET',
'http://www.example.com/?id=' + encodeURIComponent(id),
true
);
xhr.send(null);
上面代碼中结榄,GET請求的參數(shù)囤捻,作為查詢字符串附加在 URL 后面蝎土。
下面是發(fā)送 POST 請求的例子瘟则。
var xhr = new XMLHttpRequest();
var data = 'email='
+ encodeURIComponent(email)
+ '&password='
+ encodeURIComponent(password);
xhr.open('POST', 'http://www.example.com', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send(data);
注意,所有 XMLHttpRequest 的監(jiān)聽事件慷嗜,都必須在send()方法調(diào)用之前設(shè)定庆械。
send方法的參數(shù)就是發(fā)送的數(shù)據(jù)缭乘。多種格式的數(shù)據(jù)琉用,都可以作為它的參數(shù)。
void send();
void send(ArrayBufferView data);
void send(Blob data);
void send(Document data);
void send(String data);
void send(FormData data);
如果send()發(fā)送 DOM 對象特姐,在發(fā)送之前唐含,數(shù)據(jù)會先被串行化沫浆。如果發(fā)送二進(jìn)制數(shù)據(jù)专执,最好是發(fā)送ArrayBufferView或Blob對象,這使得通過 Ajax 上傳文件成為可能争剿。
下面是發(fā)送表單數(shù)據(jù)的例子。FormData對象可以用于構(gòu)造表單數(shù)據(jù)凿叠。
var formData = new FormData();
formData.append('username', '張三');
formData.append('email', 'zhangsan@example.com');
formData.append('birthDate', 1940);
var xhr = new XMLHttpRequest();
xhr.open('POST', '/register');
xhr.send(formData);
上面代碼中盒件,F(xiàn)ormData對象構(gòu)造了表單數(shù)據(jù)舱禽,然后使用send()方法發(fā)送誊稚。它的效果與發(fā)送下面的表單數(shù)據(jù)是一樣的里伯。
<form id='registration' name='registration' action='/register'>
<input type='text' name='username' value='張三'>
<input type='email' name='email' value='zhangsan@example.com'>
<input type='number' name='birthDate' value='1940'>
<input type='submit' onclick='return sendForm(this.form);'>
</form>
3.3.readyStateChange 事件
readyState屬性的值發(fā)生改變疾瓮,就會觸發(fā) readyStateChange 事件。
我們可以通過onReadyStateChange屬性蜒灰,指定這個(gè)事件的監(jiān)聽函數(shù)强窖,對不同狀態(tài)進(jìn)行不同處理。尤其是當(dāng)狀態(tài)變?yōu)?的時(shí)候削饵,表示通信成功窿撬,這時(shí)回調(diào)函數(shù)就可以處理服務(wù)器傳送回來的數(shù)據(jù)劈伴。
3.4.load 事件握爷、error 事件新啼、abort 事件
load 事件表示服務(wù)器傳來的數(shù)據(jù)接收完畢追城,error 事件表示請求出錯(cuò),abort 事件表示請求被中斷(比如用戶取消請求)燥撞。
var xhr = new XMLHttpRequest();
xhr.addEventListener('load', transferComplete);
xhr.addEventListener('error', transferFailed);
xhr.addEventListener('abort', transferCanceled);
xhr.open();
function transferComplete() {
console.log('數(shù)據(jù)接收完畢');
}
function transferFailed() {
console.log('數(shù)據(jù)接收出錯(cuò)');
}
function transferCanceled() {
console.log('用戶取消接收');
}
3.5.timeout 事件
服務(wù)器超過指定時(shí)間還沒有返回結(jié)果座柱,就會觸發(fā) timeout 事件。
4.原生AJAX封裝與調(diào)用
function ajax(options) {
options = options || {};
options.type = (options.type || "GET").toUpperCase();
options.dataType = options.dataType || 'json';
options.async = options.async || true;
options.timeout=options.timeout||5000;//超時(shí)處理物舒,默認(rèn)5s
var params = getParams(options.data);
var timeoutFlag=null;
var xhr;
var that=this;
if (window.XMLHttpRequest) {
xhr = new XMLHttpRequest();
} else {
//為了兼容ie6以下
xhr = new ActiveXObject('Microsoft.XMLHTTP')
}
xhr.onreadystatechange = function() {
if(options.dataType === 'json'){
if (xhr.readyState == 4) {
window.clearTimeout(that.timeoutFlag);
var status = xhr.status;
if (status >= 200 && status < 300) {
// 如果需要像 html 表單那樣 POST 數(shù)據(jù)色洞,請使用 setRequestHeader() 來添加 http 頭。
options.success && options.success(xhr.responseText, xhr.responseXML);
} else {
options.error && options.error(status);
}
}
} else {
if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
window.clearTimeout(that.timeoutFlag);
var oScript = document.createElement('script');
document.body.appendChild(oScript);
var callbackname = 'ajaxCallBack'
oScript.src = options.url + "?" + params+'&callback='+callbackname;
window['ajaxCallBack'] = function(data) {
options.success(data);
document.body.removeChild(oScript);
};
}
}
};
if (options.type == 'GET') {
xhr.open("GET", options.url + '?' + params, options.async);
xhr.send(null)
} else if (options.type == 'POST') {
xhr.open('POST', options.url, options.async);
if(options.contentType=="undefined"||options.contentType==null){
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send(params);
}else{
xhr.setRequestHeader('Content-Type', options.contentType);
xhr.send(JSON.stringify(options.data));
}
}
this.timeoutFlag=window.setTimeout(function(){//計(jì)時(shí)器冠胯,超時(shí)后處理
window.clearTimeout(that.timeoutFlag);
//options.error("timeout");
xhr.abort();
}.bind(this),options.timeout);
}
function getParams(data) {
var arr = [];
for (var param in data) {
arr.push(encodeURIComponent(param) + '=' + encodeURIComponent(data[param]));
}
return arr.join('&');
}
封裝代碼的使用:
ajax({
url: "", //請求地址
type: 'GET', //請求方式
async:true,//同步異步設(shè)置
timeout:6000,//超時(shí)設(shè)置
data: {
name: '',
age: '',
email: ''
}, //請求參數(shù)
success: function(response, xml) {
console.log(response); // 此處執(zhí)行請求成功后的回調(diào)
},
error: function(status) {
console.log('狀態(tài)碼為' + status); // 此處為請求失敗后的回調(diào)
}
})
為了方便記憶和使用,像上面封裝原生JS的ajax的屬性和方法在jQuery中的$.ajax也都有荠察。
在MVC頁面中置蜀,多個(gè)PartialView(分部視圖)同時(shí)加載,一般使用異步請求悉盆。一個(gè)原因是分部視圖加載順序不分先后盾碗,另一個(gè)原因是即使有一個(gè)分部視圖加載失敗也不會影響其他視圖的加載。一般只有兩種情況下會使用到同步請求:1.需要控制ajax請求返回結(jié)果順序時(shí)舀瓢;2.在ajax請求時(shí)需鎖住瀏覽器時(shí)廷雅。
5.用promise手寫實(shí)現(xiàn)AJAX
function getJSON(url) {
// 創(chuàng)建一個(gè) promise 對象
let promise = new Promise(function(resolve, reject) {
let xhr = new XMLHttpRequest();
// 新建一個(gè) http 請求
xhr.open("GET", url, true);
// 設(shè)置狀態(tài)的監(jiān)聽函數(shù)
xhr.onreadystatechange = function() {
if (this.readyState !== 4) return;
// 當(dāng)請求成功或失敗時(shí),改變 promise 的狀態(tài)
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
};
// 設(shè)置錯(cuò)誤監(jiān)聽函數(shù)
xhr.onerror = function() {
reject(new Error(this.statusText));
};
// 設(shè)置響應(yīng)的數(shù)據(jù)類型
xhr.responseType = "json";
// 設(shè)置請求頭信息
xhr.setRequestHeader("Accept", "application/json");
// 發(fā)送 http 請求
xhr.send(null);
});
return promise;
}