摘要:導(dǎo)讀全稱即異步與它最早在中被使用然后由推廣開來典型的代表應(yīng)用有以及現(xiàn)代網(wǎng)頁中幾乎無不歡前后端分離也正是建立在異步通信的基礎(chǔ)之上瀏覽器為做了什么現(xiàn)代瀏覽器中雖然幾乎全部支持但它們的技術(shù)方案卻分為兩種標(biāo)準(zhǔn)瀏覽器通過對象實(shí)現(xiàn)了的功能只需要通過一行
導(dǎo)讀
Ajax 全稱 Asynchronous JavaScript and XML, 即異步JS與XML. 它最早在IE5中被使用, 然后由Mozilla, Apple, Google推廣開來. 典型的代表應(yīng)用有 Outlook Web Access, 以及 GMail. 現(xiàn)代網(wǎng)頁中幾乎無ajax不歡. 前后端分離也正是建立在ajax異步通信的基礎(chǔ)之上.
瀏覽器為ajax做了什么現(xiàn)代瀏覽器中, 雖然幾乎全部支持ajax, 但它們的技術(shù)方案卻分為兩種:
① 標(biāo)準(zhǔn)瀏覽器通過 XMLHttpRequest 對象實(shí)現(xiàn)了ajax的功能. 只需要通過一行語句便可創(chuàng)建一個用于發(fā)送ajax請求的對象.
var xhr = new XMLHttpRequest();
② IE瀏覽器通過 XMLHttpRequest 或者 ActiveXObject 對象同樣實(shí)現(xiàn)了ajax的功能.
MSXML鑒于IE系列各種 "神級" 表現(xiàn), 我們先來看看IE瀏覽器風(fēng)騷的走位.
IE下的使用環(huán)境略顯復(fù)雜, IE7及更高版本瀏覽器可以直接使用BOM的 XMLHttpRequest 對象. MSDN傳送門: Native XMLHTTPRequest object. IE6及更低版本瀏覽器只能使用 ActiveXObject 對象來創(chuàng)建 XMLHttpRequest 對象實(shí)例. 創(chuàng)建時需要指明一個類似"Microsoft.XMLHTTP"這樣的ProgID. 而實(shí)際呢, windows系統(tǒng)環(huán)境下, 以下ProgID都應(yīng)該可以創(chuàng)建XMLHTTP對象:
Microsoft.XMLHTTP Microsoft.XMLHTTP.1.0 Msxml2.ServerXMLHTTP Msxml2.ServerXMLHTTP.3.0 Msxml2.ServerXMLHTTP.4.0 Msxml2.ServerXMLHTTP.5.0 Msxml2.ServerXMLHTTP.6.0 Msxml2.XMLHTTP Msxml2.XMLHTTP.3.0 Msxml2.XMLHTTP.4.0 Msxml2.XMLHTTP.5.0 Msxml2.XMLHTTP.6.0
簡言之, Microsoft.XMLHTTP 已經(jīng)非常老了, 主要用于提供對歷史遺留版本的支持, 不建議使用.對于 MSXML4, 它已被 MSXML6 替代; 而 MSXML5 又是專門針對office辦公場景, 在沒有安裝 Microsoft Office 2003 及更高版本辦公軟件的情況下, MSXML5 未必可用. 相比之下, MSXML6 具有比 MSXML3 更穩(wěn)定, 更高性能, 更安全的優(yōu)勢, 同時它也提供了一些 MSXML3 中沒有的功能, 比如說 XSD schema. 唯一遺憾的是, MSXML6 只在 vista 系統(tǒng)及以上才是默認(rèn)支持的; 而 MSXML3 在 Win2k SP4及以上系統(tǒng)就是可用的. 因此一般情況下, MSXML3 可以作為 MSXML6 的優(yōu)雅降級方案, 我們通過指定 PorgID 為 Msxml2.XMLHTTP 即可自動映射到 Msxml2.XMLHTTP.3.0. 如下所示:
var xhr = new ActiveXObject("Msxml2.XMLHTTP");// 即MSXML3,等同于如下語句 var xhr = new ActiveXObject("MSXML2.XMLHTTP.3.0");
MSDN有篇文章專門講解了各個版本的MSXML. 傳送門: Using the right version of MSXML in Internet Explorer.
親測了 IE5, IE5.5, IE6, IE7, IE8, IE9, IE10, IE edge等瀏覽器, IE5及之后的瀏覽器均可以通過如下語句獲取xhr對象:
var xhr = new ActiveXObject("Msxml2.XMLHTTP");// 即MSXML3 var xhr = new ActiveXObject("Microsoft.XMLHTTP");// 很老的api,雖然瀏覽器支持,功能可能不完善,故不建議使用
以上, 思路已經(jīng)很清晰了, 下面給出個全兼容的方法.
全平臺兼容的XMLHttpRequest對象function getXHR(){ var xhr = null; if(window.XMLHttpRequest) { xhr = new XMLHttpRequest(); } else if (window.ActiveXObject) { try { xhr = new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) { try { xhr = new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) { alert("您的瀏覽器暫不支持Ajax!"); } } } return xhr; }ajax有沒有破壞js單線程機(jī)制
對于這個問題, 我們先看下瀏覽器線程機(jī)制. 一般情況下, 瀏覽器有如下四種線程:
GUI渲染線程
javascript引擎線程
瀏覽器事件觸發(fā)線程
HTTP請求線程
那么這么多線程, 它們究竟是怎么同js引擎線程交互的呢?
通常, 它們的線程間交互以事件的方式發(fā)生, 通過事件回調(diào)的方式予以通知. 而事件回調(diào), 又是以先進(jìn)先出的方式添加到任務(wù)隊(duì)列 的末尾 , 等到j(luò)s引擎空閑時, 任務(wù)隊(duì)列 中排隊(duì)的任務(wù)將會依次被執(zhí)行. 這些事件回調(diào)包括 setTimeout, setInterval, click, ajax異步請求等回調(diào).
瀏覽器中, js引擎線程會循環(huán)從 任務(wù)隊(duì)列 中讀取事件并且執(zhí)行, 這種運(yùn)行機(jī)制稱作 Event Loop (事件循環(huán)).
對于一個ajax請求, js引擎首先生成 XMLHttpRequest 實(shí)例對象, open過后再調(diào)用send方法. 至此, 所有的語句都是同步執(zhí)行. 但從send方法內(nèi)部開始, 瀏覽器為將要發(fā)生的網(wǎng)絡(luò)請求創(chuàng)建了新的http請求線程, 這個線程獨(dú)立于js引擎線程, 于是網(wǎng)絡(luò)請求異步被發(fā)送出去了. 另一方面, js引擎并不會等待 ajax 發(fā)起的http請求收到結(jié)果, 而是直接順序往下執(zhí)行.
當(dāng)ajax請求被服務(wù)器響應(yīng)并且收到response后, 瀏覽器事件觸發(fā)線程捕獲到了ajax的回調(diào)事件 onreadystatechange (當(dāng)然也可能觸發(fā)onload, 或者 onerror等等) . 該回調(diào)事件并沒有被立即執(zhí)行, 而是被添加到 任務(wù)隊(duì)列 的末尾. 直到j(luò)s引擎空閑了, 任務(wù)隊(duì)列 的任務(wù)才被撈出來, 按照添加順序, 挨個執(zhí)行, 當(dāng)然也包括剛剛append到隊(duì)列末尾的 onreadystatechange 事件.
在 onreadystatechange 事件內(nèi)部, 有可能對dom進(jìn)行操作. 此時瀏覽器便會掛起js引擎線程, 轉(zhuǎn)而執(zhí)行GUI渲染線程, 進(jìn)行UI重繪(repaint)或者回流(reflow). 當(dāng)js引擎重新執(zhí)行時, GUI渲染線程又會被掛起, GUI更新將被保存起來, 等到j(luò)s引擎空閑時立即被執(zhí)行.
以上整個ajax請求過程中, 有涉及到瀏覽器的4種線程. 其中除了 GUI渲染線程 和 js引擎線程 是互斥的. 其他線程相互之間, 都是可以并行執(zhí)行的. 通過這樣的一種方式, ajax并沒有破壞js的單線程機(jī)制.
ajax與setTimeout排隊(duì)問題通常, ajax 和 setTimeout 的事件回調(diào)都被同等的對待, 按照順序自動的被添加到 任務(wù)隊(duì)列 的末尾, 等待js引擎空閑時執(zhí)行. 但請注意, 并非xhr的所有回調(diào)執(zhí)行都滯后于setTImeout的回調(diào). 請看如下代碼:
function ajax(url, method){ var xhr = getXHR(); xhr.onreadystatechange = function(){ console.log("xhr.readyState:" + this.readyState); } xhr.onloadstart = function(){ console.log("onloadStart"); } xhr.onload = function(){ console.log("onload"); } xhr.open(method, url, true); xhr.setRequestHeader("Cache-Control",3600); xhr.send(); } var timer = setTimeout(function(){ console.log("setTimeout"); },0); ajax("http://louiszhai.github.io/docImages/ajax01.png","GET");
上述代碼執(zhí)行結(jié)果如下圖:
由于ajax異步, setTimeout回調(diào)本應(yīng)該最先被執(zhí)行, 然而實(shí)際上, 一次ajax請求, 并非所有的部分都是異步的, 至少"readyState==1"的 onreadystatechange 回調(diào)以及 onloadstart 回調(diào)就是同步執(zhí)行的. 因此它們的輸出排在最前面.
XMLHttpRequest 屬性解讀首先在Chrome console下創(chuàng)建一個 XMLHttpRequest 實(shí)例對象xhr. 如下所示:
inherit試運(yùn)行以下代碼.
var xhr = new XMLHttpRequest(), i=0; for(var key in xhr){ if(xhr.hasOwnProperty(key)){ i++; } } console.log(i);//0 console.log(XMLHttpRequest.prototype.hasOwnProperty("timeout"));//true
可見, XMLHttpRequest 實(shí)例對象沒有自有屬性. 實(shí)際上, 它的所有屬性均來自于 XMLHttpRequest.prototype .
追根溯源, XMLHttpRequest 實(shí)例對象具有如下的繼承關(guān)系. (下面以a<
xhr << XMLHttpRequest.prototype << XMLHttpRequestEventTarget.prototype << EventTarget.prototype << Object.prototype
由上, xhr也具有Object等原型中的所有方法. 如toString方法.
xhr.toString();//"[object XMLHttpRequest]"
通常, 一個xhr實(shí)例對象擁有10個普通屬性+9個方法.
readyState只讀屬性, readyState屬性記錄了ajax調(diào)用過程中所有可能的狀態(tài). 它的取值簡單明了, 如下:
readyState | 對應(yīng)常量 | 描述 |
---|---|---|
0 (未初始化) | xhr.UNSENT | 請求已建立, 但未初始化(此時未調(diào)用open方法) |
1 (初始化) | xhr.OPENED | 請求已建立, 但未發(fā)送 (已調(diào)用open方法, 但未調(diào)用send方法) |
2 (發(fā)送數(shù)據(jù)) | xhr.HEADERS_RECEIVED | 請求已發(fā)送 (send方法已調(diào)用, 已收到響應(yīng)頭) |
3 (數(shù)據(jù)傳送中) | xhr.LOADING | 請求處理中, 因響應(yīng)內(nèi)容不全, 這時通過responseBody和responseText獲取可能會出現(xiàn)錯誤 |
4 (完成) | xhr.DONE | 數(shù)據(jù)接收完畢, 此時可以通過通過responseBody和responseText獲取完整的響應(yīng)數(shù)據(jù) |
注意, readyState 是一個只讀屬性, 想要改變它的值是不可行的.
onreadystatechangeonreadystatechange事件回調(diào)方法在readystate狀態(tài)改變時觸發(fā), 在一個收到響應(yīng)的ajax請求周期中, onreadystatechange 方法會被觸發(fā)4次. 因此可以在 onreadystatechange 方法中綁定一些事件回調(diào), 比如:
xhr.onreadystatechange = function(e){ if(xhr.readystate==4){ var s = xhr.status; if((s >= 200 && s < 300) || s == 304){ var resp = xhr.responseText; //TODO ... } } }
注意: onreadystatechange回調(diào)中默認(rèn)會傳入Event實(shí)例, 如下:
status只讀屬性, status表示http請求的狀態(tài), 初始值為0. 如果服務(wù)器沒有顯式地指定狀態(tài)碼, 那么status將被設(shè)置為默認(rèn)值, 即200.
statusText只讀屬性, statusText表示服務(wù)器的響應(yīng)狀態(tài)信息, 它是一個 UTF-16 的字符串, 請求成功且status==20X時, 返回大寫的 OK . 請求失敗時返回空字符串. 其他情況下返回相應(yīng)的狀態(tài)描述. 比如: 301的 Moved Permanently , 302的 Found , 303的 See Other , 307 的 Temporary Redirect , 400的 Bad Request , 401的 Unauthorized 等等.
onloadstartonloadstart事件回調(diào)方法在ajax請求發(fā)送之前觸發(fā), 觸發(fā)時機(jī)在 readyState==1 狀態(tài)之后, readyState==2 狀態(tài)之前.
onloadstart方法中默認(rèn)將傳入一個ProgressEvent事件進(jìn)度對象. 如下:
ProgressEvent對象具有三個重要的Read only屬性.
lengthComputable 表示長度是否可計算, 它是一個布爾值, 初始值為false.
loaded 表示已加載資源的大小, 如果使用http下載資源, 它僅僅表示已下載內(nèi)容的大小, 而不包括http headers等. 它是一個無符號長整型, 初始值為0.
total 表示資源總大小, 如果使用http下載資源, 它僅僅表示內(nèi)容的總大小, 而不包括http headers等, 它同樣是一個無符號長整型, 初始值為0.
onprogressonprogress事件回調(diào)方法在 readyState==3 狀態(tài)時開始觸發(fā), 默認(rèn)傳入 ProgressEvent 對象, 可通過 e.loaded/e.total 來計算加載資源的進(jìn)度, 該方法用于獲取資源的下載進(jìn)度.
注意: 該方法適用于 IE10+ 及其他現(xiàn)代瀏覽器.
xhr.onprogress = function(e){ console.log("progress:", e.loaded/e.total); }onload
onload事件回調(diào)方法在ajax請求成功后觸發(fā), 觸發(fā)時機(jī)在 readyState==4 狀態(tài)之后.
想要捕捉到一個ajax異步請求的成功狀態(tài), 并且執(zhí)行回調(diào), 一般下面的語句就足夠了:
xhr.onload = function(){ var s = xhr.status; if((s >= 200 && s < 300) || s == 304){ var resp = xhr.responseText; //TODO ... } }onloadend
onloadend事件回調(diào)方法在ajax請求完成后觸發(fā), 觸發(fā)時機(jī)在 readyState==4 狀態(tài)之后(收到響應(yīng)時) 或者 readyState==2 狀態(tài)之后(未收到響應(yīng)時).
onloadend方法中默認(rèn)將傳入一個ProgressEvent事件進(jìn)度對象.
timeouttimeout屬性用于指定ajax的超時時長. 通過它可以靈活地控制ajax請求時間的上限. timeout的值滿足如下規(guī)則:
通常設(shè)置為0時不生效.
設(shè)置為字符串時, 如果字符串中全部為數(shù)字, 它會自動將字符串轉(zhuǎn)化為數(shù)字, 反之該設(shè)置不生效.
設(shè)置為對象時, 如果該對象能夠轉(zhuǎn)化為數(shù)字, 那么將設(shè)置為轉(zhuǎn)化后的數(shù)字.
xhr.timeout = 0; //不生效 xhr.timeout = "123"; //生效, 值為123 xhr.timeout = "123s"; //不生效 xhr.timeout = ["123"]; //生效, 值為123 xhr.timeout = {a:123}; //不生效ontimeout
ontimeout方法在ajax請求超時時觸發(fā), 通過它可以在ajax請求超時時做一些后續(xù)處理.
xhr.ontimeout = function(e) { console.error("請求超時!!!") }response responseText
均為只讀屬性, response表示服務(wù)器的響應(yīng)內(nèi)容, 相應(yīng)的, responseText表示服務(wù)器響應(yīng)內(nèi)容的文本形式.
responseXML只讀屬性, responseXML表示xml形式的響應(yīng)數(shù)據(jù), 缺省為null, 若數(shù)據(jù)不是有效的xml, 則會報錯.
responseTyperesponseType表示響應(yīng)的類型, 缺省為空字符串, 可取 "arraybuffer" , "blob" , "document" , "json" , and "text" 共五種類型.
responseURLresponseURL返回ajax請求最終的URL, 如果請求中存在重定向, 那么responseURL表示重定向之后的URL.
withCredentialswithCredentials是一個布爾值, 默認(rèn)為false, 表示跨域請求中不發(fā)送cookies等信息. 當(dāng)它設(shè)置為true時, cookies , authorization headers 或者 TLS客戶端證書 都可以正常發(fā)送和接收. 顯然它的值對同域請求沒有影響.
注意: 該屬性適用于 IE10+, opera12+及其他現(xiàn)代瀏覽器.
abortabort方法用于取消ajax請求, 取消后, readyState 狀態(tài)將被設(shè)置為?0?(UNSENT). 如下, 調(diào)用abort 方法后, 請求將被取消.
getResponseHeadergetResponseHeader方法用于獲取ajax響應(yīng)頭中指定name的值. 如果response headers中存在相同的name, 那么它們的值將自動以字符串的形式連接在一起.
console.log(xhr.getResponseHeader("Content-Type"));//"text/html"getAllResponseHeaders
getAllResponseHeaders方法用于獲取所有安全的ajax響應(yīng)頭, 響應(yīng)頭以字符串形式返回. 每個HTTP報頭名稱和值用冒號分隔, 如key:value, 并以rn結(jié)束.
xhr.onreadystatechange = function() { if(this.readyState == this.HEADERS_RECEIVED) { console.log(this.getAllResponseHeaders()); } } //Content-Type: text/html"
以上, readyState === 2 狀態(tài)時, 就意味著響應(yīng)頭已接受完整. 此時便可以打印出完整的 response headers.
setRequestHeader既然可以獲取響應(yīng)頭, 那么自然也可以設(shè)置請求頭, setRequestHeader就是干這個的. 如下:
//指定請求的type為json格式 xhr.setRequestHeader("Content-type", "application/json"); //除此之外, 還可以設(shè)置其他的請求頭 xhr.setRequestHeader("x-requested-with", "123456");onerror
onerror方法用于在ajax請求出錯后執(zhí)行. 通常只在網(wǎng)絡(luò)出現(xiàn)問題時或者ERR_CONNECTION_RESET時觸發(fā)(如果請求返回的是407狀態(tài)碼, chrome下也會觸發(fā)onerror).
uploadupload屬性默認(rèn)返回一個 XMLHttpRequestUpload 對象, 用于上傳資源. 該對象具有如下方法:
onloadstart
onprogress
onabort
onerror
onload
ontimeout
onloadend
上述方法功能同 xhr 對象中同名方法一致. 其中, onprogress 事件回調(diào)方法可用于跟蹤資源上傳的進(jìn)度.
xhr.upload.onprogress = function(e){ var percent = 100 * e.loaded / e.total |0; console.log("upload: " + precent + "%"); }overrideMimeType
overrideMimeType方法用于強(qiáng)制指定response 的 MIME 類型, 即強(qiáng)制修改response的 Content-Type . 如下, 服務(wù)器返回的response的 MIME 類型為 text/plain .
xhr.getResponseHeader("Content-Type");//"text/plain" xhr.responseXML;//null
通過overrideMimeType方法將response的MIME類型設(shè)置為 text/xml;charset=utf-8 , 如下所示:
xhr.overrideMimeType("text/xml; charset = utf-8"); xhr.send();
此時雖然 response headers 如上圖, 沒有變化, 但 Content-Type 已替換為新值.
xhr.getResponseHeader("Content-Type");//"text/xml; charset = utf-8"
此時, xhr.responseXML 也將返回DOM對象, 如下圖.
XHR一級XHR1 即 XMLHttpRequest Level 1. XHR1時, xhr對象具有如下缺點(diǎn):
僅支持文本數(shù)據(jù)傳輸, 無法傳輸二進(jìn)制數(shù)據(jù).
傳輸數(shù)據(jù)時, 沒有進(jìn)度信息提示, 只能提示是否完成.
受瀏覽器 同源策略 限制, 只能請求同域資源.
沒有超時機(jī)制, 不方便掌控ajax請求節(jié)奏.
XHR二級XHR2 即 XMLHttpRequest Level 2. XHR2針對XHR1的上述缺點(diǎn)做了如下改進(jìn):
支持二進(jìn)制數(shù)據(jù), 可以上傳文件, 可以使用FormData對象管理表單.
提供進(jìn)度提示, 可通過 xhr.upload.onprogress 事件回調(diào)方法獲取傳輸進(jìn)度.
依然受 同源策略 限制, 這個安全機(jī)制不會變. XHR2新提供 Access-Control-Allow-Origin 等headers, 設(shè)置為 * 時表示允許任何域名請求, 從而實(shí)現(xiàn)跨域CORS訪問(有關(guān)CORS詳細(xì)介紹請耐心往下讀).
可以設(shè)置timeout 及 ontimeout, 方便設(shè)置超時時長和超時后續(xù)處理.
這里就H5新增的FormData對象舉個例.
//可直接創(chuàng)建FormData實(shí)例 var data = new FormData(); data.append("name", "louis"); xhr.send(data); //還可以通過傳入表單DOM對象來創(chuàng)建FormData實(shí)例 var form = document.getElementById("form"); var data = new FormData(form); data.append("password", "123456"); xhr.send(data);
目前, 主流瀏覽器基本上都支持XHR2, 除了IE系列需要IE10及更高版本. 因此IE10以下是不支持XHR2的.
那么問題來了, IE7, 8,9的用戶怎么辦? 很遺憾, 這些用戶是比較尷尬的. 對于IE8,9而言, 只有一個閹割版的 XDomainRequest 可用,IE7則沒有. 估計IE7用戶只能哭暈在廁所了.
XDomainRequestXDomainRequest 對象是IE8,9折騰出來的, 用于支持CORS請求非成熟的解決方案. 以至于IE10中直接移除了它, 并重新回到了 XMLHttpRequest 的懷抱.
XDomainRequest 僅可用于發(fā)送 GET 和 POST 請求. 如下即創(chuàng)建過程.
var xdr = new XDomainRequest();
xdr具有如下屬性:
timeout
responseText
如下方法:
open: 只能接收Method,和url兩個參數(shù). 只能發(fā)送異步請求.
send
abort
如下事件回調(diào):
onprogress
ontimeout
onerror
onload
除了缺少一些方法外, XDomainRequest 基本上就和 XMLHttpRequest 的使用方式保持一致.
必須要明確的是:
XDomainRequest 不支持跨域傳輸cookie.
只能設(shè)置請求頭的Content-Type字段, 且不能訪問響應(yīng)頭信息.
$.ajax$.ajax是jquery對原生ajax的一次封裝. 通過封裝ajax, jquery抹平了不同版本瀏覽器異步http的差異性, 取而代之的是高度統(tǒng)一的api. jquery作為js類庫時代的先驅(qū), 對前端發(fā)展有著深遠(yuǎn)的影響. 了解并熟悉其ajax方法, 不可謂不重要.
參數(shù)列表$.ajax() 只有一個參數(shù), 該參數(shù)為key-value設(shè)置對象. 實(shí)際上, jq發(fā)送的所有ajax請求, 都是通過調(diào)用該ajax方法實(shí)現(xiàn)的. 它的詳細(xì)參數(shù)如下表:
序號 | 參數(shù) | 類型 | 描述 |
---|---|---|---|
1 | accepts | PlainObject | 用于通知服務(wù)器該請求需要接收何種類型的返回結(jié)果. 如有必要, 推薦在 $.ajaxSetup()?方法中設(shè)置一次. |
2 | async | Boolean | 默認(rèn)為true, 即異步. |
3 | beforeSend | Function | 請求發(fā)送前的回調(diào), 默認(rèn)傳入?yún)?shù)jqXHR和settings. 函數(shù)內(nèi)顯式返回false將取消本次請求. |
4 | cache | Boolean | 請求是否開啟緩存, 默認(rèn)為true, 如不需要緩存請設(shè)置為false. 不過, dataType為"script"和"jsonp"時默認(rèn)為false. |
5 | complete | Function | 請求完成后的回調(diào)(請求success?和?error之后均調(diào)用), 默認(rèn)傳入?yún)?shù)jqXHR和textStatus(請求狀態(tài), 取值為 "success","notmodified","error","timeout","abort","parsererror"之一). 從jq1.5開始, complete可以設(shè)置為一個包含函數(shù)的數(shù)組. 如此每個函數(shù)將依次被調(diào)用. |
6 | contents | PlainObject | 一個以"{字符串/正則表達(dá)式}"配對的對象, 根據(jù)給定的內(nèi)容類型, 解析請求的返回結(jié)果. |
7 | contentType | String | 編碼類型, 相對應(yīng)于http請求頭域的"Content-Type"字段. 默認(rèn)值為"application/x-www-form-urlencoded; charset=UTF-8". |
8 | context | Object | 設(shè)置ajax回調(diào)函數(shù)的上下文. 默認(rèn)上下文為ajax請求傳入的參數(shù)設(shè)置對象. 如設(shè)置為document.body, 那么所有ajax回調(diào)函數(shù)中將以body為上下文. |
9 | converters | PlainObject | 一個數(shù)據(jù)類型到數(shù)據(jù)類型轉(zhuǎn)換器的對象. 默認(rèn)為 {"* text": window.String, "text html": true, "text json": jQuery.parseJSON, "text xml": jQuery.parseXML} . 如設(shè)置converters:{"json jsonp": function(msg){}} |
10 | crossDomain | Boolean | 默認(rèn)同域請求為false, 跨域請求為true. |
11 | data | Object, Array | 發(fā)送到服務(wù)器的數(shù)據(jù), 默認(rèn)data為鍵值對格式對象, 若data為數(shù)組則按照traditional參數(shù)的值, 自動轉(zhuǎn)化為一個同名的多值查詢字符串. 如{a:1,b:2}將轉(zhuǎn)換為"&a=1&b=2". |
12 | dataFilter | Function | 處理XMLHttpRequest原始響應(yīng)數(shù)據(jù)的回調(diào), 默認(rèn)傳入data和type參數(shù), data是Ajax返回的原始數(shù)據(jù), type是調(diào)用$.ajax時提供的dataType參數(shù) |
13 | dataType | String | 預(yù)期服務(wù)器返回的數(shù)據(jù)類型, 可設(shè)置為"xml","html","script","json","jsonp","text"之一, 其中設(shè)置為"xml"或"text"類型時, 數(shù)據(jù)不會經(jīng)過處理. |
14 | error | Function | 請求失敗時的回調(diào)函數(shù), 默認(rèn)傳入jqXHR(jq1.4以前為原生xhr對象),textStatus(請求狀態(tài),取值為null,"timeout","error","abort" 或 "parsererror"),errorString(錯誤內(nèi)容), 當(dāng)一個HTTP錯誤發(fā)生時, errorThrown?接收HTTP狀態(tài)的文本部分,比如"Not Found"等. 從jq1.5開始, error可以設(shè)置為一個包含函數(shù)的數(shù)組. 如此每個函數(shù)將依次被調(diào)用.注意: 跨域腳本和JSONP請求時error不被調(diào)用. |
15 | global | Boolean | 表示是否觸發(fā)全局ajax事件, 默認(rèn)為true. 設(shè)為false將不再觸發(fā)ajaxStart,ajaxStop,ajaxSend,ajaxError等. 跨站腳本和jsonp請求, 該值自動設(shè)置為false. |
16 | headers | PlainObject | 設(shè)置請求頭, 格式為k-v鍵值對對象. 由于該設(shè)置會在beforeSend函數(shù)被調(diào)用之前生效, 因此可在beforeSend函數(shù)內(nèi)覆蓋該對象. |
17 | ifModified | Boolean | 只有上次請求響應(yīng)改變時, 才允許請求成功. 它使用HTTP包的Last-Modified 頭信息判斷, 默認(rèn)為false. 若設(shè)置為true, 且數(shù)據(jù)自從上次請求后沒有更改過就會報錯. |
18 | isLocal | Boolean | 運(yùn)行當(dāng)前環(huán)境設(shè)置為"本地",默認(rèn)為false, 若設(shè)置為true, 將影響請求發(fā)送時的協(xié)議. |
19 | jsonp | String | 顯式指定jsonp請求中的回調(diào)函數(shù)的名稱. 如jsonp:cb, jq會將cb代替callback, 以 "cb=?"傳給服務(wù)器. 從jq1.5開始, 若設(shè)置jsonp:false, 那么需要明確設(shè)置jsonpCallback:"callbackName". |
20 | jsonpCallback | String,Function | 為jsonp請求指定一個回調(diào)函數(shù)名, 以取代jq自動生成的隨機(jī)函數(shù)名. 從jq1.5開始, 可以將該屬性設(shè)置為一個函數(shù), 函數(shù)的返回值就是jsonpCallback的結(jié)果. |
21 | mimeType | String | 設(shè)置一個MIME類型, 以覆蓋xhr的MIM類型(jq1.5新增) |
22 | password | String | 設(shè)置認(rèn)證請求中的密碼 |
23 | processData | Boolean | jq的ajax方法默認(rèn)會將傳入的data隱式轉(zhuǎn)換為查詢字符串(如"&a=1&b=2"), 以配合 默認(rèn)內(nèi)容類型 "application/x-www-form-urlencoded", 如果不希望轉(zhuǎn)換請設(shè)置為false. angular中想要禁用默認(rèn)轉(zhuǎn)換, 需要重寫transformRequest方法. |
24 | scriptCharset | String | 僅在"script"請求中使用(如跨域jsonp, dataType為"script"類型). 顯式指定時, 請求中將在script標(biāo)簽上設(shè)置charset屬性, 可在發(fā)現(xiàn)本地和遠(yuǎn)程編碼不一致時使用. |
25 | statusCode | PlainObject | 一組http狀態(tài)碼和回調(diào)函數(shù)對應(yīng)的鍵值對對象. 該對象以 {404:function(){}} 這種形式表示. 可用于根據(jù)不同的http狀態(tài)碼, 執(zhí)行不同的回調(diào).(jq1.5新增) |
26 | timeout | Number | 設(shè)置超時時間. |
27 | traditional | Boolean | 是否按照默認(rèn)方式序列化data對象, 默認(rèn)值為false. |
28 | type | String | 可以設(shè)置為8種http method之一, jq中不區(qū)分大小寫. |
29 | url | String | 請求的uri地址. |
30 | username | String | 設(shè)置認(rèn)證請求中的用戶名 |
31 | xhr | Function | 在回調(diào)內(nèi)創(chuàng)建并返回xhr對象 |
32 | xhrFields | PlainObject | 鍵值對對象, 用于設(shè)置原生的xhr對象, 如可用來設(shè)置withCredentials:true(jq1.5.1新增) |
$.ajax() 方法返回jqXHR對象(jq1.5起), 如果使用的不是XMLHttpRequest對象時, 如jsonp請求, 返回的jqXHR對象將盡可能模擬原生的xhr. 從jq1.5起, 返回的jqXHR對象實(shí)現(xiàn)了promise接口, 具有如下新方法.
新方法 | 被替代的老方法(jq1.8起棄用) |
---|---|
done(function(data, textStatus, jqXHR) {}) | |
fail(function(jqXHR, textStatus, errorThrown) {}) | |
always(function(data or jqXHR, textStatus, jqXHR or errorThrown) {}) |
從jq1.6開始, done, fail, always按照FIFO隊(duì)列可以分配多個回調(diào).
使用轉(zhuǎn)換器$.ajax() 的轉(zhuǎn)換器可以將支持的數(shù)據(jù)類型映射到其它數(shù)據(jù)類型. 如果需要將自定義數(shù)據(jù)類型映射到已知的類型. 需要使用 contents 選項(xiàng)在響應(yīng)的 "Content-Type" 和實(shí)際數(shù)據(jù)類型之間添加一個轉(zhuǎn)換函數(shù).
$.ajaxSetup({ contents: { myContentType: /myContentType/ }, converters: { "myContentType json": function(data) { //TODO something return newData; } } });
轉(zhuǎn)換一個支持的類型為自定義類型, 然后再返回. 如 text—>myContentType—>json.
$.ajaxSetup({ contents: { myContentType: /myContentType/ }, converters: { "text myContentType": true, "myContentType json": function(data) { //TODO something return newData; } } });事件觸發(fā)順序
$.ajax()方法觸發(fā)的事件紛繁復(fù)雜, 有將近20個之多. 為了囊括最多的事件, 這里以一次成功的上傳請求為例, 以下是它們的調(diào)用順序(請求出現(xiàn)錯誤時的順序, 請自行對應(yīng)).
序號 | 事件名稱 | 是否全局事件 | 是否能關(guān)閉 | 默認(rèn)形參 |
---|---|---|---|---|
1 | $.ajaxPrefilter | ?? | ? | function(options, originalOptions, jqXHR){} |
2 | $(document).ajaxStar | ?? | ?? | function(){}(只在當(dāng)前無激活ajax時觸發(fā)) |
3 | beforeSend | ? | - | function(jqXHR, settings){} |
4 | $(document).ajaxSend | ?? | ?? | function(){} |
5 | xhr.onloadstart | - | - | ProgressEvent |
6 | xhr.upload.onloadstart | - | - | ProgressEvent |
7 | xhr.upload.onprogress | - | - | ProgressEvent |
8 | xhr.upload.onload | - | - | ProgressEvent |
9 | xhr.upload.onloadend | - | - | ProgressEvent |
10 | xhr.onprogress | - | - | ProgressEvent |
11 | xhr.onload | - | - | ProgressEvent |
12 |
|
? | - | function(data, textStatus, jqXHR){} |
13 | $(document).ajaxSuccess | ?? | ?? | function(event, jqXHR, options){} |
14 |
|
? | - | function(jqXHR, textStatus){} |
15 | $(document).ajaxComplete | ?? | ?? | function(event, jqXHR, textStatus) |
16 | $(document).ajaxStop | ?? | ?? | function(){} |
17 | xhr.onloadend | - | - | ProgressEvent |
從jq1.8起, 對于函數(shù) ajaxStart, ajaxSend, ajaxSuccess, ajaxComplete, ajaxStop , 只能為document對象綁定事件處理函數(shù), 為其他元素綁定的事件處理函數(shù)不會起作用.
Axios實(shí)際上, 如果你僅僅只是想要一個不錯的http庫, 相比于龐大臃腫的jquery, 短小精悍的Axios可能更加適合你. 原因如下:
Axios支持node, jquery并不支持.
Axios基于promise語法, jq3.0才開始全面支持.
Axios短小精悍, 更加適合http場景, jquery大而全, 加載較慢.
vue作者尤大放棄推薦vue-resource, 轉(zhuǎn)向推薦Axios. 以下為尤大原話.
"最近團(tuán)隊(duì)討論了一下, Ajax 本身跟 Vue 并沒有什么需要特別整合的地方, 使用 fetch polyfill 或是 axios、superagent 等等都可以起到同等的效果, vue-resource 提供的價值和其維護(hù)成本相比并不劃算, 所以決定在不久以后取消對 vue-resource 的官方推薦."
Axios大小僅12k, 目前最新版本號為:
語法上Axios基本就和promise一樣, 在then方法中處理回調(diào), 在catch方法中處理異常. 如下:
axios.get("https://api.github.com/users/louiszhai") .then(function(response){ console.log(response); }) .catch(function (error) { console.log(error); });
除了get, 它還支持post, delete, head, put, patch, request請求. 具體使用攻略, 請戳這里: axios .
如需在網(wǎng)頁上引入 Axios, 可以鏈接CDN axios | Bootstrap中文網(wǎng)開源項(xiàng)目免費(fèi) CDN 服務(wù) 或者將其下載到本地.
Fetch說到ajax, 就不得不提及fetch, 由于篇幅較長, fetch已從本文中獨(dú)立出來, 請戳 Fetch進(jìn)階指南 .
ajax跨域請求 什么是CORSCORS是一個W3C(World Wide Web)標(biāo)準(zhǔn), 全稱是跨域資源共享(Cross-origin resource sharing).它允許瀏覽器向跨域服務(wù)器, 發(fā)出異步http請求, 從而克服了ajax受同源策略的限制. 實(shí)際上, 瀏覽器不會攔截不合法的跨域請求, 而是攔截了他們的響應(yīng), 因此即使請求不合法, 很多時候, 服務(wù)器依然收到了請求.(Chrome和Firefox下https網(wǎng)站不允許發(fā)送http異步請求除外)
通常, 一次跨域訪問擁有如下流程:
移動端CORS兼容性當(dāng)前幾乎所有的桌面瀏覽器(Internet Explorer 8+, Firefox 3.5+, Safari 4+和 Chrome 3+)都可通過名為跨域資源共享的協(xié)議支持ajax跨域調(diào)用.
那么移動端兼容性又如何呢? 請看下圖:
可見, CORS的技術(shù)在IOS Safari7.1及Android webview2.3中就早已支持, 即使低版本下webview的canvas在使用跨域的video或圖片時會有問題, 也絲毫不影響CORS的在移動端的使用. 至此, 我們就可以放心大膽的去應(yīng)用CORS了.
CORS有關(guān)的headers1) HTTP Response Header(服務(wù)器提供):
Access-Control-Allow-Origin: 指定允許哪些源的網(wǎng)頁發(fā)送請求.
Access-Control-Allow-Credentials: 指定是否允許cookie發(fā)送.
Access-Control-Allow-Methods: 指定允許哪些請求方法.
Access-Control-Allow-Headers: 指定允許哪些常規(guī)的頭域字段, 比如說 Content-Type.
Access-Control-Expose-Headers: 指定允許哪些額外的頭域字段, 比如說 X-Custom-Header.
該字段可省略. CORS請求時, xhr.getResponseHeader() 方法默認(rèn)只能獲取6個基本字段: Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma . 如果需要獲取其他字段, 就需要在Access-Control-Expose-Headers 中指定. 如上, 這樣xhr.getResponseHeader("X-Custom-Header") 才能返回X-Custom-Header字段的值.(該部分摘自阮一峰老師博客)
Access-Control-Max-Age: 指定preflight OPTIONS請求的有效期, 單位為秒.
2) HTTP Request Header(瀏覽器OPTIONS請求默認(rèn)自帶):
Access-Control-Request-Method: 告知服務(wù)器,瀏覽器將發(fā)送哪種請求, 比如說POST.
Access-Control-Request-Headers: 告知服務(wù)器, 瀏覽器將包含哪些額外的頭域字段.
3) 以下所有的header name 是被拒絕的:
Accept-Charset
Accept-Encoding
Access-Control-Request-Headers
Access-Control-Request-Method
Connection
Content-Length
Cookie
Cookie2
Date
DNT
Expect
Host
Keep-Alive
Origin
Referer
TE
Trailer
Transfer-Encoding
Upgrade
Via
包含以Proxy- 或 Sec- 開頭的header name
CORS請求CORS請求分為兩種, ① 簡單請求; ② 非簡單請求.
滿足如下兩個條件便是簡單請求, 反之則為非簡單請求.(CORS請求部分摘自阮一峰老師博客)
1) 請求是以下三種之一:
HEAD
GET
POST
2) http頭域不超出以下幾種字段:
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type字段限三個值 application/x-www-form-urlencoded、multipart/form-data、text/plain
對于簡單請求, 瀏覽器將發(fā)送一次http請求, 同時在Request頭域中增加 Origin 字段, 用來標(biāo)示請求發(fā)起的源, 服務(wù)器根據(jù)這個源采取不同的響應(yīng)策略. 若服務(wù)器認(rèn)為該請求合法, 那么需要往返回的 HTTP Response 中添加 Access-Control-* 等字段.( Access-Control-* 相關(guān)字段解析請閱讀我之前寫的CORS 跨域訪問 )
對于非簡單請求, 比如Method為POST且Content-Type值為 application/json 的請求或者M(jìn)ethod為 PUT 或 DELETE 的請求, 瀏覽器將發(fā)送兩次http請求. 第一次為preflight預(yù)檢(Method: OPTIONS),主要驗(yàn)證來源是否合法. 值得注意的是:OPTION請求響應(yīng)頭同樣需要包含 Access-Control-* 字段等. 第二次才是真正的HTTP請求. 所以服務(wù)器必須處理OPTIONS應(yīng)答(通常需要返回20X的狀態(tài)碼, 否則xhr.onerror事件將被觸發(fā)).
以上請求流程圖為:
HTML啟用CORShttp-equiv 相當(dāng)于http的響應(yīng)頭, 它回應(yīng)給瀏覽器一些有用的信息,以幫助正確和精確地顯示網(wǎng)頁內(nèi)容. 如下html將允許任意域名下的網(wǎng)頁跨域訪問.
圖片啟用CORS通常, 圖片允許跨域訪問, 也可以在canvas中使用跨域的圖片, 但這樣做會污染畫布, 一旦畫布受污染, 將無法讀取其數(shù)據(jù). 比如無法調(diào)用 toBlob(), toDataURL() 或 getImageData()方法. 瀏覽器的這種安全機(jī)制規(guī)避了未經(jīng)許可的遠(yuǎn)程服務(wù)器圖片被濫用的風(fēng)險.(該部分內(nèi)容摘自 啟用了 CORS 的圖片 - HTML(超文本標(biāo)記語言) | MDN)
因此如需在canvas中使用跨域的圖片資源, 請參考如下apache配置片段(來自HTML5 Boilerplate Apache server configs).
ajax文件上傳SetEnvIf Origin ":" IS_CORS Header set Access-Control-Allow-Origin "*" env=IS_CORS
ajax實(shí)現(xiàn)文件上傳非常簡單, 這里我選取原生js, jq, angular 分別來比較下, 并順便聊聊使用它們時的注意事項(xiàng).(ajax文件上傳的代碼已上傳至github, 請戳這里預(yù)覽效果: ajax 文件上傳 demo | louis)
1) 為了上傳文件, 我們得先選中一個文件. 一個type為file的input框就夠了.
2) 然后用FormData對象包裹?選中的文件.
var input = document.getElementById("input"), formData = new FormData(); formData.append("file",input.files[0]);//key可以隨意定義,只要后臺能理解就行
3) 定義上傳的URL, 以及方法. github上我搭建了一個 node-webserver, 根據(jù)需要可以自行克隆下來npm start后便可調(diào)試本篇代碼.
var url = "http://localhost:10108/test", method = "POST";js文件上傳
4.1) 封裝一個用于發(fā)送ajax請求的方法.
function ajax(url, method, data){ var xhr = null; if(window.XMLHttpRequest) { xhr = new XMLHttpRequest(); } else if (window.ActiveXObject) { try { xhr = new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) { try { xhr = new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) { alert("您的瀏覽器暫不支持Ajax!"); } } } xhr.onerror = function(e){ console.log(e); } xhr.open(method, url); try{ setTimeout(function(){ xhr.send(data); }); }catch(e){ console.log("error:",e); } return xhr; }
4.2) 上傳文件并綁定事件.
var xhr = ajax(url, method, formData); xhr.upload.onprogress = function(e){ console.log("upload progress:", e.loaded/e.total*100 + "%"); }; xhr.upload.onload = function(){ console.log("upload onload."); }; xhr.onload = function(){ console.log("onload."); }
上傳結(jié)果如下所示:
fetch上傳5) fetch只要發(fā)送一個post請求, 并且body屬性設(shè)置為formData即可. 遺憾的是, fetch無法跟蹤上傳的進(jìn)度信息.
fetch(url, { method: method, body: formData }).then(function(res){ console.log(res); }).catch(function(e){ console.log(e); });jquery文件上傳
jq提供了各式各樣的上傳插件, 其原理都是利用jq自身的ajax方法.
6) jq的ajax提供了xhr屬性用于自定義各種事件.
$.ajax({ type: method, url: url, data: formData, processData : false, contentType : false ,//必須false才會自動加上正確的Content-Type xhr: function(){ var xhr = $.ajaxSettings.xhr();//實(shí)際上就是return new window.XMLHttpRequest()對象 if(xhr.upload) { xhr.upload.addEventListener("progress", function(e){ console.log("jq upload progress:", e.loaded/e.total*100 + "%"); }, false); xhr.upload.addEventListener("load", function(){ console.log("jq upload onload."); }); xhr.addEventListener("load", function(){ console.log("jq onload."); }); return xhr; } } });
jq上傳結(jié)果如下所示:
有關(guān)jq ajax更多的api, 請參考中文文檔 jQuery.ajax() | jQuery API 中文文檔 .
angular文件上傳7.1) angular提供了$http方法用于發(fā)送http請求, 該方法返回一個promise對象.
$http({ method: method, url: url, data: formData, }).success(function(res) { console.log(res); }).error(function(err, status) { console.log(err); });
angular文件上傳的代碼已上傳至github, 請戳這里預(yù)覽效果: angular 文件上傳 demo | louis.
低版本angular中文件上傳的功能并不完整, 直到angular1.5.5才在$http中加入了eventHandler和uploadEventHandlers等方法, 使得它支持上傳進(jìn)度信息. 如下:
$http({ method: method, url: url, eventHandlers: { progress: function(c) {//下載進(jìn)度 console.log("Progress -> " + c); } }, uploadEventHandlers: { progress: function(e) {//上傳進(jìn)度 console.log("UploadProgress -> " + e); } }, data: formData, }).success(function(res) { console.log(res); }).error(function(err, status) { console.log(err); });
angular1.5.5以下低版本中, 請參考成熟的實(shí)現(xiàn)方案 angular-file-upload 以及它提供的demo Simple example .
ajax請求二進(jìn)制文件 FileReader處理二進(jìn)制文件主要使用的是H5的FileReader.
PC支持性如下:
IE | Edge | Firefox | Chrome | Safari | Opera |
---|---|---|---|---|---|
10 | 12 | 3.6 | 6 | 6 | 11.5 |
Mobile支持性如下:
IOS Safari | Opera Mini | Android Browser | Chrome/Android | UC/Android |
---|---|---|---|---|
7.1 | - | 4 | 53 | 11 |
以下是其API:
屬性/方法名稱 | 描述 |
---|---|
error | 表示讀取文件期間發(fā)生的錯誤. |
readyState | 表示讀取文件的狀態(tài).默認(rèn)有三個值:0表示文件還沒有加載;1表示文件正在讀取;2表示文件讀取完成. |
result | 讀取的文件內(nèi)容. |
abort() | 取消文件讀取操作, 此時readyState屬性將置為2. |
readAsArrayBuffer() | 讀取文件(或blob對象)為類型化數(shù)組(ArrayBuffer), 類型化數(shù)組允許開發(fā)者以數(shù)組下標(biāo)的方式, 直接操作內(nèi)存, 由于數(shù)據(jù)以二進(jìn)制形式傳遞, 效率非常高. |
讀取文件(或blob對象)為二進(jìn)制字符串, 該方法已移出標(biāo)準(zhǔn)api, 請謹(jǐn)慎使用. | |
readAsDataURL() | 讀取文件(或blob對象)為base64編碼的URL字符串, 與window.URL.createObjectURL方法效果類似. |
readAsText() | 讀取文件(或blob對象)為文本字符串. |
onload() | 文件讀取完成時的事件回調(diào), 默認(rèn)傳入event事件對象. 該回調(diào)內(nèi), 可通過this.result 或 event.target.result獲取讀取的文件內(nèi)容. |
var xhr = new XMLHttpRequest(), url = "http://louiszhai.github.io/docImages/ajax01.png"; xhr.open("GET", url); xhr.responseType = "blob"; xhr.onload = function(){ if(this.status == 200){ var blob = this.response; var img = document.createElement("img"); //方案一 img.src = window.URL.createObjectURL(blob);//這里blob依然占據(jù)著內(nèi)存 img.onload = function() { window.URL.revokeObjectURL(img.src);//釋放內(nèi)存 }; //方案二 /*var reader = new FileReader(); reader.readAsDataURL(blob);//FileReader將返回base64編碼的data-uri對象 reader.onload = function(){ img.src = this.result; }*/ //方案三 //img.src = url;//最簡單方法 document.body.appendChild(img); } } xhr.send();ajax請求二進(jìn)制文本并展示
var xhr = new XMLHttpRequest(); xhr.open("GET","http://localhost:8080/Information/download.jsp?data=node-fetch.js"); xhr.responseType = "blob"; xhr.onload = function(){ if(this.status == 200){ var blob = this.response; var reader = new FileReader(); reader.readAsBinaryString(blob);//該方法已被移出標(biāo)準(zhǔn)api,建議使用reader.readAsText(blob); reader.onload=function(){ document.body.innerHTML = "" + this.result + ""; } } } xhr.send();
有關(guān)二進(jìn)制文件的讀取, 請移步這篇博客 HTML5新特性之文件和二進(jìn)制數(shù)據(jù)的操作 .
如何等待多個ajax請求完成原生js可以使用ES6新增的Promise. ES6的Promise基于 Promises/A+ 規(guī)范(該部分 Fetch入門指南 一文也有提及).
這里先提供一個解析responses的函數(shù).
function todo(responses){ responses.forEach(function(response){ response.json().then(function(res){ console.log(res); }); }); }
原生js使用 Promise.all 方法. 如下:
var p1 = fetch("http://localhost:10108/test1"), p2 = fetch("http://localhost:10108/test2"); Promise.all([p1, p2]).then(function(responses){ todo(responses); //TODO do somethings }); //"test1" //"test2"
jquery可以使用$.when方法. 該方法接受一個或多個Deferred對象作為參數(shù), 只有全部成功才調(diào)用resolved狀態(tài)的回調(diào)函數(shù), 但只要其中有一個失敗,就調(diào)用rejected狀態(tài)的回調(diào)函數(shù). 其實(shí), jq的Deferred是基于 Promises/A規(guī)范實(shí)現(xiàn), 但并非完全遵循. (傳送門: jQuery 中的 Deferred 和 Promises (2) ).
var p1 = $.ajax("http://localhost:10108/test1"), p2 = $.ajax("http://localhost:10108/test2"); $.when(p1, p2).then(function(res1, res2){ console.log(res1);//["test1", "success", Object] console.log(res2);//["test2", "success", Object] //TODO do somethings });
如上, $.when默認(rèn)返回一個jqXHR對象, 可以直接進(jìn)行鏈?zhǔn)秸{(diào)用. then方法的回調(diào)中默認(rèn)傳入相應(yīng)的請求結(jié)果, 每個請求結(jié)果的都是數(shù)組, 數(shù)組中依次是responseText, 請求狀態(tài), 請求的jqXHR對象.
angular中可以借助 $q.all() 來實(shí)現(xiàn). 別忘了, $q 需要在controller中注入. 此外, $q 相關(guān)講解可參考 AngularJS: ng.$q 或 Angular $q service學(xué)習(xí)筆記 .
var p1 = fetch("http://localhost:10108/test1"), p2 = fetch("http://localhost:10108/test2"); $q.all([p1, p2]).then(function(responses){ todo(responses); //TODO do somethings }); //"test1" //"test2"
$q.all() 實(shí)際上就是對 Promise.all 的封裝.
ajax與history的兼容ajax的一大痛點(diǎn)就是無法支持瀏覽器前進(jìn)和后退操作. 因此早期的Gmail 采用 iframe, 來模擬ajax的前進(jìn)和后退.
如今, H5普及, pjax大行其道. pajax 就是 ajax+history.pushState 組合的一種技術(shù). 使用它便可以無刷新通過瀏覽器前進(jìn)和后退來改變頁面內(nèi)容.
先看下兼容性.
IE | Edge | Firefox | Chrome | Safari | Opera | iOS Safari | Android Browser | Chrome for Android | |
---|---|---|---|---|---|---|---|---|---|
pushState/replaceState | 10 | 12 | 4 | 5 | 6 | 11.5 | 7.1 | 4.3 | 53 |
history.state | 10 | 4 | 18 | 6 | 11.5 |
可見IE8,9并不能使用 H5的history. 需要使用墊片 HTML5 History API expansion for browsers not supporting pushState, replaceState .
pjaxpjax簡單易用, 僅需要如下三個api:
history.pushState(obj, title, url) 表示往頁面history末尾新增一個歷史項(xiàng)(history entry), 此時history.length會+1.
history.replaceState(obj, title, url) 表示替換當(dāng)前歷史項(xiàng)為新的歷史項(xiàng). 此時history.length保持不變.
window.onpopstate 僅在瀏覽器前進(jìn)和后退時觸發(fā)(history.go(1), history.back() 及l(fā)ocation.href="xxx" 均會觸發(fā)), 此時可在history.state中拿到剛剛?cè)M(jìn)去的state, 即obj對象(其他數(shù)據(jù)類型亦可).
我們注意到, 首次進(jìn)入一個頁面, 此時 history.length 值為1, history.state 為空. 如下:
1) 為了在onpopstate事件回調(diào)中每次都能拿到 history.state , 此時需要在頁面載入完成后, 自動替換下當(dāng)前url.
history.replaceState("init", title, "xxx.html?state=0");
2) 每次發(fā)送ajax請求時, 在請求完成后, 調(diào)用如下, 從而實(shí)現(xiàn)瀏覽器history往前進(jìn).
history.pushState("ajax請求相關(guān)參數(shù)", title, "xxx.html?state=標(biāo)識符");
3) 瀏覽器前進(jìn)和后退時, popstate 事件會自動觸發(fā), 此時我們手動取出 history.state , 構(gòu)建參數(shù)并重新發(fā)送ajax請求或者直接取用state值, 從而實(shí)現(xiàn)無刷新還原頁面.
window.addEventListener("popstate", function(e) { var currentState = history.state; //TODO 拼接ajax請求參數(shù)并重新發(fā)送ajax請求, 從而回到歷史頁面 //TODO 或者從state中拿到關(guān)鍵值直接還原歷史頁面 });
popstate 事件觸發(fā)時, 默認(rèn)會傳入 PopStateEvent 事件對象. 該對象具有如下屬性.
如有不懂, 更詳細(xì)講解請移步 : ajax與HTML5 history pushState/replaceState實(shí)例 ? 張鑫旭-鑫空間-鑫生活 .
ajax緩存處理js中的http緩存沒有開關(guān), 受制于瀏覽器http緩存策略. 原生xhr請求中, 可通過如下設(shè)置關(guān)閉緩存.
xhr.setRequestHeader("If-Modified-Since","0"); xhr.setRequestHeader("Cache-Control","no-cache"); //或者 URL 參數(shù)后加上 "?timestamp=" + new Date().getTime()
jquery的http緩存是否開啟可通過在settings中指定cache.
$.ajax({ url : "url", dataType : "xml", cache: true,//true表示緩存開啟, false表示緩存不開啟 success : function(xml, status){ } });
同時jquery還可以全局設(shè)置是否緩存. 如下將全局關(guān)閉ajax緩存.
$.ajaxSetup({cache:false});
除此之外, 調(diào)試過程中出現(xiàn)的瀏覽器緩存尤為可惡. 建議開啟隱私瀏覽器或者勾選??控制臺的 Disable cache 選項(xiàng). (這里以Chrome舉例, 其他瀏覽器類似)
ajax的錯誤處理前面已經(jīng)提過, 通常只要是ajax請求收到了http狀態(tài)碼, 便不會進(jìn)入到錯誤捕獲里.(Chrome中407響應(yīng)頭除外)
實(shí)際上, $.ajax 方法略有區(qū)別, jquery的ajax方法還會在類型解析出錯時觸發(fā)error回調(diào). 最常見的便是: dataType設(shè)置為json, 但是返回的data并非json格式, 此時 $.ajax 的error回調(diào)便會觸發(fā).
ajax調(diào)試技巧有關(guān)調(diào)試, 如果接口只是做小部分修改. 那么可以使用charles(Mac) 或者fiddler(Windows), 做代理, 將請求的資源替換為本地文件, 或者使用其斷點(diǎn)功能, 直接編輯response.
如果是新增接口的調(diào)試, 可以本地搭建node服務(wù). 利用hosts文件配置dns + nginx將http請求轉(zhuǎn)發(fā)到本地node服務(wù)器. 簡易的node調(diào)試服務(wù)器可參考我的 node-webserver . 如下舉一個栗子?:
hosts+nginx+node-webserver假設(shè)我們要調(diào)試的是 www.test.com 的GET接口. 以下所有步驟以Mac為例, 其他系統(tǒng), 請自行搜索?文件路徑.
1) hosts配置.
sudo vim /etc/hosts #新增一行 127.0.0.1 www.test.com
2) nginx 配置
brew install nginx #安裝 #安裝成功后進(jìn)入目標(biāo)目錄 cd /usr/local/etc/nginx/ cd servers #默認(rèn)配置入口為nginx.conf.同時servers目錄下*.conf文件已自動加入到配置文件列表中 vim test.conf #粘貼如下內(nèi)容 server { listen 80; server_name www.test.com; index index.html; error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } location / { proxy_pass http://localhost:10108/; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Read-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } } #:wq保存并退出 #啟動nginx sudo nginx -s reload #如果啟動了只需重啟即可 sudo nginx #如果沒有啟動,便啟動之
3) node-webServer 配置
參考 node-webserver . 啟動服務(wù)前只需更改index.js, 在第9行后插入如下內(nèi)容:
"get": { "/": { getKey : "Welcome to Simple Node WebServer!" }, "接口api": "你的response內(nèi)容"http://插入的代碼 },
如需在nginx中配置CORS, 請看這里: Nginx通過CORS實(shí)現(xiàn)跨域.
編碼問題XMLHttpRequest 返回的數(shù)據(jù)默認(rèn)的字符編碼是utf-8, post方法提交數(shù)據(jù)默認(rèn)的字符編碼也是utf-8. 若頁面編碼為gbk等中文編碼, 那么就會產(chǎn)生亂碼.
后端接口測試技巧通常, 如果后端接口開發(fā)OK了, 前端同學(xué)需要通過一些手段來確認(rèn)接口是能正常訪問的.
使用命令測試OPTIONS請求curl -I -X OPTIONS -H "Origin: http://example.com" http://localhost:10108/ # response HTTP/1.1 200 OK X-Powered-By: Express Content-Type: text/json;charset=UTF-8 Access-Control-Allow-Credentials: true Access-Control-Allow-Headers: x-requested-with,Content-Type Access-Control-Allow-Methods: GET,POST,PUT,DELETE,OPTIONS Access-Control-Allow-Origin: http://example.com Access-Control-Max-Age: 3600 Server: Node WebServer Website: https://github.com/Louiszhai/node-webserver Date: Fri, 21 Oct 2016 09:00:40 GMT Connection: keep-alive Transfer-Encoding: chunked
以上, http狀態(tài)碼為200, 表示允許OPTIONS請求.
GET, POST 請求與GET類似, 其他請求亦然.
curl -I -X GET -H "Origin: http://example.com" http://localhost:10108/ #HTTP/1.1 200 OK curl -I -X POST -H "Origin: http://example.com" http://localhost:10108/test #HTTP/1.1 200 OKpostman
除此之外, 我們還可以通過chrome的postman擴(kuò)展進(jìn)行測試. 請看postman素潔的界面:
postman支持所有類型的http請求, 由于其向chrome申請了cookie訪問權(quán)限及所有http(s)網(wǎng)站的訪問權(quán)限. 因此可以放心使用它進(jìn)行各種網(wǎng)站api的測試.
同時, 強(qiáng)烈建議閱讀本文的你升級postman的使用技巧, 這里有篇: 基于Postman的API自動化測試 , 拿走不謝.
ajax移動端兼容性移動端的支持性比較弱, 使用需謹(jǐn)慎. 看表.
IOS Safari | Opera Mini | Android Browser | Android Chrome | Android UC | |
---|---|---|---|---|---|
XMLHttpRequest | 8.4 | - | 4.4.4 | 53
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。 轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/81998.html 相關(guān)文章
發(fā)表評論0條評論Aomine男|高級講師TA的文章閱讀更多
閱讀需要支付1元查看
|