提及ajax,你們都不陌生。可是因爲目前不少框架或者庫等都對網絡請求作了封裝,致使了不少初學者只知其然而不知其因此然。因此今天咱們就詳細瞭解一下ajax的實現原理和封裝ajax的關鍵步驟。javascript
ajax的核心是XMLHttpRequest對象。首先咱們先建立一個XMLHTTPRequest對象 var xhr = new XMLHttpRequest();
。java
注意:本文所說起的內容不兼容古老的IE,有想了解的同窗自行查閱ActiveXObject相關內容。
在使用XMLHttpRequest對象的第一步,咱們首先要調用open方法來初始化請求參數,xhr.open('get','/test',true)
,雖然名字叫open,可是此時請求還並無發送。ajax
open(method, url[, async, username, password])segmentfault
若是調用了open方法後再次對它進行調用,則至關於調用了abort方法,abort方法咱們在後面介紹。
若是咱們想爲爲請求綁定一些操做,這個時候就能夠開始啦。經常使用的操做有以下幾個:跨域
setRequestHeader(key, value)瀏覽器
顧名思義,這個方法用於設置請求頭內容。服務器
overrideMimeType(type)cookie
重寫服務器返回的MIME類型。經過這個方法能夠告訴服務器你想要的數據類型。網絡
注意:以上這些操做必須定義在send方法以前。不然,就拿setRequestHeader來講,你都把請求發出去了再設置還有什麼用?
這個時候,咱們就能夠經過調用send 方法來發送請求了,xhr.send(null)
。app
send(data)
發送請求,若是是同步請求的話,會阻塞代碼的執行,直至收到服務器響應纔會繼續。
readyStateChanhe()
每次readyState的值改變的時候都會觸發這個函數。
getResponseHeader(name)
獲取指定響應頭部的值,參數是響應頭部的名稱,而且不區分大小寫。
getAllResponseHeaders()
獲取服務器發送的全部HTTP響應的頭部。
在這裏咱們穿插幾個概念,readyState,這個屬性代表了請求的狀態,伴隨HTTP請求的整個生命週期,它的值代表此時請求所處的階段,具體以下:
readyState
數值 | 描述 |
---|---|
0 | 初始化,open()還沒有調用 |
1 | open()已經調用,可是send未調用 |
2 | 已獲取到返回頭信息 |
3 | 正在下載返回體信息 |
4 | 請求完成 |
還有幾個較爲經常使用的屬性
名稱 | 含義 |
---|---|
responseText | 響應的文本 |
status | 響應的狀態碼 |
statusText | 響應的狀態信息 |
responseXML | 響應內容是「text/xml」或者是「application/xml」格式的時候,這個屬性的值就是響應數據的XMLDOM文檔。 |
咱們用下面這段代碼作個測試
var xhr = new XMLHttpRequest(); console.log(xhr.readyState) xhr.onreadystatechange = function(){ console.log('------') console.log('readyState:' + xhr.readyState) console.log('ResponseHeaders:' + xhr.getAllResponseHeaders()) console.log('ResponseText:' + xhr.responseText.length) console.log('------') } xhr.open('get','/') xhr.send(null)
下圖咱們能夠直觀的看到在建立了XMLHttpRequest對象的時候,readyState的值爲0。
而後咱們定義了onreadystatechange函數,讓其打印一些屬性,並調用open方法,此時readyState變爲1。
最後咱們調用send方法,能夠看到經歷了以下過程:
abort()
取消響應,調用這個方法會終止已發送的請求。咱們嘗試在以前的代碼最後加一句。
xhr.abort(); console.log(xhr.readyState);
也就是說,send執行之後,並無去嘗試請求數據,而是直接取消掉了,而且咱們發現abort會將readyState的值置爲0。
除此以外,XMLHttpRequest還有一個很重要的屬性withCredentials,cookie在同域請求的時候,會被自動攜帶在請求頭中,可是跨域請求則不會,除非把withCredentials的值設爲true(默認爲false)。同時須要在服務端的響應頭部中設置Access-Control-Allow-Credentials:true。不只如此Access-Control-Allow-Origin的值也必須爲當前頁面的域名。
到此爲止,咱們終於講完了XMLHttpRequest的一些經常使用概念。接下來,咱們嘗試本身封裝一個支持get和post的簡易jax請求。
function ajax(url, option){ option = option || {}; var method = (option.method || 'GET').toUpperCase(), async = option.async === undefined ? true : option.async, params = handleParams(option.data); var xhr = new XMLHttpRequest(); if(async){ xhr.onreadystatechange = function () { if (xhr.readyState == 4){ callback(option,xhr); } }; } if (method === 'GET'){ xhr.open("GET",url + '?' + params, async); xhr.send(null) }else if (method === 'POST'){ xhr.open('POST', url, async); xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded'); xhr.send(params); } if(!async){ callback(option,xhr); } function callback(opt,obj){ var status = obj.status; if (status >= 200 && status < 300 ){ opt.success && opt.success(obj.responseText,obj.responseXML); }else{ opt.fail && opt.fail(status); } } function handleParams(data) { var arr = []; for (var i in data) { arr.push(encodeURIComponent(i) + '=' + encodeURIComponent(data[i])); } return arr.join('&'); } } // 測試調用 ajax('/xxx',{ method:'POST', data:{ key: 'test' }, success:function(){ console.log('success') }, fail:function(){ console.log('fail') } });
其實ajax實現原理並不複雜,複雜的是容錯、兼容性等的處理,使用的時候儘可能使用庫或者框架提供的封裝,避免沒必要要的漏洞。
感謝@蝦嗶嗶的提問,這裏作個簡單的補充說明。
async是一個可選的布爾值參數,默認爲true,意味着是否執行異步操做,若是值爲false,則send()方法不會返回任何東西,直到接受到了服務器的返回數據。若是爲值爲true,一個對開發者透明的通知會發送到相關的事件監聽者。這個值必須是true,若是multipart 屬性是true,不然將會出現一個意外。
根據個人分析,當async爲false的時候,readyState會在send方法以後直接由1變成4。也就是說異步模式,send方法會馬上返回。同步模式下,只有響應徹底接受後,send纔會返回。
另外,因爲同步模式會阻塞,較新版本的Chrome在主線程上的同步請求已被棄用。