AJAX

1.簡(jiǎn)介

瀏覽器與服務(wù)器之間沾谜,采用 HTTP 協(xié)議通信巴碗。用戶在瀏覽器地址欄鍵入一個(gè)網(wǎng)址,或者通過網(wǎng)頁表單向服務(wù)器提交內(nèi)容擎厢,這時(shí)瀏覽器就會(huì)向服務(wù)器發(fā)出 HTTP 請(qǐng)求。

1999年辣吃,微軟公司發(fā)布 IE 瀏覽器5.0版,第一次引入新功能:允許 JavaScript 腳本向服務(wù)器發(fā)起 HTTP 請(qǐng)求芬探。這個(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)頁的對(duì)應(yīng)部分别智,而不用刷新整個(gè)網(wǎng)頁宗苍。后來,AJAX 這個(gè)詞就成為 JavaScript 腳本發(fā)起 HTTP 通信的代名詞薄榛,也就是說讳窟,只要用腳本發(fā)起通信,就可以叫做 AJAX 通信敞恋。W3C 也在2006年發(fā)布了它的國際標(biāo)準(zhǔn)丽啡。

具體來說,AJAX 包括以下幾個(gè)步驟硬猫。

  1. 創(chuàng)建 XMLHttpRequest 實(shí)例
  2. 發(fā)出 HTTP 請(qǐng)求
  3. 接收服務(wù)器傳回的數(shù)據(jù)
  4. 更新網(wǎng)頁數(shù)據(jù)

概括起來补箍,就是一句話改执,AJAX 通過原生的XMLHttpRequest對(duì)象發(fā)出 HTTP 請(qǐng)求,得到服務(wù)器返回的數(shù)據(jù)后坑雅,再進(jìn)行處理”补遥現(xiàn)在,服務(wù)器返回的都是 JSON 格式的數(shù)據(jù)霞丧,XML 格式已經(jīng)過時(shí)了呢岗,但是 AJAX 這個(gè)名字已經(jīng)成了一個(gè)通用名詞,字面含義已經(jīng)消失了蛹尝。

XMLHttpRequest對(duì)象是 AJAX 的主要接口后豫,用于瀏覽器與服務(wù)器之間的通信。盡管名字里面有XMLHttp突那,它實(shí)際上可以使用多種協(xié)議(比如fileftp)挫酿,發(fā)送任何格式的數(shù)據(jù)(包括字符串和二進(jìn)制)。

XMLHttpRequest本身是一個(gè)構(gòu)造函數(shù)愕难,可以使用new命令生成實(shí)例早龟。它沒有任何參數(shù)。

var xhr = new XMLHttpRequest();

一旦新建實(shí)例猫缭,就可以使用open()方法發(fā)出 HTTP 請(qǐng)求葱弟。

xhr.open('GET', 'http://www.example.com/page.php', true);

上面代碼向指定的服務(wù)器網(wǎng)址,發(fā)出 GET 請(qǐng)求猜丹。

然后芝加,指定回調(diào)函數(shù),監(jiān)聽通信狀態(tài)(readyState屬性)的變化射窒。

ajax.onreadystatechange = handleStateChange;

function handleStateChange() {
  // ...
}

上面代碼中藏杖,一旦XMLHttpRequest實(shí)例的狀態(tài)發(fā)生變化,就會(huì)調(diào)用監(jiān)聽函數(shù)handleStateChange

一旦拿到服務(wù)器返回的數(shù)據(jù)脉顿,AJAX 不會(huì)刷新整個(gè)網(wǎng)頁蝌麸,而是只更新網(wǎng)頁里面的相關(guān)部分,從而不打斷用戶正在做的事情艾疟。

注意来吩,AJAX 只能向同源網(wǎng)址(協(xié)議、域名蔽莱、端口都相同)發(fā)出 HTTP 請(qǐng)求误褪,如果發(fā)出跨域請(qǐng)求,就會(huì)報(bào)錯(cuò)(詳見《同源政策》和《CORS 通信》兩章)碾褂。

下面是XMLHttpRequest對(duì)象簡(jiǎn)單用法的完整例子兽间。

var xhr = new XMLHttpRequest();

xhr.onreadystatechange = function(){
  // 通信成功時(shí),狀態(tài)值為4
  if (xhr.readyState === 4){
    if (xhr.status === 200){
      console.log(xhr.responseText);
    } else {
      console.error(xhr.statusText);
    }
  }
};

xhr.onerror = function (e) {
  console.error(xhr.statusText);
};

xhr.open('GET', '/endpoint', true);
xhr.send(null);

2.XMLHttpRequest 的實(shí)例屬性

2.1 XMLHttpRequest.readyState

XMLHttpRequest.readyState返回一個(gè)整數(shù)正塌,表示實(shí)例對(duì)象的當(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 請(qǐng)求的頭信息饥瓷。
  • 2,表示實(shí)例的send()方法已經(jīng)調(diào)用痹籍,并且服務(wù)器返回的頭信息和狀態(tài)碼已經(jīng)收到呢铆。
  • 3,表示正在接收服務(wù)器傳來的數(shù)據(jù)體(body 部分)蹲缠。這時(shí)棺克,如果實(shí)例的responseType屬性等于text或者空字符串,responseText屬性就會(huì)包含已經(jīng)收到的部分信息线定。
  • 4娜谊,表示服務(wù)器返回的數(shù)據(jù)已經(jīng)完全接收,或者本次接收已經(jīng)失敗斤讥。

通信過程中纱皆,每當(dāng)實(shí)例對(duì)象發(fā)生狀態(tài)變化,它的readyState屬性的值就會(huì)改變周偎。這個(gè)值每一次變化,都會(huì)觸發(fā)readyStateChange事件撑帖。

var xhr = new XMLHttpRequest();

if (xhr.readyState === 4) {
  // 請(qǐng)求結(jié)束蓉坎,處理服務(wù)器返回的數(shù)據(jù)
} else {
  // 顯示提示“加載中……”
}

上面代碼中,xhr.readyState等于4時(shí)胡嘿,表明腳本發(fā)出的 HTTP 請(qǐng)求已經(jīng)成功蛉艾。其他情況,都表示 HTTP 請(qǐng)求還在進(jìn)行中衷敌。

2.2 XMLHttpRequest.onreadystatechange

XMLHttpRequest.onreadystatechange屬性指向一個(gè)監(jiān)聽函數(shù)勿侯。readystatechange事件發(fā)生時(shí)(實(shí)例的readyState屬性變化),就會(huì)執(zhí)行這個(gè)屬性缴罗。

另外助琐,如果使用實(shí)例的abort()方法,終止 XMLHttpRequest 請(qǐng)求面氓,也會(huì)造成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ù)類型泰演,比如字符串、對(duì)象葱轩、二進(jìn)制對(duì)象等等睦焕,具體的類型由XMLHttpRequest.responseType屬性決定。該屬性只讀靴拱。

如果本次請(qǐng)求沒有成功或者數(shù)據(jù)不完整垃喊,該屬性等于null。但是缭嫡,如果responseType屬性等于text或空字符串缔御,在請(qǐng)求沒有結(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 對(duì)象纸俭,表示服務(wù)器返回二進(jìn)制數(shù)組。
  • “blob”:Blob 對(duì)象南窗,表示服務(wù)器返回二進(jìn)制對(duì)象揍很。
  • “document”:Document 對(duì)象,表示服務(wù)器返回一個(gè)文檔對(duì)象万伤。
  • “json”:JSON 對(duì)象窒悔。
  • “text”:字符串。

上面幾種類型之中敌买,

text類型適合大多數(shù)情況简珠,而且直接處理文本也比較方便。

document類型適合返回 HTML / XML 文檔的情況虹钮,這意味著北救,對(duì)于那些打開 CORS 的網(wǎng)站荐操,可以直接用 Ajax 抓取網(wǎng)頁,然后不用解析 HTML 字符串珍策,直接對(duì)抓取回來的數(shù)據(jù)進(jìn)行 DOM 操作托启。

blob類型適合讀取二進(jìn)制數(shù)據(jù),比如圖片文件攘宙。

var xhr = new XMLHttpRequest();
xhr.open('GET', '/path/to/image.png', true);
xhr.responseType = 'blob';

xhr.onload = function(e) {
  if (this.status === 200) {
    var blob = new Blob([xhr.response], {type: 'image/png'});
    // 或者
    var blob = xhr.response;
  }
};

xhr.send();

如果將這個(gè)屬性設(shè)為ArrayBuffer屯耸,就可以按照數(shù)組的方式處理二進(jìn)制數(shù)據(jù)。

var xhr = new XMLHttpRequest();
xhr.open('GET', '/path/to/image.png', true);
xhr.responseType = 'arraybuffer';

xhr.onload = function(e) {
  var uInt8Array = new Uint8Array(this.response);
  for (var i = 0, len = binStr.length; i < len; ++i) {
    // var byte = uInt8Array[i];
  }
};

xhr.send();

如果將這個(gè)屬性設(shè)為json蹭劈,瀏覽器就會(huì)自動(dòng)對(duì)返回?cái)?shù)據(jù)調(diào)用JSON.parse()方法疗绣。也就是說,從xhr.response屬性(注意铺韧,不是xhr.responseText屬性)得到的不是文本多矮,而是一個(gè) JSON 對(duì)象。

2.5 XMLHttpRequest.responseText

XMLHttpRequest.responseText屬性返回從服務(wù)器接收到的字符串哈打,該屬性為只讀塔逃。只有 HTTP 請(qǐng)求完成接收以后,該屬性才會(huì)包含完整的數(shù)據(jù)料仗。

var xhr = new XMLHttpRequest();
xhr.open('GET', '/server', true);

xhr.responseType = 'text';
xhr.onload = function () {
  if (xhr.readyState === 4 && xhr.status === 200) {
    console.log(xhr.responseText);
  }
};

xhr.send(null);

2.6 XMLHttpRequest.responseXML

XMLHttpRequest.responseXML屬性返回從服務(wù)器接收到的 HTML 或 XML 文檔對(duì)象湾盗,該屬性為只讀。如果本次請(qǐng)求沒有成功立轧,或者收到的數(shù)據(jù)不能被解析為 XML 或 HTML格粪,該屬性等于null

該屬性生效的前提是 HTTP 回應(yīng)的Content-Type頭信息等于text/xmlapplication/xml氛改。這要求在發(fā)送請(qǐng)求前帐萎,XMLHttpRequest.responseType屬性要設(shè)為document。如果 HTTP 回應(yīng)的Content-Type頭信息不等于text/xmlapplication/xml胜卤,但是想從responseXML拿到數(shù)據(jù)(即把數(shù)據(jù)按照 DOM 格式解析)疆导,那么需要手動(dòng)調(diào)用XMLHttpRequest.overrideMimeType()方法,強(qiáng)制進(jìn)行 XML 解析瑰艘。

該屬性得到的數(shù)據(jù)是鬼,是直接解析后的文檔 DOM 樹肤舞。

var xhr = new XMLHttpRequest();
xhr.open('GET', '/server', true);

xhr.responseType = 'document';
xhr.overrideMimeType('text/xml');

xhr.onload = function () {
  if (xhr.readyState === 4 && xhr.status === 200) {
    console.log(xhr.responseXML);
  }
};

xhr.send(null);

2.7 XMLHttpRequest.responseURL

XMLHttpRequest.responseURL屬性是字符串紫新,表示發(fā)送數(shù)據(jù)的服務(wù)器的網(wǎng)址。

var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://example.com/test', true);
xhr.onload = function () {
  // 返回 http://example.com/test
  console.log(xhr.responseURL);
};
xhr.send(null);

注意李剖,這個(gè)屬性的值與open()方法指定的請(qǐng)求網(wǎng)址不一定相同芒率。如果服務(wù)器端發(fā)生跳轉(zhuǎn),這個(gè)屬性返回最后實(shí)際返回?cái)?shù)據(jù)的網(wǎng)址篙顺。另外偶芍,如果原始 URL 包括錨點(diǎn)(fragment)充择,該屬性會(huì)把錨點(diǎn)剝離。

2.8 XMLHttpRequest.status匪蟀,XMLHttpRequest.statusText

XMLHttpRequest.status屬性返回一個(gè)整數(shù)椎麦,表示服務(wù)器回應(yīng)的 HTTP 狀態(tài)碼。一般來說材彪,如果通信成功的話观挎,這個(gè)狀態(tài)碼是200;如果服務(wù)器沒有返回狀態(tài)碼段化,那么這個(gè)屬性默認(rèn)是200嘁捷。請(qǐng)求發(fā)出之前,該屬性為0显熏。該屬性只讀雄嚣。

  • 200, OK,訪問正常
  • 301, Moved Permanently喘蟆,永久移動(dòng)
  • 302, Move temporarily缓升,暫時(shí)移動(dòng)
  • 304, Not Modified,未修改
  • 307, Temporary Redirect履肃,暫時(shí)重定向
  • 401, Unauthorized仔沿,未授權(quán)
  • 403, Forbidden,禁止訪問
  • 404, Not Found尺棋,未發(fā)現(xiàn)指定網(wǎng)址
  • 500, Internal Server Error封锉,服務(wù)器發(fā)生錯(cuò)誤

基本上,只有2xx和304的狀態(tài)碼膘螟,表示服務(wù)器返回是正常狀態(tài)成福。

if (xhr.readyState === 4) {
  if ( (xhr.status >= 200 && xhr.status < 300)
    || (xhr.status === 304) ) {
    // 處理服務(wù)器的返回?cái)?shù)據(jù)
  } else {
    // 出錯(cuò)
  }
}

XMLHttpRequest.statusText屬性返回一個(gè)字符串,表示服務(wù)器發(fā)送的狀態(tài)提示荆残。不同于status屬性奴艾,該屬性包含整個(gè)狀態(tài)信息,比如“OK”和“Not Found”内斯。在請(qǐng)求發(fā)送之前(即調(diào)用open()方法之前)蕴潦,該屬性的值是空字符串;如果服務(wù)器沒有返回狀態(tài)提示俘闯,該屬性的值默認(rèn)為”“OK”潭苞。該屬性為只讀屬性。

2.9 XMLHttpRequest.timeout真朗,XMLHttpRequestEventTarget.ontimeout

XMLHttpRequest.timeout屬性返回一個(gè)整數(shù)此疹,表示多少毫秒后,如果請(qǐng)求仍然沒有得到結(jié)果,就會(huì)自動(dòng)終止蝗碎。如果該屬性等于0湖笨,就表示沒有時(shí)間限制。

XMLHttpRequestEventTarget.ontimeout屬性用于設(shè)置一個(gè)監(jiān)聽函數(shù)蹦骑,如果發(fā)生 timeout 事件慈省,就會(huì)執(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);

2.10 事件監(jiān)聽屬性

XMLHttpRequest 對(duì)象可以對(duì)以下事件指定監(jiān)聽函數(shù)辫呻。

  • XMLHttpRequest.onloadstart:loadstart 事件(HTTP 請(qǐng)求發(fā)出)的監(jiān)聽函數(shù)
  • XMLHttpRequest.onprogress:progress事件(正在發(fā)送和加載數(shù)據(jù))的監(jiān)聽函數(shù)
  • XMLHttpRequest.onabort:abort 事件(請(qǐng)求中止,比如用戶調(diào)用了abort()方法)的監(jiān)聽函數(shù)
  • XMLHttpRequest.onerror:error 事件(請(qǐng)求失斍矸妗)的監(jiān)聽函數(shù)
  • XMLHttpRequest.onload:load 事件(請(qǐng)求成功完成)的監(jiān)聽函數(shù)
  • XMLHttpRequest.ontimeout:timeout 事件(用戶指定的時(shí)限超過了放闺,請(qǐng)求還未完成)的監(jiān)聽函數(shù)
  • XMLHttpRequest.onloadend:loadend 事件(請(qǐng)求完成,不管成功或失斅瓶病)的監(jiān)聽函數(shù)

下面是一個(gè)例子怖侦。

xhr.onload = function() {
 var responseText = xhr.responseText;
 console.log(responseText);
 // process the response.
};

xhr.onabort = function () {
  console.log('The request was aborted');
};

xhr.onprogress = function (event) {
  console.log(event.loaded);
  console.log(event.total);
};

xhr.onerror = function() {
  console.log('There was an error!');
};

progress事件的監(jiān)聽函數(shù)有一個(gè)事件對(duì)象參數(shù),該對(duì)象有三個(gè)屬性:

loaded屬性返回已經(jīng)傳輸?shù)臄?shù)據(jù)量.

total屬性返回總的數(shù)據(jù)量.

lengthComputable屬性返回一個(gè)布爾值谜叹,表示加載的進(jìn)度是否可以計(jì)算竭宰。

所有這些監(jiān)聽函數(shù)里面美澳,只有progress事件的監(jiān)聽函數(shù)有參數(shù),其他函數(shù)都沒有參數(shù)。

注意烹玉,如果發(fā)生網(wǎng)絡(luò)錯(cuò)誤(比如服務(wù)器無法連通)药蜻,onerror事件無法獲取報(bào)錯(cuò)信息御毅。也就是說轧抗,可能沒有錯(cuò)誤對(duì)象,所以這樣只能顯示報(bào)錯(cuò)的提示疾忍。

2.11 XMLHttpRequest.withCredentials

XMLHttpRequest.withCredentials屬性是一個(gè)布爾值乔外,表示跨域請(qǐng)求時(shí),用戶信息(比如 Cookie 和認(rèn)證的 HTTP 頭信息)是否會(huì)包含在請(qǐng)求之中一罩,默認(rèn)為false杨幼,即向example.com發(fā)出跨域請(qǐng)求時(shí),不會(huì)發(fā)送example.com設(shè)置在本機(jī)上的 Cookie(如果有的話)聂渊。

如果需要跨域 AJAX 請(qǐng)求發(fā)送Cookie差购,需要withCredentials屬性設(shè)為true。注意汉嗽,同源的請(qǐng)求不需要設(shè)置這個(gè)屬性欲逃。

var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://example.com/', true);
xhr.withCredentials = true;
xhr.send(null);

為了讓這個(gè)屬性生效,服務(wù)器必須顯式返回Access-Control-Allow-Credentials這個(gè)頭信息诊胞。

Access-Control-Allow-Credentials: true

withCredentials屬性打開的話暖夭,跨域請(qǐng)求不僅會(huì)發(fā)送 Cookie,還會(huì)設(shè)置遠(yuǎn)程主機(jī)指定的 Cookie撵孤。反之也成立迈着,如果withCredentials屬性沒有打開,那么跨域的 AJAX 請(qǐng)求即使明確要求瀏覽器設(shè)置 Cookie邪码,瀏覽器也會(huì)忽略裕菠。

注意,腳本總是遵守同源政策闭专,無法從document.cookie或者 HTTP 回應(yīng)的頭信息之中奴潘,讀取跨域的 Cookie,withCredentials屬性不影響這一點(diǎn)影钉。

2.12 XMLHttpRequest.upload

XMLHttpRequest 不僅可以發(fā)送請(qǐng)求画髓,還可以發(fā)送文件,這就是 AJAX 文件上傳平委。發(fā)送文件以后奈虾,通過XMLHttpRequest.upload屬性可以得到一個(gè)對(duì)象,通過觀察這個(gè)對(duì)象廉赔,可以得知上傳的進(jìn)展肉微。主要方法就是監(jiān)聽這個(gè)對(duì)象的各種事件:loadstart、loadend蜡塌、load碉纳、abort、error馏艾、progress劳曹、timeout。

假定網(wǎng)頁上有一個(gè)<progress>元素琅摩。

<progress min="0" max="100" value="0">0% complete</progress>

文件上傳時(shí)厚者,對(duì)upload屬性指定progress事件的監(jiān)聽函數(shù),即可獲得上傳的進(jìn)度迫吐。

function upload(blobOrFile) {
  var xhr = new XMLHttpRequest();
  xhr.open('POST', '/server', true);
  xhr.onload = function (e) {};

  var progressBar = document.querySelector('progress');
  xhr.upload.onprogress = function (e) {
    if (e.lengthComputable) {
      progressBar.value = (e.loaded / e.total) * 100;
      // 兼容不支持 <progress> 元素的老式瀏覽器
      progressBar.textContent = progressBar.value;
    }
  };

  xhr.send(blobOrFile);
}

upload(new Blob(['hello world'], {type: 'text/plain'}));

3. XMLHttpRequest 的實(shí)例方法

3.1 XMLHttpRequest.open()

XMLHttpRequest.open()方法用于指定 HTTP 請(qǐng)求的參數(shù)库菲,或者說初始化 XMLHttpRequest 實(shí)例對(duì)象。它一共可以接受五個(gè)參數(shù)志膀。

void open(
   string method,
   string url,
   optional boolean async,
   optional string user,
   optional string password
);
  • method:表示 HTTP 動(dòng)詞方法熙宇,比如GETPOST溉浙、PUT烫止、DELETEHEAD等戳稽。
  • url: 表示請(qǐng)求發(fā)送目標(biāo) URL馆蠕。
  • async: 布爾值期升,表示請(qǐng)求是否為異步,默認(rèn)為true互躬。如果設(shè)為false播赁,則send()方法只有等到收到服務(wù)器返回了結(jié)果,才會(huì)進(jìn)行下一步操作吼渡。該參數(shù)可選容为。由于同步 AJAX 請(qǐng)求會(huì)造成瀏覽器失去響應(yīng),許多瀏覽器已經(jīng)禁止在主線程使用寺酪,只允許 Worker 里面使用坎背。所以,這個(gè)參數(shù)輕易不應(yīng)該設(shè)為false寄雀。
  • user:表示用于認(rèn)證的用戶名得滤,默認(rèn)為空字符串。該參數(shù)可選盒犹。
  • password:表示用于認(rèn)證的密碼耿戚,默認(rèn)為空字符串。該參數(shù)可選阿趁。

注意膜蛔,如果對(duì)使用過open()方法的 AJAX 請(qǐng)求,再次使用這個(gè)方法脖阵,等同于調(diào)用abort()皂股,即終止請(qǐng)求。

下面發(fā)送 POST 請(qǐng)求的例子命黔。

var xhr = new XMLHttpRequest();
xhr.open('POST', encodeURI('someURL'));

3.2 XMLHttpRequest.send()

XMLHttpRequest.send()方法用于實(shí)際發(fā)出 HTTP 請(qǐng)求呜呐。它的參數(shù)是可選的,

如果不帶參數(shù)悍募,就表示 HTTP 請(qǐng)求只包含頭信息蘑辑,也就是只有一個(gè) URL,典型例子就是 GET 請(qǐng)求坠宴;

如果帶有參數(shù)洋魂,就表示除了頭信息,還帶有包含具體數(shù)據(jù)的信息體喜鼓,典型例子就是 POST 請(qǐng)求副砍。

下面是 GET 請(qǐng)求的例子。

var xhr = new XMLHttpRequest();
xhr.open('GET',
  'http://www.example.com/?id=' + encodeURIComponent(id),
  true
);
xhr.send(null);

// 等同于
var data = 'id=' + encodeURIComponent(id);
xhr.open('GET', 'http://www.example.com', true);
xhr.send(data);

上面代碼中庄岖,GET請(qǐng)求的參數(shù)豁翎,可以作為查詢字符串附加在 URL 后面,也可以作為send方法的參數(shù)隅忿。

下面是發(fā)送 POST 請(qǐng)求的例子心剥。

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);

如果發(fā)送 DOM 對(duì)象,在發(fā)送之前熏版,數(shù)據(jù)會(huì)先被串行化纷责。發(fā)送二進(jìn)制數(shù)據(jù),最好使用ArrayBufferViewBlob對(duì)象撼短,這使得通過 Ajax 上傳文件成為可能再膳。

下面是發(fā)送表單數(shù)據(jù)的例子。FormData對(duì)象可以用于構(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);

上面代碼FormData對(duì)象構(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>

下面的例子是使用FormData對(duì)象加工表單數(shù)據(jù)灾杰,然后再發(fā)送。

function sendForm(form) {
  var formData = new FormData(form);
  formData.append('csrf', 'e69a18d7db1286040586e6da1950128c');

  var xhr = new XMLHttpRequest();
  xhr.open('POST', form.action, true);
  xhr.onload = function() {
    // ...
  };
  xhr.send(formData);

  return false;
}

var form = document.querySelector('#registration');
sendForm(form);

3.3 XMLHttpRequest.setRequestHeader()

XMLHttpRequest.setRequestHeader()方法用于設(shè)置瀏覽器發(fā)送的 HTTP 請(qǐng)求的頭信息熙参。該方法必須在open()之后艳吠、send()之前調(diào)用。如果該方法多次調(diào)用孽椰,設(shè)定同一個(gè)字段昭娩,則每一次調(diào)用的值會(huì)被合并成一個(gè)單一的值發(fā)送。

該方法接受兩個(gè)參數(shù)黍匾。第一個(gè)參數(shù)是字符串栏渺,表示頭信息的字段名,第二個(gè)參數(shù)是字段值锐涯。

xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('Content-Length', JSON.stringify(data).length);
xhr.send(JSON.stringify(data));

上面代碼首先設(shè)置頭信息Content-Type磕诊,表示發(fā)送 JSON 格式的數(shù)據(jù);然后設(shè)置Content-Length纹腌,表示數(shù)據(jù)長(zhǎng)度秀仲;最后發(fā)送 JSON 數(shù)據(jù)。

3.4 XMLHttpRequest.overrideMimeType()

XMLHttpRequest.overrideMimeType()方法用來指定 MIME 類型壶笼,覆蓋服務(wù)器返回的真正的 MIME 類型神僵,從而讓瀏覽器進(jìn)行不一樣的處理。舉例來說覆劈,服務(wù)器返回的數(shù)據(jù)類型是text/xml保礼,由于種種原因?yàn)g覽器解析不成功報(bào)錯(cuò)沛励,這時(shí)就拿不到數(shù)據(jù)了。為了拿到原始數(shù)據(jù)炮障,我們可以把 MIME 類型改成text/plain目派,這樣瀏覽器就不會(huì)去自動(dòng)解析,從而我們就可以拿到原始文本了胁赢。

xhr.overrideMimeType('text/plain')

注意企蹭,該方法必須在send()方法之前調(diào)用。

修改服務(wù)器返回的數(shù)據(jù)類型智末,不是正常情況下應(yīng)該采取的方法谅摄。如果希望服務(wù)器返回指定的數(shù)據(jù)類型,可以用responseType屬性告訴服務(wù)器系馆,就像下面的例子送漠。只有在服務(wù)器無法返回某種數(shù)據(jù)類型時(shí),才使用overrideMimeType()方法由蘑。

var xhr = new XMLHttpRequest();
xhr.onload = function(e) {
  var arraybuffer = xhr.response;
  // ...
}
xhr.open('GET', url);
xhr.responseType = 'arraybuffer';
xhr.send();

3.5 XMLHttpRequest.getResponseHeader()

XMLHttpRequest.getResponseHeader()方法返回 HTTP 頭信息指定字段的值闽寡,如果還沒有收到服務(wù)器回應(yīng)或者指定字段不存在,返回null尼酿。該方法的參數(shù)不區(qū)分大小寫爷狈。

function getHeaderTime() {
  console.log(this.getResponseHeader("Last-Modified"));
}

var xhr = new XMLHttpRequest();
xhr.open('HEAD', 'yourpage.html');
xhr.onload = getHeaderTime;
xhr.send();

如果有多個(gè)字段同名,它們的值會(huì)被連接為一個(gè)字符串裳擎,每個(gè)字段之間使用“逗號(hào)+空格”分隔淆院。

3.6 XMLHttpRequest.getAllResponseHeaders()

XMLHttpRequest.getAllResponseHeaders()方法返回一個(gè)字符串,表示服務(wù)器發(fā)來的所有 HTTP 頭信息句惯。格式為字符串土辩,每個(gè)頭信息之間使用CRLF分隔(回車+換行),如果沒有收到服務(wù)器回應(yīng)抢野,該屬性為null拷淘。如果發(fā)生網(wǎng)絡(luò)錯(cuò)誤,該屬性為空字符串指孤。

var xhr = new XMLHttpRequest();
xhr.open('GET', 'foo.txt', true);
xhr.send();

xhr.onreadystatechange = function () {
  if (this.readyState === 4) {
    var headers = xhr.getAllResponseHeaders();
  }
}

上面代碼用于獲取服務(wù)器返回的所有頭信息启涯。它可能是下面這樣的字符串。

date: Fri, 08 Dec 2017 21:04:30 GMT\r\n
content-encoding: gzip\r\n
x-content-type-options: nosniff\r\n
server: meinheld/0.6.1\r\n
x-frame-options: DENY\r\n
content-type: text/html; charset=utf-8\r\n
connection: keep-alive\r\n
strict-transport-security: max-age=63072000\r\n
vary: Cookie, Accept-Encoding\r\n
content-length: 6502\r\n
x-xss-protection: 1; mode=block\r\n

然后恃轩,對(duì)這個(gè)字符串進(jìn)行處理结洼。

var arr = headers.trim().split(/[\r\n]+/);
var headerMap = {};

arr.forEach(function (line) {
  var parts = line.split(': ');
  var header = parts.shift();
  var value = parts.join(': ');
  headerMap[header] = value;
});

headerMap['content-length'] // "6502"

3.7 XMLHttpRequest.abort()

XMLHttpRequest.abort()方法用來終止已經(jīng)發(fā)出的 HTTP 請(qǐng)求。調(diào)用這個(gè)方法以后叉跛,readyState屬性變?yōu)?code>4松忍,status屬性變?yōu)?code>0。

var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://www.example.com/page.php', true);
setTimeout(function () {
  if (xhr) {
    xhr.abort();
    xhr = null;
  }
}, 5000);

上面代碼在發(fā)出5秒之后筷厘,終止一個(gè) AJAX 請(qǐng)求鸣峭。

4.XMLHttpRequest 實(shí)例的事件

4.1 readyStateChange 事件

readyState屬性的值發(fā)生改變宏所,就會(huì)觸發(fā) readyStateChange 事件熄浓。

我們可以通過onReadyStateChange屬性离福,指定這個(gè)事件的監(jiān)聽函數(shù),對(duì)不同狀態(tài)進(jìn)行不同處理贱纠。尤其是當(dāng)狀態(tài)變?yōu)?code>4的時(shí)候莫换,表示通信成功霞玄,這時(shí)回調(diào)函數(shù)就可以處理服務(wù)器傳送回來的數(shù)據(jù)。

4.2 progress 事件

上傳文件時(shí)拉岁,XMLHTTPRequest 實(shí)例對(duì)象本身和實(shí)例的upload屬性坷剧,都有一個(gè)progress事件,會(huì)不斷返回上傳的進(jìn)度膛薛。

var xhr = new XMLHttpRequest();

function updateProgress (oEvent) {
  if (oEvent.lengthComputable) {
    var percentComplete = oEvent.loaded / oEvent.total;
  } else {
    console.log('無法計(jì)算進(jìn)展');
  }
}

xhr.addEventListener('progress', updateProgress);

xhr.open();

4.3 load 事件听隐、error 事件补鼻、abort 事件

load 事件表示服務(wù)器傳來的數(shù)據(jù)接收完畢哄啄,error 事件表示請(qǐng)求出錯(cuò),abort 事件表示請(qǐng)求被中斷(比如用戶取消請(qǐng)求)风范。

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('用戶取消接收');
}

4.4 loadend 事件

abort咨跌、loaderror這三個(gè)事件,會(huì)伴隨一個(gè)loadend事件硼婿,表示請(qǐng)求結(jié)束锌半,但不知道其是否成功。

xhr.addEventListener('loadend', loadEnd);

function loadEnd(e) {
  console.log('請(qǐng)求結(jié)束寇漫,狀態(tài)未知');
}

4.5 timeout 事件

服務(wù)器超過指定時(shí)間還沒有返回結(jié)果刊殉,就會(huì)觸發(fā) timeout 事件,具體的例子參見timeout屬性一節(jié)州胳。

5. 文件上傳

HTML 網(wǎng)頁的<form>元素能夠以四種格式记焊,向服務(wù)器發(fā)送數(shù)據(jù)。

(1)使用POST方法栓撞,將enctype屬性設(shè)為application/x-www-form-urlencoded遍膜,這是默認(rèn)方法。

<form action="register.php" method="post" onsubmit="AJAXSubmit(this); return false;">
</form>

(2)使用POST方法瓤湘,將enctype屬性設(shè)為text/plain瓢颅。

<form action="register.php" method="post" enctype="text/plain" onsubmit="AJAXSubmit(this); return false;">
</form>

(3)使用POST方法,將enctype屬性設(shè)為multipart/form-data弛说。

<form action="register.php" method="post" enctype="multipart/form-data" onsubmit="AJAXSubmit(this); return false;">
</form>
  • 使用GET方法挽懦,enctype屬性將被忽略。
<form action="register.php" method="get" onsubmit="AJAXSubmit(this); return false;">
</form>

某個(gè)表單有兩個(gè)字段木人,分別是foobaz巾兆,其中foo字段的值等于bar猎物,baz字段的值一個(gè)分為兩行的字符串。上面四種方法角塑,都可以將這個(gè)表單發(fā)送到服務(wù)器蔫磨。

第一種方法是默認(rèn)方法,POST發(fā)送圃伶,Encoding type為application/x-www-form-urlencoded堤如。

Content-Type: application/x-www-form-urlencoded

foo=bar&baz=The+first+line.&#37;0D%0AThe+second+line.%0D%0A

第二種方法是POST發(fā)送,Encoding type為text/plain窒朋。

Content-Type: text/plain

foo=bar
baz=The first line.
The second line.

第三種方法是POST發(fā)送搀罢,Encoding type為multipart/form-data。

Content-Type: multipart/form-data; boundary=---------------------------314911788813839

-----------------------------314911788813839
Content-Disposition: form-data; name="foo"

bar
-----------------------------314911788813839
Content-Disposition: form-data; name="baz"

The first line.
The second line.

-----------------------------314911788813839--

第四種方法是GET請(qǐng)求侥猩。

?foo=bar&baz=The%20first%20line.%0AThe%20second%20line.

通常榔至,我們使用file控件實(shí)現(xiàn)文件上傳。

<form id="file-form" action="handler.php" method="POST">
  <input type="file" id="file-select" name="photos[]" multiple/>
  <button type="submit" id="upload-button">上傳</button>
</form>

上面HTML代碼中欺劳,file控件的multiple屬性唧取,指定可以一次選擇多個(gè)文件;如果沒有這個(gè)屬性划提,則一次只能選擇一個(gè)文件枫弟。

file對(duì)象的files屬性,返回一個(gè)FileList對(duì)象鹏往,包含了用戶選中的文件淡诗。

var fileSelect = document.getElementById('file-select');
var files = fileSelect.files;

然后,新建一個(gè)FormData對(duì)象的實(shí)例伊履,用來模擬發(fā)送到服務(wù)器的表單數(shù)據(jù)韩容,把選中的文件添加到這個(gè)對(duì)象上面。

var formData = new FormData();

for (var i = 0; i < files.length; i++) {
  var file = files[i];

  if (!file.type.match('image.*')) {
    continue;
  }

  formData.append('photos[]', file, file.name);
}

上面代碼中的FormData對(duì)象的append方法唐瀑,除了可以添加文件群凶,還可以添加二進(jìn)制對(duì)象(Blob)或者字符串。

// Files
formData.append(name, file, filename);

// Blobs
formData.append(name, blob, filename);

// Strings
formData.append(name, value); 

append方法的第一個(gè)參數(shù)是表單的控件名介褥,第二個(gè)參數(shù)是實(shí)際的值座掘,第三個(gè)參數(shù)是可選的,通常是文件名柔滔。

最后溢陪,使用Ajax方法向服務(wù)器上傳文件。

var xhr = new XMLHttpRequest();

xhr.open('POST', 'handler.php', true);

xhr.onload = function () {
  if (xhr.status !== 200) {
    alert('An error occurred!');
  }
};

xhr.send(formData);

除了使用FormData接口上傳睛廊,也可以直接使用File API上傳形真。

var file = document.getElementById('test-input').files[0];
var xhr = new XMLHttpRequest();

xhr.open('POST', 'myserver/uploads');
xhr.setRequestHeader('Content-Type', file.type);
xhr.send(file);

可以看到,上面這種寫法比FormData的寫法,要簡(jiǎn)單很多咆霜。

1.簡(jiǎn)介

瀏覽器與服務(wù)器之間邓馒,采用 HTTP 協(xié)議通信。用戶在瀏覽器地址欄鍵入一個(gè)網(wǎng)址蛾坯,或者通過網(wǎng)頁表單向服務(wù)器提交內(nèi)容光酣,這時(shí)瀏覽器就會(huì)向服務(wù)器發(fā)出 HTTP 請(qǐng)求。

1999年脉课,微軟公司發(fā)布 IE 瀏覽器5.0版救军,第一次引入新功能:允許 JavaScript 腳本向服務(wù)器發(fā)起 HTTP 請(qǐng)求。這個(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)頁的對(duì)應(yīng)部分,而不用刷新整個(gè)網(wǎng)頁虏辫。后來蚌吸,AJAX 這個(gè)詞就成為 JavaScript 腳本發(fā)起 HTTP 通信的代名詞锈拨,也就是說砌庄,只要用腳本發(fā)起通信,就可以叫做 AJAX 通信奕枢。W3C 也在2006年發(fā)布了它的國際標(biāo)準(zhǔn)娄昆。

具體來說,AJAX 包括以下幾個(gè)步驟缝彬。

  1. 創(chuàng)建 XMLHttpRequest 實(shí)例
  2. 發(fā)出 HTTP 請(qǐng)求
  3. 接收服務(wù)器傳回的數(shù)據(jù)
  4. 更新網(wǎng)頁數(shù)據(jù)

概括起來萌焰,就是一句話,AJAX 通過原生的XMLHttpRequest對(duì)象發(fā)出 HTTP 請(qǐng)求谷浅,得到服務(wù)器返回的數(shù)據(jù)后扒俯,再進(jìn)行處理。現(xiàn)在一疯,服務(wù)器返回的都是 JSON 格式的數(shù)據(jù)撼玄,XML 格式已經(jīng)過時(shí)了,但是 AJAX 這個(gè)名字已經(jīng)成了一個(gè)通用名詞墩邀,字面含義已經(jīng)消失了掌猛。

XMLHttpRequest對(duì)象是 AJAX 的主要接口,用于瀏覽器與服務(wù)器之間的通信眉睹。盡管名字里面有XMLHttp荔茬,它實(shí)際上可以使用多種協(xié)議(比如fileftp)废膘,發(fā)送任何格式的數(shù)據(jù)(包括字符串和二進(jìn)制)。

XMLHttpRequest本身是一個(gè)構(gòu)造函數(shù)慕蔚,可以使用new命令生成實(shí)例丐黄。它沒有任何參數(shù)。

var xhr = new XMLHttpRequest();

一旦新建實(shí)例孔飒,就可以使用open()方法發(fā)出 HTTP 請(qǐng)求孵稽。

xhr.open('GET', 'http://www.example.com/page.php', true);

上面代碼向指定的服務(wù)器網(wǎng)址,發(fā)出 GET 請(qǐng)求十偶。

然后菩鲜,指定回調(diào)函數(shù),監(jiān)聽通信狀態(tài)(readyState屬性)的變化惦积。

ajax.onreadystatechange = handleStateChange;

function handleStateChange() {
  // ...
}

上面代碼中接校,一旦XMLHttpRequest實(shí)例的狀態(tài)發(fā)生變化,就會(huì)調(diào)用監(jiān)聽函數(shù)handleStateChange

一旦拿到服務(wù)器返回的數(shù)據(jù)狮崩,AJAX 不會(huì)刷新整個(gè)網(wǎng)頁蛛勉,而是只更新網(wǎng)頁里面的相關(guān)部分,從而不打斷用戶正在做的事情睦柴。

注意诽凌,AJAX 只能向同源網(wǎng)址(協(xié)議、域名坦敌、端口都相同)發(fā)出 HTTP 請(qǐng)求侣诵,如果發(fā)出跨域請(qǐng)求,就會(huì)報(bào)錯(cuò)(詳見《同源政策》和《CORS 通信》兩章)狱窘。

下面是XMLHttpRequest對(duì)象簡(jiǎn)單用法的完整例子杜顺。

var xhr = new XMLHttpRequest();

xhr.onreadystatechange = function(){
  // 通信成功時(shí),狀態(tài)值為4
  if (xhr.readyState === 4){
    if (xhr.status === 200){
      console.log(xhr.responseText);
    } else {
      console.error(xhr.statusText);
    }
  }
};

xhr.onerror = function (e) {
  console.error(xhr.statusText);
};

xhr.open('GET', '/endpoint', true);
xhr.send(null);

2.XMLHttpRequest 的實(shí)例屬性

2.1 XMLHttpRequest.readyState

XMLHttpRequest.readyState返回一個(gè)整數(shù)蘸炸,表示實(shí)例對(duì)象的當(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 請(qǐng)求的頭信息。
  • 2,表示實(shí)例的send()方法已經(jīng)調(diào)用铲咨,并且服務(wù)器返回的頭信息和狀態(tài)碼已經(jīng)收到躲胳。
  • 3,表示正在接收服務(wù)器傳來的數(shù)據(jù)體(body 部分)纤勒。這時(shí)坯苹,如果實(shí)例的responseType屬性等于text或者空字符串,responseText屬性就會(huì)包含已經(jīng)收到的部分信息摇天。
  • 4粹湃,表示服務(wù)器返回的數(shù)據(jù)已經(jīng)完全接收,或者本次接收已經(jīng)失敗泉坐。

通信過程中为鳄,每當(dāng)實(shí)例對(duì)象發(fā)生狀態(tài)變化,它的readyState屬性的值就會(huì)改變腕让。這個(gè)值每一次變化孤钦,都會(huì)觸發(fā)readyStateChange事件。

var xhr = new XMLHttpRequest();

if (xhr.readyState === 4) {
  // 請(qǐng)求結(jié)束纯丸,處理服務(wù)器返回的數(shù)據(jù)
} else {
  // 顯示提示“加載中……”
}

上面代碼中偏形,xhr.readyState等于4時(shí),表明腳本發(fā)出的 HTTP 請(qǐng)求已經(jīng)成功觉鼻。其他情況俊扭,都表示 HTTP 請(qǐng)求還在進(jìn)行中。

2.2 XMLHttpRequest.onreadystatechange

XMLHttpRequest.onreadystatechange屬性指向一個(gè)監(jiān)聽函數(shù)坠陈。readystatechange事件發(fā)生時(shí)(實(shí)例的readyState屬性變化)萨惑,就會(huì)執(zhí)行這個(gè)屬性。

另外畅姊,如果使用實(shí)例的abort()方法咒钟,終止 XMLHttpRequest 請(qǐng)求吹由,也會(huì)造成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ù)類型,比如字符串乌昔、對(duì)象隙疚、二進(jìn)制對(duì)象等等,具體的類型由XMLHttpRequest.responseType屬性決定磕道。該屬性只讀供屉。

如果本次請(qǐng)求沒有成功或者數(shù)據(jù)不完整,該屬性等于null。但是伶丐,如果responseType屬性等于text或空字符串悼做,在請(qǐng)求沒有結(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 對(duì)象利职,表示服務(wù)器返回二進(jìn)制數(shù)組。
  • “blob”:Blob 對(duì)象瘦癌,表示服務(wù)器返回二進(jìn)制對(duì)象猪贪。
  • “document”:Document 對(duì)象,表示服務(wù)器返回一個(gè)文檔對(duì)象讯私。
  • “json”:JSON 對(duì)象热押。
  • “text”:字符串。

上面幾種類型之中斤寇,

text類型適合大多數(shù)情況桶癣,而且直接處理文本也比較方便。

document類型適合返回 HTML / XML 文檔的情況娘锁,這意味著牙寞,對(duì)于那些打開 CORS 的網(wǎng)站,可以直接用 Ajax 抓取網(wǎng)頁莫秆,然后不用解析 HTML 字符串间雀,直接對(duì)抓取回來的數(shù)據(jù)進(jìn)行 DOM 操作。

blob類型適合讀取二進(jìn)制數(shù)據(jù)镊屎,比如圖片文件惹挟。

var xhr = new XMLHttpRequest();
xhr.open('GET', '/path/to/image.png', true);
xhr.responseType = 'blob';

xhr.onload = function(e) {
  if (this.status === 200) {
    var blob = new Blob([xhr.response], {type: 'image/png'});
    // 或者
    var blob = xhr.response;
  }
};

xhr.send();

如果將這個(gè)屬性設(shè)為ArrayBuffer,就可以按照數(shù)組的方式處理二進(jìn)制數(shù)據(jù)缝驳。

var xhr = new XMLHttpRequest();
xhr.open('GET', '/path/to/image.png', true);
xhr.responseType = 'arraybuffer';

xhr.onload = function(e) {
  var uInt8Array = new Uint8Array(this.response);
  for (var i = 0, len = binStr.length; i < len; ++i) {
    // var byte = uInt8Array[i];
  }
};

xhr.send();

如果將這個(gè)屬性設(shè)為json连锯,瀏覽器就會(huì)自動(dòng)對(duì)返回?cái)?shù)據(jù)調(diào)用JSON.parse()方法归苍。也就是說,從xhr.response屬性(注意运怖,不是xhr.responseText屬性)得到的不是文本霜医,而是一個(gè) JSON 對(duì)象。

2.5 XMLHttpRequest.responseText

XMLHttpRequest.responseText屬性返回從服務(wù)器接收到的字符串驳规,該屬性為只讀肴敛。只有 HTTP 請(qǐng)求完成接收以后,該屬性才會(huì)包含完整的數(shù)據(jù)吗购。

var xhr = new XMLHttpRequest();
xhr.open('GET', '/server', true);

xhr.responseType = 'text';
xhr.onload = function () {
  if (xhr.readyState === 4 && xhr.status === 200) {
    console.log(xhr.responseText);
  }
};

xhr.send(null);

2.6 XMLHttpRequest.responseXML

XMLHttpRequest.responseXML屬性返回從服務(wù)器接收到的 HTML 或 XML 文檔對(duì)象医男,該屬性為只讀。如果本次請(qǐng)求沒有成功捻勉,或者收到的數(shù)據(jù)不能被解析為 XML 或 HTML镀梭,該屬性等于null

該屬性生效的前提是 HTTP 回應(yīng)的Content-Type頭信息等于text/xmlapplication/xml踱启。這要求在發(fā)送請(qǐng)求前报账,XMLHttpRequest.responseType屬性要設(shè)為document。如果 HTTP 回應(yīng)的Content-Type頭信息不等于text/xmlapplication/xml埠偿,但是想從responseXML拿到數(shù)據(jù)(即把數(shù)據(jù)按照 DOM 格式解析)透罢,那么需要手動(dòng)調(diào)用XMLHttpRequest.overrideMimeType()方法,強(qiáng)制進(jìn)行 XML 解析冠蒋。

該屬性得到的數(shù)據(jù)羽圃,是直接解析后的文檔 DOM 樹。

var xhr = new XMLHttpRequest();
xhr.open('GET', '/server', true);

xhr.responseType = 'document';
xhr.overrideMimeType('text/xml');

xhr.onload = function () {
  if (xhr.readyState === 4 && xhr.status === 200) {
    console.log(xhr.responseXML);
  }
};

xhr.send(null);

2.7 XMLHttpRequest.responseURL

XMLHttpRequest.responseURL屬性是字符串抖剿,表示發(fā)送數(shù)據(jù)的服務(wù)器的網(wǎng)址朽寞。

var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://example.com/test', true);
xhr.onload = function () {
  // 返回 http://example.com/test
  console.log(xhr.responseURL);
};
xhr.send(null);

注意,這個(gè)屬性的值與open()方法指定的請(qǐng)求網(wǎng)址不一定相同斩郎。如果服務(wù)器端發(fā)生跳轉(zhuǎn)脑融,這個(gè)屬性返回最后實(shí)際返回?cái)?shù)據(jù)的網(wǎng)址。另外缩宜,如果原始 URL 包括錨點(diǎn)(fragment)肘迎,該屬性會(huì)把錨點(diǎn)剝離。

2.8 XMLHttpRequest.status脓恕,XMLHttpRequest.statusText

XMLHttpRequest.status屬性返回一個(gè)整數(shù)膜宋,表示服務(wù)器回應(yīng)的 HTTP 狀態(tài)碼。一般來說炼幔,如果通信成功的話,這個(gè)狀態(tài)碼是200史简;如果服務(wù)器沒有返回狀態(tài)碼乃秀,那么這個(gè)屬性默認(rèn)是200肛著。請(qǐng)求發(fā)出之前,該屬性為0跺讯。該屬性只讀枢贿。

  • 200, OK,訪問正常
  • 301, Moved Permanently刀脏,永久移動(dòng)
  • 302, Move temporarily局荚,暫時(shí)移動(dòng)
  • 304, Not Modified,未修改
  • 307, Temporary Redirect愈污,暫時(shí)重定向
  • 401, Unauthorized耀态,未授權(quán)
  • 403, Forbidden,禁止訪問
  • 404, Not Found暂雹,未發(fā)現(xiàn)指定網(wǎng)址
  • 500, Internal Server Error首装,服務(wù)器發(fā)生錯(cuò)誤

基本上,只有2xx和304的狀態(tài)碼杭跪,表示服務(wù)器返回是正常狀態(tài)仙逻。

if (xhr.readyState === 4) {
  if ( (xhr.status >= 200 && xhr.status < 300)
    || (xhr.status === 304) ) {
    // 處理服務(wù)器的返回?cái)?shù)據(jù)
  } else {
    // 出錯(cuò)
  }
}

XMLHttpRequest.statusText屬性返回一個(gè)字符串,表示服務(wù)器發(fā)送的狀態(tài)提示涧尿。不同于status屬性系奉,該屬性包含整個(gè)狀態(tài)信息,比如“OK”和“Not Found”姑廉。在請(qǐng)求發(fā)送之前(即調(diào)用open()方法之前)喜最,該屬性的值是空字符串;如果服務(wù)器沒有返回狀態(tài)提示庄蹋,該屬性的值默認(rèn)為”“OK”瞬内。該屬性為只讀屬性。

2.9 XMLHttpRequest.timeout限书,XMLHttpRequestEventTarget.ontimeout

XMLHttpRequest.timeout屬性返回一個(gè)整數(shù)虫蝶,表示多少毫秒后,如果請(qǐng)求仍然沒有得到結(jié)果倦西,就會(huì)自動(dòng)終止能真。如果該屬性等于0,就表示沒有時(shí)間限制扰柠。

XMLHttpRequestEventTarget.ontimeout屬性用于設(shè)置一個(gè)監(jiān)聽函數(shù)粉铐,如果發(fā)生 timeout 事件,就會(huì)執(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);

2.10 事件監(jiān)聽屬性

XMLHttpRequest 對(duì)象可以對(duì)以下事件指定監(jiān)聽函數(shù)。

  • XMLHttpRequest.onloadstart:loadstart 事件(HTTP 請(qǐng)求發(fā)出)的監(jiān)聽函數(shù)
  • XMLHttpRequest.onprogress:progress事件(正在發(fā)送和加載數(shù)據(jù))的監(jiān)聽函數(shù)
  • XMLHttpRequest.onabort:abort 事件(請(qǐng)求中止劝枣,比如用戶調(diào)用了abort()方法)的監(jiān)聽函數(shù)
  • XMLHttpRequest.onerror:error 事件(請(qǐng)求失斕捞ぁ)的監(jiān)聽函數(shù)
  • XMLHttpRequest.onload:load 事件(請(qǐng)求成功完成)的監(jiān)聽函數(shù)
  • XMLHttpRequest.ontimeout:timeout 事件(用戶指定的時(shí)限超過了织鲸,請(qǐng)求還未完成)的監(jiān)聽函數(shù)
  • XMLHttpRequest.onloadend:loadend 事件(請(qǐng)求完成,不管成功或失斚骸)的監(jiān)聽函數(shù)

下面是一個(gè)例子搂擦。

xhr.onload = function() {
 var responseText = xhr.responseText;
 console.log(responseText);
 // process the response.
};

xhr.onabort = function () {
  console.log('The request was aborted');
};

xhr.onprogress = function (event) {
  console.log(event.loaded);
  console.log(event.total);
};

xhr.onerror = function() {
  console.log('There was an error!');
};

progress事件的監(jiān)聽函數(shù)有一個(gè)事件對(duì)象參數(shù),該對(duì)象有三個(gè)屬性:

loaded屬性返回已經(jīng)傳輸?shù)臄?shù)據(jù)量.

total屬性返回總的數(shù)據(jù)量.

lengthComputable屬性返回一個(gè)布爾值哗脖,表示加載的進(jìn)度是否可以計(jì)算瀑踢。

所有這些監(jiān)聽函數(shù)里面,只有progress事件的監(jiān)聽函數(shù)有參數(shù)才避,其他函數(shù)都沒有參數(shù)橱夭。

注意,如果發(fā)生網(wǎng)絡(luò)錯(cuò)誤(比如服務(wù)器無法連通)工扎,onerror事件無法獲取報(bào)錯(cuò)信息徘钥。也就是說,可能沒有錯(cuò)誤對(duì)象肢娘,所以這樣只能顯示報(bào)錯(cuò)的提示呈础。

2.11 XMLHttpRequest.withCredentials

XMLHttpRequest.withCredentials屬性是一個(gè)布爾值,表示跨域請(qǐng)求時(shí)橱健,用戶信息(比如 Cookie 和認(rèn)證的 HTTP 頭信息)是否會(huì)包含在請(qǐng)求之中而钞,默認(rèn)為false,即向example.com發(fā)出跨域請(qǐng)求時(shí)拘荡,不會(huì)發(fā)送example.com設(shè)置在本機(jī)上的 Cookie(如果有的話)臼节。

如果需要跨域 AJAX 請(qǐng)求發(fā)送Cookie,需要withCredentials屬性設(shè)為true珊皿。注意网缝,同源的請(qǐng)求不需要設(shè)置這個(gè)屬性。

var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://example.com/', true);
xhr.withCredentials = true;
xhr.send(null);

為了讓這個(gè)屬性生效蟋定,服務(wù)器必須顯式返回Access-Control-Allow-Credentials這個(gè)頭信息粉臊。

Access-Control-Allow-Credentials: true

withCredentials屬性打開的話,跨域請(qǐng)求不僅會(huì)發(fā)送 Cookie驶兜,還會(huì)設(shè)置遠(yuǎn)程主機(jī)指定的 Cookie扼仲。反之也成立,如果withCredentials屬性沒有打開抄淑,那么跨域的 AJAX 請(qǐng)求即使明確要求瀏覽器設(shè)置 Cookie屠凶,瀏覽器也會(huì)忽略。

注意肆资,腳本總是遵守同源政策矗愧,無法從document.cookie或者 HTTP 回應(yīng)的頭信息之中,讀取跨域的 Cookie迅耘,withCredentials屬性不影響這一點(diǎn)贱枣。

2.12 XMLHttpRequest.upload

XMLHttpRequest 不僅可以發(fā)送請(qǐng)求监署,還可以發(fā)送文件颤专,這就是 AJAX 文件上傳纽哥。發(fā)送文件以后,通過XMLHttpRequest.upload屬性可以得到一個(gè)對(duì)象栖秕,通過觀察這個(gè)對(duì)象春塌,可以得知上傳的進(jìn)展。主要方法就是監(jiān)聽這個(gè)對(duì)象的各種事件:loadstart簇捍、loadend只壳、load、abort暑塑、error吼句、progress、timeout事格。

假定網(wǎng)頁上有一個(gè)<progress>元素惕艳。

<progress min="0" max="100" value="0">0% complete</progress>

文件上傳時(shí),對(duì)upload屬性指定progress事件的監(jiān)聽函數(shù)驹愚,即可獲得上傳的進(jìn)度远搪。

function upload(blobOrFile) {
  var xhr = new XMLHttpRequest();
  xhr.open('POST', '/server', true);
  xhr.onload = function (e) {};

  var progressBar = document.querySelector('progress');
  xhr.upload.onprogress = function (e) {
    if (e.lengthComputable) {
      progressBar.value = (e.loaded / e.total) * 100;
      // 兼容不支持 <progress> 元素的老式瀏覽器
      progressBar.textContent = progressBar.value;
    }
  };

  xhr.send(blobOrFile);
}

upload(new Blob(['hello world'], {type: 'text/plain'}));

3. XMLHttpRequest 的實(shí)例方法

3.1 XMLHttpRequest.open()

XMLHttpRequest.open()方法用于指定 HTTP 請(qǐng)求的參數(shù),或者說初始化 XMLHttpRequest 實(shí)例對(duì)象逢捺。它一共可以接受五個(gè)參數(shù)谁鳍。

void open(
   string method,
   string url,
   optional boolean async,
   optional string user,
   optional string password
);
  • method:表示 HTTP 動(dòng)詞方法,比如GET劫瞳、POST倘潜、PUTDELETE志于、HEAD等涮因。
  • url: 表示請(qǐng)求發(fā)送目標(biāo) URL。
  • async: 布爾值恨憎,表示請(qǐng)求是否為異步蕊退,默認(rèn)為true。如果設(shè)為false憔恳,則send()方法只有等到收到服務(wù)器返回了結(jié)果瓤荔,才會(huì)進(jìn)行下一步操作。該參數(shù)可選钥组。由于同步 AJAX 請(qǐng)求會(huì)造成瀏覽器失去響應(yīng)输硝,許多瀏覽器已經(jīng)禁止在主線程使用,只允許 Worker 里面使用程梦。所以点把,這個(gè)參數(shù)輕易不應(yīng)該設(shè)為false橘荠。
  • user:表示用于認(rèn)證的用戶名,默認(rèn)為空字符串郎逃。該參數(shù)可選哥童。
  • password:表示用于認(rèn)證的密碼,默認(rèn)為空字符串褒翰。該參數(shù)可選贮懈。

注意,如果對(duì)使用過open()方法的 AJAX 請(qǐng)求优训,再次使用這個(gè)方法朵你,等同于調(diào)用abort(),即終止請(qǐng)求揣非。

下面發(fā)送 POST 請(qǐng)求的例子抡医。

var xhr = new XMLHttpRequest();
xhr.open('POST', encodeURI('someURL'));

3.2 XMLHttpRequest.send()

XMLHttpRequest.send()方法用于實(shí)際發(fā)出 HTTP 請(qǐng)求。它的參數(shù)是可選的早敬,

如果不帶參數(shù)忌傻,就表示 HTTP 請(qǐng)求只包含頭信息,也就是只有一個(gè) URL搁嗓,典型例子就是 GET 請(qǐng)求芯勘;

如果帶有參數(shù),就表示除了頭信息,還帶有包含具體數(shù)據(jù)的信息體,典型例子就是 POST 請(qǐng)求露戒。

下面是 GET 請(qǐng)求的例子唆途。

var xhr = new XMLHttpRequest();
xhr.open('GET',
  'http://www.example.com/?id=' + encodeURIComponent(id),
  true
);
xhr.send(null);

// 等同于
var data = 'id=' + encodeURIComponent(id);
xhr.open('GET', 'http://www.example.com', true);
xhr.send(data);

上面代碼中,GET請(qǐng)求的參數(shù),可以作為查詢字符串附加在 URL 后面,也可以作為send方法的參數(shù)。

下面是發(fā)送 POST 請(qǐng)求的例子荐类。

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);

如果發(fā)送 DOM 對(duì)象,在發(fā)送之前铁追,數(shù)據(jù)會(huì)先被串行化季蚂。發(fā)送二進(jìn)制數(shù)據(jù),最好使用ArrayBufferViewBlob對(duì)象,這使得通過 Ajax 上傳文件成為可能扭屁。

下面是發(fā)送表單數(shù)據(jù)的例子算谈。FormData對(duì)象可以用于構(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);

上面代碼FormData對(duì)象構(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>

下面的例子是使用FormData對(duì)象加工表單數(shù)據(jù)幔欧,然后再發(fā)送罪治。

function sendForm(form) {
  var formData = new FormData(form);
  formData.append('csrf', 'e69a18d7db1286040586e6da1950128c');

  var xhr = new XMLHttpRequest();
  xhr.open('POST', form.action, true);
  xhr.onload = function() {
    // ...
  };
  xhr.send(formData);

  return false;
}

var form = document.querySelector('#registration');
sendForm(form);

3.3 XMLHttpRequest.setRequestHeader()

XMLHttpRequest.setRequestHeader()方法用于設(shè)置瀏覽器發(fā)送的 HTTP 請(qǐng)求的頭信息丽声。該方法必須在open()之后礁蔗、send()之前調(diào)用。如果該方法多次調(diào)用雁社,設(shè)定同一個(gè)字段浴井,則每一次調(diào)用的值會(huì)被合并成一個(gè)單一的值發(fā)送。

該方法接受兩個(gè)參數(shù)霉撵。第一個(gè)參數(shù)是字符串磺浙,表示頭信息的字段名,第二個(gè)參數(shù)是字段值徒坡。

xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('Content-Length', JSON.stringify(data).length);
xhr.send(JSON.stringify(data));

上面代碼首先設(shè)置頭信息Content-Type撕氧,表示發(fā)送 JSON 格式的數(shù)據(jù);然后設(shè)置Content-Length喇完,表示數(shù)據(jù)長(zhǎng)度伦泥;最后發(fā)送 JSON 數(shù)據(jù)。

3.4 XMLHttpRequest.overrideMimeType()

XMLHttpRequest.overrideMimeType()方法用來指定 MIME 類型锦溪,覆蓋服務(wù)器返回的真正的 MIME 類型不脯,從而讓瀏覽器進(jìn)行不一樣的處理。舉例來說刻诊,服務(wù)器返回的數(shù)據(jù)類型是text/xml防楷,由于種種原因?yàn)g覽器解析不成功報(bào)錯(cuò),這時(shí)就拿不到數(shù)據(jù)了则涯。為了拿到原始數(shù)據(jù)复局,我們可以把 MIME 類型改成text/plain,這樣瀏覽器就不會(huì)去自動(dòng)解析粟判,從而我們就可以拿到原始文本了亿昏。

xhr.overrideMimeType('text/plain')

注意,該方法必須在send()方法之前調(diào)用浮入。

修改服務(wù)器返回的數(shù)據(jù)類型龙优,不是正常情況下應(yīng)該采取的方法。如果希望服務(wù)器返回指定的數(shù)據(jù)類型,可以用responseType屬性告訴服務(wù)器彤断,就像下面的例子野舶。只有在服務(wù)器無法返回某種數(shù)據(jù)類型時(shí),才使用overrideMimeType()方法宰衙。

var xhr = new XMLHttpRequest();
xhr.onload = function(e) {
  var arraybuffer = xhr.response;
  // ...
}
xhr.open('GET', url);
xhr.responseType = 'arraybuffer';
xhr.send();

3.5 XMLHttpRequest.getResponseHeader()

XMLHttpRequest.getResponseHeader()方法返回 HTTP 頭信息指定字段的值平道,如果還沒有收到服務(wù)器回應(yīng)或者指定字段不存在,返回null供炼。該方法的參數(shù)不區(qū)分大小寫一屋。

function getHeaderTime() {
  console.log(this.getResponseHeader("Last-Modified"));
}

var xhr = new XMLHttpRequest();
xhr.open('HEAD', 'yourpage.html');
xhr.onload = getHeaderTime;
xhr.send();

如果有多個(gè)字段同名,它們的值會(huì)被連接為一個(gè)字符串袋哼,每個(gè)字段之間使用“逗號(hào)+空格”分隔冀墨。

3.6 XMLHttpRequest.getAllResponseHeaders()

XMLHttpRequest.getAllResponseHeaders()方法返回一個(gè)字符串,表示服務(wù)器發(fā)來的所有 HTTP 頭信息涛贯。格式為字符串诽嘉,每個(gè)頭信息之間使用CRLF分隔(回車+換行),如果沒有收到服務(wù)器回應(yīng)弟翘,該屬性為null虫腋。如果發(fā)生網(wǎng)絡(luò)錯(cuò)誤,該屬性為空字符串稀余。

var xhr = new XMLHttpRequest();
xhr.open('GET', 'foo.txt', true);
xhr.send();

xhr.onreadystatechange = function () {
  if (this.readyState === 4) {
    var headers = xhr.getAllResponseHeaders();
  }
}

上面代碼用于獲取服務(wù)器返回的所有頭信息悦冀。它可能是下面這樣的字符串。

date: Fri, 08 Dec 2017 21:04:30 GMT\r\n
content-encoding: gzip\r\n
x-content-type-options: nosniff\r\n
server: meinheld/0.6.1\r\n
x-frame-options: DENY\r\n
content-type: text/html; charset=utf-8\r\n
connection: keep-alive\r\n
strict-transport-security: max-age=63072000\r\n
vary: Cookie, Accept-Encoding\r\n
content-length: 6502\r\n
x-xss-protection: 1; mode=block\r\n

然后睛琳,對(duì)這個(gè)字符串進(jìn)行處理盒蟆。

var arr = headers.trim().split(/[\r\n]+/);
var headerMap = {};

arr.forEach(function (line) {
  var parts = line.split(': ');
  var header = parts.shift();
  var value = parts.join(': ');
  headerMap[header] = value;
});

headerMap['content-length'] // "6502"

3.7 XMLHttpRequest.abort()

XMLHttpRequest.abort()方法用來終止已經(jīng)發(fā)出的 HTTP 請(qǐng)求。調(diào)用這個(gè)方法以后掸掏,readyState屬性變?yōu)?code>4茁影,status屬性變?yōu)?code>0。

var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://www.example.com/page.php', true);
setTimeout(function () {
  if (xhr) {
    xhr.abort();
    xhr = null;
  }
}, 5000);

上面代碼在發(fā)出5秒之后丧凤,終止一個(gè) AJAX 請(qǐng)求募闲。

4.XMLHttpRequest 實(shí)例的事件

4.1 readyStateChange 事件

readyState屬性的值發(fā)生改變,就會(huì)觸發(fā) readyStateChange 事件愿待。

我們可以通過onReadyStateChange屬性浩螺,指定這個(gè)事件的監(jiān)聽函數(shù),對(duì)不同狀態(tài)進(jìn)行不同處理仍侥。尤其是當(dāng)狀態(tài)變?yōu)?code>4的時(shí)候要出,表示通信成功,這時(shí)回調(diào)函數(shù)就可以處理服務(wù)器傳送回來的數(shù)據(jù)农渊。

4.2 progress 事件

上傳文件時(shí)患蹂,XMLHTTPRequest 實(shí)例對(duì)象本身和實(shí)例的upload屬性,都有一個(gè)progress事件,會(huì)不斷返回上傳的進(jìn)度传于。

var xhr = new XMLHttpRequest();

function updateProgress (oEvent) {
  if (oEvent.lengthComputable) {
    var percentComplete = oEvent.loaded / oEvent.total;
  } else {
    console.log('無法計(jì)算進(jìn)展');
  }
}

xhr.addEventListener('progress', updateProgress);

xhr.open();

4.3 load 事件囱挑、error 事件、abort 事件

load 事件表示服務(wù)器傳來的數(shù)據(jù)接收完畢沼溜,error 事件表示請(qǐng)求出錯(cuò)平挑,abort 事件表示請(qǐng)求被中斷(比如用戶取消請(qǐng)求)。

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('用戶取消接收');
}

4.4 loadend 事件

abort系草、loaderror這三個(gè)事件通熄,會(huì)伴隨一個(gè)loadend事件,表示請(qǐng)求結(jié)束找都,但不知道其是否成功唇辨。

xhr.addEventListener('loadend', loadEnd);

function loadEnd(e) {
  console.log('請(qǐng)求結(jié)束,狀態(tài)未知');
}

4.5 timeout 事件

服務(wù)器超過指定時(shí)間還沒有返回結(jié)果檐嚣,就會(huì)觸發(fā) timeout 事件助泽,具體的例子參見timeout屬性一節(jié)。

5. 文件上傳

HTML 網(wǎng)頁的<form>元素能夠以四種格式嚎京,向服務(wù)器發(fā)送數(shù)據(jù)。

(1)使用POST方法隐解,將enctype屬性設(shè)為application/x-www-form-urlencoded鞍帝,這是默認(rèn)方法。

<form action="register.php" method="post" onsubmit="AJAXSubmit(this); return false;">
</form>

(2)使用POST方法煞茫,將enctype屬性設(shè)為text/plain帕涌。

<form action="register.php" method="post" enctype="text/plain" onsubmit="AJAXSubmit(this); return false;">
</form>

(3)使用POST方法,將enctype屬性設(shè)為multipart/form-data续徽。

<form action="register.php" method="post" enctype="multipart/form-data" onsubmit="AJAXSubmit(this); return false;">
</form>
  • 使用GET方法蚓曼,enctype屬性將被忽略。
<form action="register.php" method="get" onsubmit="AJAXSubmit(this); return false;">
</form>

某個(gè)表單有兩個(gè)字段钦扭,分別是foobaz纫版,其中foo字段的值等于barbaz字段的值一個(gè)分為兩行的字符串客情。上面四種方法其弊,都可以將這個(gè)表單發(fā)送到服務(wù)器。

第一種方法是默認(rèn)方法膀斋,POST發(fā)送梭伐,Encoding type為application/x-www-form-urlencoded。

Content-Type: application/x-www-form-urlencoded

foo=bar&baz=The+first+line.&#37;0D%0AThe+second+line.%0D%0A

第二種方法是POST發(fā)送仰担,Encoding type為text/plain糊识。

Content-Type: text/plain

foo=bar
baz=The first line.
The second line.

第三種方法是POST發(fā)送,Encoding type為multipart/form-data。

Content-Type: multipart/form-data; boundary=---------------------------314911788813839

-----------------------------314911788813839
Content-Disposition: form-data; name="foo"

bar
-----------------------------314911788813839
Content-Disposition: form-data; name="baz"

The first line.
The second line.

-----------------------------314911788813839--

第四種方法是GET請(qǐng)求赂苗。

?foo=bar&baz=The%20first%20line.%0AThe%20second%20line.

通常铃将,我們使用file控件實(shí)現(xiàn)文件上傳。

<form id="file-form" action="handler.php" method="POST">
  <input type="file" id="file-select" name="photos[]" multiple/>
  <button type="submit" id="upload-button">上傳</button>
</form>

上面HTML代碼中哑梳,file控件的multiple屬性劲阎,指定可以一次選擇多個(gè)文件;如果沒有這個(gè)屬性鸠真,則一次只能選擇一個(gè)文件悯仙。

file對(duì)象的files屬性,返回一個(gè)FileList對(duì)象吠卷,包含了用戶選中的文件锡垄。

var fileSelect = document.getElementById('file-select');
var files = fileSelect.files;

然后,新建一個(gè)FormData對(duì)象的實(shí)例祭隔,用來模擬發(fā)送到服務(wù)器的表單數(shù)據(jù)货岭,把選中的文件添加到這個(gè)對(duì)象上面。

var formData = new FormData();

for (var i = 0; i < files.length; i++) {
  var file = files[i];

  if (!file.type.match('image.*')) {
    continue;
  }

  formData.append('photos[]', file, file.name);
}

上面代碼中的FormData對(duì)象的append方法疾渴,除了可以添加文件千贯,還可以添加二進(jìn)制對(duì)象(Blob)或者字符串。

// Files
formData.append(name, file, filename);

// Blobs
formData.append(name, blob, filename);

// Strings
formData.append(name, value); 

append方法的第一個(gè)參數(shù)是表單的控件名搞坝,第二個(gè)參數(shù)是實(shí)際的值搔谴,第三個(gè)參數(shù)是可選的,通常是文件名桩撮。

最后敦第,使用Ajax方法向服務(wù)器上傳文件。

var xhr = new XMLHttpRequest();

xhr.open('POST', 'handler.php', true);

xhr.onload = function () {
  if (xhr.status !== 200) {
    alert('An error occurred!');
  }
};

xhr.send(formData);

除了使用FormData接口上傳店量,也可以直接使用File API上傳芜果。

var file = document.getElementById('test-input').files[0];
var xhr = new XMLHttpRequest();

xhr.open('POST', 'myserver/uploads');
xhr.setRequestHeader('Content-Type', file.type);
xhr.send(file);

可以看到,上面這種寫法比FormData的寫法融师,要簡(jiǎn)單很多右钾。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市诬滩,隨后出現(xiàn)的幾起案子霹粥,更是在濱河造成了極大的恐慌,老刑警劉巖疼鸟,帶你破解...
    沈念sama閱讀 221,820評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件后控,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡空镜,警方通過查閱死者的電腦和手機(jī)浩淘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門捌朴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人张抄,你說我怎么就攤上這事砂蔽。” “怎么了署惯?”我有些...
    開封第一講書人閱讀 168,324評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵左驾,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我极谊,道長(zhǎng)诡右,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,714評(píng)論 1 297
  • 正文 為了忘掉前任轻猖,我火速辦了婚禮帆吻,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘咙边。我一直安慰自己猜煮,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,724評(píng)論 6 397
  • 文/花漫 我一把揭開白布败许。 她就那樣靜靜地躺著王带,像睡著了一般。 火紅的嫁衣襯著肌膚如雪檐束。 梳的紋絲不亂的頭發(fā)上辫秧,一...
    開封第一講書人閱讀 52,328評(píng)論 1 310
  • 那天,我揣著相機(jī)與錄音被丧,去河邊找鬼。 笑死绪妹,一個(gè)胖子當(dāng)著我的面吹牛甥桂,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播邮旷,決...
    沈念sama閱讀 40,897評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼黄选,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了婶肩?” 一聲冷哼從身側(cè)響起办陷,我...
    開封第一講書人閱讀 39,804評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎律歼,沒想到半個(gè)月后民镜,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,345評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡险毁,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,431評(píng)論 3 340
  • 正文 我和宋清朗相戀三年制圈,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了们童。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,561評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡鲸鹦,死狀恐怖慧库,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情馋嗜,我是刑警寧澤齐板,帶...
    沈念sama閱讀 36,238評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站葛菇,受9級(jí)特大地震影響甘磨,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜熟呛,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,928評(píng)論 3 334
  • 文/蒙蒙 一宽档、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧庵朝,春花似錦吗冤、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至侄旬,卻和暖如春肺蔚,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背儡羔。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評(píng)論 1 272
  • 我被黑心中介騙來泰國打工哗咆, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人莉钙。 一個(gè)月前我還...
    沈念sama閱讀 48,983評(píng)論 3 376
  • 正文 我出身青樓苛茂,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親族操。 傳聞我的和親對(duì)象是個(gè)殘疾皇子苛坚,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,573評(píng)論 2 359