- xmlhttprequest 對象
- 簡介
- xmlhttprequest 的實例屬性
- xmlhttprequest.readystate
- xmlhttprequest.onreadystatechange
- xmlhttprequest.response
- xmlhttprequest.responsetype
- xmlhttprequest.responsetext
- xmlhttprequest.responsexml
- xmlhttprequest.responseurl
- xmlhttprequest.status,xmlhttprequest.statustext
- xmlhttprequest.timeout,xmlhttprequesteventtarget.ontimeout
- 事件監(jiān)聽屬性
- xmlhttprequest.withcredentials
- xmlhttprequest.upload
- xmlhttprequest 的實例方法
- xmlhttprequest.open()
- xmlhttprequest.send()
- xmlhttprequest.setrequestheader()
- xmlhttprequest.overridemimetype()
- xmlhttprequest.getresponseheader()
- xmlhttprequest.getallresponseheaders()
- xmlhttprequest.abort()
- xmlhttprequest 實例的事件
- navigator.sendbeacon()
- 總結(jié)
xmlhttprequest 對象
簡介
瀏覽器與服務(wù)器之間,采用 http 協(xié)議通信。用戶在瀏覽器地址欄鍵入一個網(wǎng)址,或者通過網(wǎng)頁表單向服務(wù)器提交內(nèi)容,這時瀏覽器就會向服務(wù)器發(fā)出 http 請求。
1999年,微軟公司發(fā)布 ie 瀏覽器5.0版,第一次引入新功能:允許 javascript 腳本向服務(wù)器發(fā)起 http 請求。這個功能當(dāng)時并沒有引起注意,直到2004年 gmail 發(fā)布和2005年 google map 發(fā)布,才引起廣泛重視。2005年2月,ajax 這個詞第一次正式提出,它是 asynchronous javascript and xml 的縮寫,指的是通過 javascript 的異步通信,從服務(wù)器獲取 xml 文檔從中提取數(shù)據(jù),再更新當(dāng)前網(wǎng)頁的對應(yīng)部分,而不用刷新整個網(wǎng)頁。后來,ajax 這個詞就成為 javascript 腳本發(fā)起 http 通信的代名詞,也就是說,只要用腳本發(fā)起通信,就可以叫做 ajax 通信。w3c 也在2006年發(fā)布了它的國際標(biāo)準(zhǔn)。
具體來說,ajax 包括以下幾個步驟。
概括起來,就是一句話,ajax 通過原生的xmlhttprequest對象發(fā)出 http 請求,得到服務(wù)器返回的數(shù)據(jù)后,再進(jìn)行處理。現(xiàn)在,服務(wù)器返回的都是 json 格式的數(shù)據(jù),xml 格式已經(jīng)過時了,但是 ajax 這個名字已經(jīng)成了一個通用名詞,字面含義已經(jīng)消失了。
xmlhttprequest對象是 ajax 的主要接口,用于瀏覽器與服務(wù)器之間的通信。盡管名字里面有xml和http,它實際上可以使用多種協(xié)議(比如file或ftp),發(fā)送任何格式的數(shù)據(jù)(包括字符串和二進(jìn)制)。
xmlhttprequest本身是一個構(gòu)造函數(shù),可以使用new命令生成實例。它沒有任何參數(shù)。
var xhr = new xmlhttprequest();
一旦新建實例,就可以使用open()方法指定建立 http 連接的一些細(xì)節(jié)。
xhr.open('get', 'http://www.example.com/page.php', true);
上面代碼指定使用 get 方法,跟指定的服務(wù)器網(wǎng)址建立連接。第三個參數(shù)true,表示請求是異步的。
然后,指定回調(diào)函數(shù),監(jiān)聽通信狀態(tài)(readystate屬性)的變化。
xhr.onreadystatechange = handlestatechange; function handlestatechange() { // ... }
上面代碼中,一旦xmlhttprequest實例的狀態(tài)發(fā)生變化,就會調(diào)用監(jiān)聽函數(shù)handlestatechange
最后使用send()方法,實際發(fā)出請求。
xhr.send(null);
上面代碼中,send()的參數(shù)為null,表示發(fā)送請求的時候,不帶有數(shù)據(jù)體。如果發(fā)送的是 post 請求,這里就需要指定數(shù)據(jù)體。
一旦拿到服務(wù)器返回的數(shù)據(jù),ajax 不會刷新整個網(wǎng)頁,而是只更新網(wǎng)頁里面的相關(guān)部分,從而不打斷用戶正在做的事情。
注意,ajax 只能向同源網(wǎng)址(協(xié)議、域名、端口都相同)發(fā)出 http 請求,如果發(fā)出跨域請求,就會報錯(詳見《同源政策》和《cors 通信》兩章)。
下面是xmlhttprequest對象簡單用法的完整例子。
var xhr = new xmlhttprequest(); xhr.onreadystatechange = function(){ // 通信成功時,狀態(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);
xmlhttprequest 的實例屬性
xmlhttprequest.readystate
xmlhttprequest.readystate返回一個整數(shù),表示實例對象的當(dāng)前狀態(tài)。該屬性只讀。它可能返回以下值。
0,表示 xmlhttprequest 實例已經(jīng)生成,但是實例的open()方法還沒有被調(diào)用。
1,表示open()方法已經(jīng)調(diào)用,但是實例的send()方法還沒有調(diào)用,仍然可以使用實例的setrequestheader()方法,設(shè)定 http 請求的頭信息。
2,表示實例的send()方法已經(jīng)調(diào)用,并且服務(wù)器返回的頭信息和狀態(tài)碼已經(jīng)收到。
3,表示正在接收服務(wù)器傳來的數(shù)據(jù)體(body 部分)。這時,如果實例的responsetype屬性等于text或者空字符串,responsetext屬性就會包含已經(jīng)收到的部分信息。
4,表示服務(wù)器返回的數(shù)據(jù)已經(jīng)完全接收,或者本次接收已經(jīng)失敗。
通信過程中,每當(dāng)實例對象發(fā)生狀態(tài)變化,它的readystate屬性的值就會改變。這個值每一次變化,都會觸發(fā)readystatechange事件。
var xhr = new xmlhttprequest(); if (xhr.readystate === 4) { ??// 請求結(jié)束,處理服務(wù)器返回的數(shù)據(jù) } else { ? // 顯示提示“加載中……” }
上面代碼中,xhr.readystate等于4時,表明腳本發(fā)出的 http 請求已經(jīng)完成。其他情況,都表示 http 請求還在進(jìn)行中。
xmlhttprequest.onreadystatechange
xmlhttprequest.onreadystatechange屬性指向一個監(jiān)聽函數(shù)。readystatechange事件發(fā)生時(實例的readystate屬性變化),就會執(zhí)行這個屬性。
另外,如果使用實例的abort()方法,終止 xmlhttprequest 請求,也會造成readystate屬性變化,導(dǎo)致調(diào)用xmlhttprequest.onreadystatechange屬性。
下面是一個例子。
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();
xmlhttprequest.response
xmlhttprequest.response屬性表示服務(wù)器返回的數(shù)據(jù)體(即 http 回應(yīng)的 body 部分)。它可能是任何數(shù)據(jù)類型,比如字符串、對象、二進(jìn)制對象等等,具體的類型由xmlhttprequest.responsetype屬性決定。xmlhttprequest.response屬性是只讀的。
如果本次請求沒有成功或者數(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); } }
xmlhttprequest.responsetype
xmlhttprequest.responsetype屬性是一個字符串,表示服務(wù)器返回數(shù)據(jù)的類型。這個屬性是可寫的,可以在調(diào)用open()方法之后、調(diào)用send()方法之前,設(shè)置這個屬性的值,告訴瀏覽器如何解讀返回的數(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ù)器返回一個文檔對象。
- "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ù),比如圖片文件。
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();
如果將這個屬性設(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 = uint8array.length; i < len; ++i) { // var byte = uint8array[i]; } }; xhr.send();
如果將這個屬性設(shè)為json,瀏覽器就會自動對返回數(shù)據(jù)調(diào)用json.parse()方法。也就是說,從xhr.response屬性(注意,不是xhr.responsetext屬性)得到的不是文本,而是一個 json 對象。
xmlhttprequest.responsetext
xmlhttprequest.responsetext屬性返回從服務(wù)器接收到的字符串,該屬性為只讀。只有 http 請求完成接收以后,該屬性才會包含完整的數(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);
xmlhttprequest.responsexml
xmlhttprequest.responsexml屬性返回從服務(wù)器接收到的 html 或 xml 文檔對象,該屬性為只讀。如果本次請求沒有成功,或者收到的數(shù)據(jù)不能被解析為 xml 或 html,該屬性等于null。
該屬性生效的前提是 http 回應(yīng)的content-type頭信息等于text/xml或application/xml。這要求在發(fā)送請求前,xmlhttprequest.responsetype屬性要設(shè)為document。如果 http 回應(yīng)的content-type頭信息不等于text/xml和application/xml,但是想從responsexml拿到數(shù)據(jù)(即把數(shù)據(jù)按照 dom 格式解析),那么需要手動調(diào)用xmlhttprequest.overridemimetype()方法,強制進(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);
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);
注意,這個屬性的值與open()方法指定的請求網(wǎng)址不一定相同。如果服務(wù)器端發(fā)生跳轉(zhuǎn),這個屬性返回最后實際返回數(shù)據(jù)的網(wǎng)址。另外,如果原始 url 包括錨點(fragment),該屬性會把錨點剝離。
xmlhttprequest.status,xmlhttprequest.statustext
xmlhttprequest.status屬性返回一個整數(shù),表示服務(wù)器回應(yīng)的 http 狀態(tài)碼。一般來說,如果通信成功的話,這個狀態(tài)碼是200;如果服務(wù)器沒有返回狀態(tài)碼,那么這個屬性默認(rèn)是200。請求發(fā)出之前,該屬性為0。該屬性只讀。
- 200, ok,訪問正常
- 301, moved permanently,永久移動
- 302, moved temporarily,暫時移動
- 304, not modified,未修改
- 307, temporary redirect,暫時重定向
- 401, unauthorized,未授權(quán)
- 403, forbidden,禁止訪問
- 404, not found,未發(fā)現(xiàn)指定網(wǎng)址
- 500, internal server error,服務(wù)器發(fā)生錯誤
基本上,只有2xx和304的狀態(tài)碼,表示服務(wù)器返回是正常狀態(tài)。
if (xhr.readystate === 4) { ??if ( (xhr.status >= 200 && xhr.status < 300) ????|| (xhr.status === 304) ) { ????// 處理服務(wù)器的返回數(shù)據(jù) ??} else { ????// 出錯 ??} }
xmlhttprequest.statustext屬性返回一個字符串,表示服務(wù)器發(fā)送的狀態(tài)提示。不同于status屬性,該屬性包含整個狀態(tài)信息,比如“ok”和“not found”。在請求發(fā)送之前(即調(diào)用open()方法之前),該屬性的值是空字符串;如果服務(wù)器沒有返回狀態(tài)提示,該屬性的值默認(rèn)為“ok”。該屬性為只讀屬性。
xmlhttprequest.timeout,xmlhttprequesteventtarget.ontimeout
xmlhttprequest.timeout屬性返回一個整數(shù),表示多少毫秒后,如果請求仍然沒有得到結(jié)果,就會自動終止。如果該屬性等于0,就表示沒有時間限制。
xmlhttprequesteventtarget.ontimeout屬性用于設(shè)置一個監(jiān)聽函數(shù),如果發(fā)生 timeout 事件,就會執(zhí)行這個監(jiān)聽函數(shù)。
下面是一個例子。
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 秒鐘超時 xhr.timeout = 10 * 1000; xhr.send(null);
事件監(jiān)聽屬性
xmlhttprequest 對象可以對以下事件指定監(jiān)聽函數(shù)。
- xmlhttprequest.onloadstart:loadstart 事件(http 請求發(fā)出)的監(jiān)聽函數(shù)
- xmlhttprequest.onprogress:progress事件(正在發(fā)送和加載數(shù)據(jù))的監(jiān)聽函數(shù)
- xmlhttprequest.onabort:abort 事件(請求中止,比如用戶調(diào)用了abort()方法)的監(jiān)聽函數(shù)
- xmlhttprequest.onerror:error 事件(請求失?。┑谋O(jiān)聽函數(shù)
- xmlhttprequest.onload:load 事件(請求成功完成)的監(jiān)聽函數(shù)
- xmlhttprequest.ontimeout:timeout 事件(用戶指定的時限超過了,請求還未完成)的監(jiān)聽函數(shù)
- xmlhttprequest.onloadend:loadend 事件(請求完成,不管成功或失?。┑谋O(jiān)聽函數(shù)
下面是一個例子。
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ù)有一個事件對象參數(shù),該對象有三個屬性:loaded屬性返回已經(jīng)傳輸?shù)臄?shù)據(jù)量,total屬性返回總的數(shù)據(jù)量,lengthcomputable屬性返回一個布爾值,表示加載的進(jìn)度是否可以計算。所有這些監(jiān)聽函數(shù)里面,只有progress事件的監(jiān)聽函數(shù)有參數(shù),其他函數(shù)都沒有參數(shù)。
注意,如果發(fā)生網(wǎng)絡(luò)錯誤(比如服務(wù)器無法連通),onerror事件無法獲取報錯信息。也就是說,可能沒有錯誤對象,所以這樣只能顯示報錯的提示。
xmlhttprequest.withcredentials
xmlhttprequest.withcredentials屬性是一個布爾值,表示跨域請求時,用戶信息(比如 cookie 和認(rèn)證的 http 頭信息)是否會包含在請求之中,默認(rèn)為false,即向example.com發(fā)出跨域請求時,不會發(fā)送example.com設(shè)置在本機上的 cookie(如果有的話)。
如果需要跨域 ajax 請求發(fā)送 cookie,需要withcredentials屬性設(shè)為true。注意,同源的請求不需要設(shè)置這個屬性。
var xhr = new xmlhttprequest(); xhr.open('get', 'http://example.com/', true); xhr.withcredentials = true; xhr.send(null);
為了讓這個屬性生效,服務(wù)器必須顯式返回access-control-allow-credentials這個頭信息。
access-control-allow-credentials: true
withcredentials屬性打開的話,跨域請求不僅會發(fā)送 cookie,還會設(shè)置遠(yuǎn)程主機指定的 cookie。反之也成立,如果withcredentials屬性沒有打開,那么跨域的 ajax 請求即使明確要求瀏覽器設(shè)置 cookie,瀏覽器也會忽略。
注意,腳本總是遵守同源政策,無法從document.cookie或者 http 回應(yīng)的頭信息之中,讀取跨域的 cookie,withcredentials屬性不影響這一點。
xmlhttprequest.upload
xmlhttprequest 不僅可以發(fā)送請求,還可以發(fā)送文件,這就是 ajax 文件上傳。發(fā)送文件以后,通過xmlhttprequest.upload屬性可以得到一個對象,通過觀察這個對象,可以得知上傳的進(jìn)展。主要方法就是監(jiān)聽這個對象的各種事件:loadstart、loadend、load、abort、error、progress、timeout。
假定網(wǎng)頁上有一個<progress>元素。
<progress min="0" max="100" value="0">0% complete</progress>
文件上傳時,對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'}));
xmlhttprequest 的實例方法
xmlhttprequest.open()
xmlhttprequest.open()方法用于指定 http 請求的參數(shù),或者說初始化 xmlhttprequest 實例對象。它一共可以接受五個參數(shù)。
void open( string method, string url, optional boolean async, optional string user, optional string password );
- method:表示 http 動詞方法,比如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 里面使用。所以,這個參數(shù)輕易不應(yīng)該設(shè)為false。
- user:表示用于認(rèn)證的用戶名,默認(rèn)為空字符串。該參數(shù)可選。
- password:表示用于認(rèn)證的密碼,默認(rèn)為空字符串。該參數(shù)可選。
注意,如果對使用過open()方法的 ajax 請求,再次使用這個方法,等同于調(diào)用abort(),即終止請求。
下面發(fā)送 post 請求的例子。
var xhr = new xmlhttprequest(); xhr.open('post', encodeuri('someurl'));
xmlhttprequest.send()
xmlhttprequest.send()方法用于實際發(fā)出 http 請求。它的參數(shù)是可選的,如果不帶參數(shù),就表示 http 請求只有一個 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);
上面代碼中,formdata對象構(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對象加工表單數(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);
xmlhttprequest.setrequestheader()
xmlhttprequest.setrequestheader()方法用于設(shè)置瀏覽器發(fā)送的 http 請求的頭信息。該方法必須在open()之后、send()之前調(diào)用。如果該方法多次調(diào)用,設(shè)定同一個字段,則每一次調(diào)用的值會被合并成一個單一的值發(fā)送。
該方法接受兩個參數(shù)。第一個參數(shù)是字符串,表示頭信息的字段名,第二個參數(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ù)長度;最后發(fā)送 json 數(shù)據(jù)。
xmlhttprequest.overridemimetype()
xmlhttprequest.overridemimetype()方法用來指定 mime 類型,覆蓋服務(wù)器返回的真正的 mime 類型,從而讓瀏覽器進(jìn)行不一樣的處理。舉例來說,服務(wù)器返回的數(shù)據(jù)類型是text/xml,由于種種原因瀏覽器解析不成功報錯,這時就拿不到數(shù)據(jù)了。為了拿到原始數(shù)據(jù),我們可以把 mime 類型改成text/plain,這樣瀏覽器就不會去自動解析,從而我們就可以拿到原始文本了。
xhr.overridemimetype('text/plain')
注意,該方法必須在send()方法之前調(diào)用。
修改服務(wù)器返回的數(shù)據(jù)類型,不是正常情況下應(yīng)該采取的方法。如果希望服務(wù)器返回指定的數(shù)據(jù)類型,可以用responsetype屬性告訴服務(wù)器,就像下面的例子。只有在服務(wù)器無法返回某種數(shù)據(jù)類型時,才使用overridemimetype()方法。
var xhr = new xmlhttprequest(); xhr.onload = function(e) { var arraybuffer = xhr.response; // ... } xhr.open('get', url); xhr.responsetype = 'arraybuffer'; xhr.send();
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();
如果有多個字段同名,它們的值會被連接為一個字符串,每個字段之間使用“逗號+空格”分隔。
xmlhttprequest.getallresponseheaders()
xmlhttprequest.getallresponseheaders()方法返回一個字符串,表示服務(wù)器發(fā)來的所有 http 頭信息。格式為字符串,每個頭信息之間使用crlf分隔(回車+換行),如果沒有收到服務(wù)器回應(yīng),該屬性為null。如果發(fā)生網(wǎng)絡(luò)錯誤,該屬性為空字符串。
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
然后,對這個字符串進(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"
xmlhttprequest.abort()
xmlhttprequest.abort()方法用來終止已經(jīng)發(fā)出的 http 請求。調(diào)用這個方法以后,readystate屬性變?yōu)?,status屬性變?yōu)?。
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秒之后,終止一個 ajax 請求。
xmlhttprequest 實例的事件
readystatechange 事件
readystate屬性的值發(fā)生改變,就會觸發(fā) readystatechange 事件。
我們可以通過onreadystatechange屬性,指定這個事件的監(jiān)聽函數(shù),對不同狀態(tài)進(jìn)行不同處理。尤其是當(dāng)狀態(tài)變?yōu)?的時候,表示通信成功,這時回調(diào)函數(shù)就可以處理服務(wù)器傳送回來的數(shù)據(jù)。
progress 事件
上傳文件時,xmlhttprequest 實例對象本身和實例的upload屬性,都有一個progress事件,會不斷返回上傳的進(jìn)度。
var xhr = new xmlhttprequest(); function updateprogress (oevent) { if (oevent.lengthcomputable) { var percentcomplete = oevent.loaded / oevent.total; } else { console.log('無法計算進(jìn)展'); } } xhr.addeventlistener('progress', updateprogress); xhr.open();
load 事件、error 事件、abort 事件
load 事件表示服務(wù)器傳來的數(shù)據(jù)接收完畢,error 事件表示請求出錯,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ù)接收出錯'); } function transfercanceled() { console.log('用戶取消接收'); }
loadend 事件
abort、load和error這三個事件,會伴隨一個loadend事件,表示請求結(jié)束,但不知道其是否成功。
xhr.addeventlistener('loadend', loadend); function loadend(e) { console.log('請求結(jié)束,狀態(tài)未知'); }
timeout 事件
服務(wù)器超過指定時間還沒有返回結(jié)果,就會觸發(fā) timeout 事件,具體的例子參見timeout屬性一節(jié)。
navigator.sendbeacon()
用戶卸載網(wǎng)頁的時候,有時需要向服務(wù)器發(fā)一些數(shù)據(jù)。很自然的做法是在unload事件或beforeunload事件的監(jiān)聽函數(shù)里面,使用xmlhttprequest對象發(fā)送數(shù)據(jù)。但是,這樣做不是很可靠,因為xmlhttprequest對象是異步發(fā)送,很可能在它即將發(fā)送的時候,頁面已經(jīng)卸載了,從而導(dǎo)致發(fā)送取消或者發(fā)送失敗。
解決方法就是unload事件里面,加一些很耗時的同步操作。這樣就能留出足夠的時間,保證異步 ajax 能夠發(fā)送成功。
function log() { let xhr = new xmlhttprequest(); xhr.open('post', '/log', true); xhr.setrequestheader('content-type', 'application/x-www-form-urlencoded'); xhr.send('foo=bar'); } window.addeventlistener('unload', function(event) { log(); // a time-consuming operation for (let i = 1; i < 10000; i++) { for (let m = 1; m < 10000; m++) { continue; } } });
上面代碼中,強制執(zhí)行了一次雙重循環(huán),拖長了unload事件的執(zhí)行時間,導(dǎo)致異步 ajax 能夠發(fā)送成功。
類似的還可以使用settimeout。下面是追蹤用戶點擊的例子。
// html 代碼如下 // <a id="target" >click</a> const clicktime = 350; const thelink = document.getelementbyid('target'); function log() { let xhr = new xmlhttprequest(); xhr.open('post', '/log', true); xhr.setrequestheader('content-type', 'application/x-www-form-urlencoded'); xhr.send('foo=bar'); } thelink.addeventlistener('click', function (event) { event.preventdefault(); log(); settimeout(function () { window.location.href = thelink.getattribute('href'); }, clicktime); });
上面代碼使用settimeout,拖延了350毫秒,才讓頁面跳轉(zhuǎn),因此使得異步 ajax 有時間發(fā)出。
這些做法的共同問題是,卸載的時間被硬生生拖長了,后面頁面的加載被推遲了,用戶體驗不好。
為了解決這個問題,瀏覽器引入了navigator.sendbeacon()方法。這個方法還是異步發(fā)出請求,但是請求與當(dāng)前頁面線程脫鉤,作為瀏覽器進(jìn)程的任務(wù),因此可以保證會把數(shù)據(jù)發(fā)出去,不拖延卸載流程。
window.addeventlistener('unload', logdata, false); function logdata() { navigator.sendbeacon('/log', analyticsdata); }
navigator.sendbeacon方法接受兩個參數(shù),第一個參數(shù)是目標(biāo)服務(wù)器的 url,第二個參數(shù)是所要發(fā)送的數(shù)據(jù)(可選),可以是任意類型(字符串、表單對象、二進(jìn)制對象等等)。
navigator.sendbeacon(url, data)
這個方法的返回值是一個布爾值,成功發(fā)送數(shù)據(jù)為true,否則為false。
該方法發(fā)送數(shù)據(jù)的 http 方法是 post,可以跨域,類似于表單提交數(shù)據(jù)。它不能指定回調(diào)函數(shù)。
下面是一個例子。
// html 代碼如下 // <body onload="analytics('start')" onunload="analytics('end')"> function analytics(state) { if (!navigator.sendbeacon) return; var url = 'http://example.com/analytics'; var data = 'state=' + state + '&location=' + window.location; navigator.sendbeacon(url, data); }
總結(jié)
到此這篇關(guān)于面試必備之a(chǎn)jax原始請求的文章就介紹到這了,更多相關(guān)ajax原始請求內(nèi)容請搜索碩編程以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持碩編程!