国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Ajax知識(shí)體系大梳理

Aomine / 2237人閱讀

摘要:導(dǎo)讀全稱即異步與它最早在中被使用然后由推廣開來(lái)典型的代表應(yīng)用有以及現(xiàn)代網(wǎng)頁(yè)中幾乎無(wú)不歡前后端分離也正是建立在異步通信的基礎(chǔ)之上瀏覽器為做了什么現(xiàn)代瀏覽器中雖然幾乎全部支持但它們的技術(shù)方案卻分為兩種標(biāo)準(zhǔn)瀏覽器通過(guò)對(duì)象實(shí)現(xiàn)了的功能只需要通過(guò)一行

導(dǎo)讀

Ajax 全稱 Asynchronous JavaScript and XML, 即異步JS與XML. 它最早在IE5中被使用, 然后由Mozilla, Apple, Google推廣開來(lái). 典型的代表應(yīng)用有 Outlook Web Access, 以及 GMail. 現(xiàn)代網(wǎng)頁(yè)中幾乎無(wú)ajax不歡. 前后端分離也正是建立在ajax異步通信的基礎(chǔ)之上.

瀏覽器為ajax做了什么

現(xiàn)代瀏覽器中, 雖然幾乎全部支持ajax, 但它們的技術(shù)方案卻分為兩種:

① 標(biāo)準(zhǔn)瀏覽器通過(guò) XMLHttpRequest 對(duì)象實(shí)現(xiàn)了ajax的功能. 只需要通過(guò)一行語(yǔ)句便可創(chuàng)建一個(gè)用于發(fā)送ajax請(qǐng)求的對(duì)象.

var xhr = new XMLHttpRequest();

② IE瀏覽器通過(guò) XMLHttpRequest 或者 ActiveXObject 對(duì)象同樣實(shí)現(xiàn)了ajax的功能.

MSXML

鑒于IE系列各種 "神級(jí)" 表現(xiàn), 我們先來(lái)看看IE瀏覽器風(fēng)騷的走位.

IE下的使用環(huán)境略顯復(fù)雜, IE7及更高版本瀏覽器可以直接使用BOM的 XMLHttpRequest 對(duì)象. MSDN傳送門: Native XMLHTTPRequest object. IE6及更低版本瀏覽器只能使用 ActiveXObject 對(duì)象來(lái)創(chuàng)建 XMLHttpRequest 對(duì)象實(shí)例. 創(chuàng)建時(shí)需要指明一個(gè)類似"Microsoft.XMLHTTP"這樣的ProgID. 而實(shí)際呢, windows系統(tǒng)環(huán)境下, 以下ProgID都應(yīng)該可以創(chuàng)建XMLHTTP對(duì)象:

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

簡(jiǎn)言之, Microsoft.XMLHTTP 已經(jīng)非常老了, 主要用于提供對(duì)歷史遺留版本的支持, 不建議使用.對(duì)于 MSXML4, 它已被 MSXML6 替代; 而 MSXML5 又是專門針對(duì)office辦公場(chǎng)景, 在沒有安裝 Microsoft Office 2003 及更高版本辦公軟件的情況下, MSXML5 未必可用. 相比之下, MSXML6 具有比 MSXML3 更穩(wěn)定, 更高性能, 更安全的優(yōu)勢(shì), 同時(shí)它也提供了一些 MSXML3 中沒有的功能, 比如說(shuō) XSD schema. 唯一遺憾的是, MSXML6 只在 vista 系統(tǒng)及以上才是默認(rèn)支持的; 而 MSXML3 在 Win2k SP4及以上系統(tǒng)就是可用的. 因此一般情況下, MSXML3 可以作為 MSXML6 的優(yōu)雅降級(jí)方案, 我們通過(guò)指定 PorgID 為 Msxml2.XMLHTTP 即可自動(dòng)映射到 Msxml2.XMLHTTP.3.0. 如下所示:

var xhr = new ActiveXObject("Msxml2.XMLHTTP");// 即MSXML3,等同于如下語(yǔ)句
var xhr = new ActiveXObject("MSXML2.XMLHTTP.3.0");

MSDN有篇文章專門講解了各個(gè)版本的MSXML. 傳送門: Using the right version of MSXML in Internet Explorer.

親測(cè)了 IE5, IE5.5, IE6, IE7, IE8, IE9, IE10, IE edge等瀏覽器, IE5及之后的瀏覽器均可以通過(guò)如下語(yǔ)句獲取xhr對(duì)象:

var xhr = new ActiveXObject("Msxml2.XMLHTTP");// 即MSXML3
var xhr = new ActiveXObject("Microsoft.XMLHTTP");// 很老的api,雖然瀏覽器支持,功能可能不完善,故不建議使用

以上, 思路已經(jīng)很清晰了, 下面給出個(gè)全兼容的方法.

全平臺(tái)兼容的XMLHttpRequest對(duì)象
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ī)制

對(duì)于這個(gè)問(wèn)題, 我們先看下瀏覽器線程機(jī)制. 一般情況下, 瀏覽器有如下四種線程:

GUI渲染線程

javascript引擎線程

瀏覽器事件觸發(fā)線程

HTTP請(qǐng)求線程

那么這么多線程, 它們究竟是怎么同js引擎線程交互的呢?

通常, 它們的線程間交互以事件的方式發(fā)生, 通過(guò)事件回調(diào)的方式予以通知. 而事件回調(diào), 又是以先進(jìn)先出的方式添加到任務(wù)隊(duì)列 的末尾 , 等到j(luò)s引擎空閑時(shí), 任務(wù)隊(duì)列 中排隊(duì)的任務(wù)將會(huì)依次被執(zhí)行. 這些事件回調(diào)包括 setTimeout, setInterval, click, ajax異步請(qǐng)求等回調(diào).

瀏覽器中, js引擎線程會(huì)循環(huán)從 任務(wù)隊(duì)列 中讀取事件并且執(zhí)行, 這種運(yùn)行機(jī)制稱作 Event Loop (事件循環(huán)).

對(duì)于一個(gè)ajax請(qǐng)求, js引擎首先生成 XMLHttpRequest 實(shí)例對(duì)象, open過(guò)后再調(diào)用send方法. 至此, 所有的語(yǔ)句都是同步執(zhí)行. 但從send方法內(nèi)部開始, 瀏覽器為將要發(fā)生的網(wǎng)絡(luò)請(qǐng)求創(chuàng)建了新的http請(qǐng)求線程, 這個(gè)線程獨(dú)立于js引擎線程, 于是網(wǎng)絡(luò)請(qǐng)求異步被發(fā)送出去了. 另一方面, js引擎并不會(huì)等待 ajax 發(fā)起的http請(qǐng)求收到結(jié)果, 而是直接順序往下執(zhí)行.

當(dāng)ajax請(qǐng)求被服務(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ù)才被撈出來(lái), 按照添加順序, 挨個(gè)執(zhí)行, 當(dāng)然也包括剛剛append到隊(duì)列末尾的 onreadystatechange 事件.

onreadystatechange 事件內(nèi)部, 有可能對(duì)dom進(jìn)行操作. 此時(shí)瀏覽器便會(huì)掛起js引擎線程, 轉(zhuǎn)而執(zhí)行GUI渲染線程, 進(jìn)行UI重繪(repaint)或者回流(reflow). 當(dāng)js引擎重新執(zhí)行時(shí), GUI渲染線程又會(huì)被掛起, GUI更新將被保存起來(lái), 等到j(luò)s引擎空閑時(shí)立即被執(zhí)行.

以上整個(gè)ajax請(qǐng)求過(guò)程中, 有涉及到瀏覽器的4種線程. 其中除了 GUI渲染線程js引擎線程 是互斥的. 其他線程相互之間, 都是可以并行執(zhí)行的. 通過(guò)這樣的一種方式, ajax并沒有破壞js的單線程機(jī)制.

ajax與setTimeout排隊(duì)問(wèn)題

通常, ajax 和 setTimeout 的事件回調(diào)都被同等的對(duì)待, 按照順序自動(dòng)的被添加到 任務(wù)隊(duì)列 的末尾, 等待js引擎空閑時(shí)執(zhí)行. 但請(qǐng)注意, 并非xhr的所有回調(diào)執(zhí)行都滯后于setTImeout的回調(diào). 請(qǐng)看如下代碼:

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請(qǐng)求, 并非所有的部分都是異步的, 至少"readyState==1"的 onreadystatechange 回調(diào)以及 onloadstart 回調(diào)就是同步執(zhí)行的. 因此它們的輸出排在最前面.

XMLHttpRequest 屬性解讀

首先在Chrome console下創(chuàng)建一個(gè) XMLHttpRequest 實(shí)例對(duì)象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í)例對(duì)象沒有自有屬性. 實(shí)際上, 它的所有屬性均來(lái)自于 XMLHttpRequest.prototype .

追根溯源, XMLHttpRequest 實(shí)例對(duì)象具有如下的繼承關(guān)系. (下面以a<

xhr << XMLHttpRequest.prototype << XMLHttpRequestEventTarget.prototype << EventTarget.prototype << Object.prototype

由上, xhr也具有Object等原型中的所有方法. 如toString方法.

xhr.toString();//"[object XMLHttpRequest]"

通常, 一個(gè)xhr實(shí)例對(duì)象擁有10個(gè)普通屬性+9個(gè)方法.

readyState

只讀屬性, readyState屬性記錄了ajax調(diào)用過(guò)程中所有可能的狀態(tài). 它的取值簡(jiǎn)單明了, 如下:

readyState 對(duì)應(yīng)常量 描述
0 (未初始化) xhr.UNSENT 請(qǐng)求已建立, 但未初始化(此時(shí)未調(diào)用open方法)
1 (初始化) xhr.OPENED 請(qǐng)求已建立, 但未發(fā)送 (已調(diào)用open方法, 但未調(diào)用send方法)
2 (發(fā)送數(shù)據(jù)) xhr.HEADERS_RECEIVED 請(qǐng)求已發(fā)送 (send方法已調(diào)用, 已收到響應(yīng)頭)
3 (數(shù)據(jù)傳送中) xhr.LOADING 請(qǐng)求處理中, 因響應(yīng)內(nèi)容不全, 這時(shí)通過(guò)responseBody和responseText獲取可能會(huì)出現(xiàn)錯(cuò)誤
4 (完成) xhr.DONE 數(shù)據(jù)接收完畢, 此時(shí)可以通過(guò)通過(guò)responseBody和responseText獲取完整的響應(yīng)數(shù)據(jù)

注意, readyState 是一個(gè)只讀屬性, 想要改變它的值是不可行的.

onreadystatechange

onreadystatechange事件回調(diào)方法在readystate狀態(tài)改變時(shí)觸發(fā), 在一個(gè)收到響應(yīng)的ajax請(qǐng)求周期中, onreadystatechange 方法會(huì)被觸發(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)會(huì)傳入Event實(shí)例, 如下:

status

只讀屬性, status表示http請(qǐng)求的狀態(tài), 初始值為0. 如果服務(wù)器沒有顯式地指定狀態(tài)碼, 那么status將被設(shè)置為默認(rèn)值, 即200.

statusText

只讀屬性, statusText表示服務(wù)器的響應(yīng)狀態(tài)信息, 它是一個(gè) UTF-16 的字符串, 請(qǐng)求成功且status==20X時(shí), 返回大寫的 OK . 請(qǐng)求失敗時(shí)返回空字符串. 其他情況下返回相應(yīng)的狀態(tài)描述. 比如: 301的 Moved Permanently , 302的 Found , 303的 See Other , 307 的 Temporary Redirect , 400的 Bad Request , 401的 Unauthorized 等等.

onloadstart

onloadstart事件回調(diào)方法在ajax請(qǐng)求發(fā)送之前觸發(fā), 觸發(fā)時(shí)機(jī)在 readyState==1 狀態(tài)之后, readyState==2 狀態(tài)之前.

onloadstart方法中默認(rèn)將傳入一個(gè)ProgressEvent事件進(jìn)度對(duì)象. 如下:

ProgressEvent對(duì)象具有三個(gè)重要的Read only屬性.

lengthComputable 表示長(zhǎng)度是否可計(jì)算, 它是一個(gè)布爾值, 初始值為false.

loaded 表示已加載資源的大小, 如果使用http下載資源, 它僅僅表示已下載內(nèi)容的大小, 而不包括http headers等. 它是一個(gè)無(wú)符號(hào)長(zhǎng)整型, 初始值為0.

total 表示資源總大小, 如果使用http下載資源, 它僅僅表示內(nèi)容的總大小, 而不包括http headers等, 它同樣是一個(gè)無(wú)符號(hào)長(zhǎng)整型, 初始值為0.

onprogress

onprogress事件回調(diào)方法在 readyState==3 狀態(tài)時(shí)開始觸發(fā), 默認(rèn)傳入 ProgressEvent 對(duì)象, 可通過(guò) e.loaded/e.total 來(lái)計(jì)算加載資源的進(jìn)度, 該方法用于獲取資源的下載進(jìn)度.

注意: 該方法適用于 IE10+ 及其他現(xiàn)代瀏覽器.

xhr.onprogress = function(e){
  console.log("progress:", e.loaded/e.total);
}
onload

onload事件回調(diào)方法在ajax請(qǐng)求成功后觸發(fā), 觸發(fā)時(shí)機(jī)在 readyState==4 狀態(tài)之后.

想要捕捉到一個(gè)ajax異步請(qǐng)求的成功狀態(tài), 并且執(zhí)行回調(diào), 一般下面的語(yǔ)句就足夠了:

xhr.onload = function(){
  var s = xhr.status;
  if((s >= 200 && s < 300) || s == 304){
    var resp = xhr.responseText;
    //TODO ...
  }
}
onloadend

onloadend事件回調(diào)方法在ajax請(qǐng)求完成后觸發(fā), 觸發(fā)時(shí)機(jī)在 readyState==4 狀態(tài)之后(收到響應(yīng)時(shí)) 或者 readyState==2 狀態(tài)之后(未收到響應(yīng)時(shí)).

onloadend方法中默認(rèn)將傳入一個(gè)ProgressEvent事件進(jìn)度對(duì)象.

timeout

timeout屬性用于指定ajax的超時(shí)時(shí)長(zhǎng). 通過(guò)它可以靈活地控制ajax請(qǐng)求時(shí)間的上限. timeout的值滿足如下規(guī)則:

通常設(shè)置為0時(shí)不生效.

設(shè)置為字符串時(shí), 如果字符串中全部為數(shù)字, 它會(huì)自動(dòng)將字符串轉(zhuǎn)化為數(shù)字, 反之該設(shè)置不生效.

設(shè)置為對(duì)象時(shí), 如果該對(duì)象能夠轉(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請(qǐng)求超時(shí)時(shí)觸發(fā), 通過(guò)它可以在ajax請(qǐng)求超時(shí)時(shí)做一些后續(xù)處理.

xhr.ontimeout = function(e) {
  console.error("請(qǐng)求超時(shí)!!!")
}
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, 則會(huì)報(bào)錯(cuò).

responseType

responseType表示響應(yīng)的類型, 缺省為空字符串, 可取 "arraybuffer" , "blob" , "document" , "json" , and "text" 共五種類型.

responseURL

responseURL返回ajax請(qǐng)求最終的URL, 如果請(qǐng)求中存在重定向, 那么responseURL表示重定向之后的URL.

withCredentials

withCredentials是一個(gè)布爾值, 默認(rèn)為false, 表示跨域請(qǐng)求中不發(fā)送cookies等信息. 當(dāng)它設(shè)置為true時(shí), cookies , authorization headers 或者 TLS客戶端證書 都可以正常發(fā)送和接收. 顯然它的值對(duì)同域請(qǐng)求沒有影響.

注意: 該屬性適用于 IE10+, opera12+及其他現(xiàn)代瀏覽器.

abort

abort方法用于取消ajax請(qǐng)求, 取消后, readyState 狀態(tài)將被設(shè)置為?0?(UNSENT). 如下, 調(diào)用abort 方法后, 請(qǐng)求將被取消.

getResponseHeader

getResponseHeader方法用于獲取ajax響應(yīng)頭中指定name的值. 如果response headers中存在相同的name, 那么它們的值將自動(dòng)以字符串的形式連接在一起.

console.log(xhr.getResponseHeader("Content-Type"));//"text/html"
getAllResponseHeaders

getAllResponseHeaders方法用于獲取所有安全的ajax響應(yīng)頭, 響應(yīng)頭以字符串形式返回. 每個(gè)HTTP報(bào)頭名稱和值用冒號(hào)分隔, 如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)時(shí), 就意味著響應(yīng)頭已接受完整. 此時(shí)便可以打印出完整的 response headers.

setRequestHeader

既然可以獲取響應(yīng)頭, 那么自然也可以設(shè)置請(qǐng)求頭, setRequestHeader就是干這個(gè)的. 如下:

//指定請(qǐng)求的type為json格式
xhr.setRequestHeader("Content-type", "application/json");
//除此之外, 還可以設(shè)置其他的請(qǐng)求頭
xhr.setRequestHeader("x-requested-with", "123456");
onerror

onerror方法用于在ajax請(qǐng)求出錯(cuò)后執(zhí)行. 通常只在網(wǎng)絡(luò)出現(xiàn)問(wèn)題時(shí)或者ERR_CONNECTION_RESET時(shí)觸發(fā)(如果請(qǐng)求返回的是407狀態(tài)碼, chrome下也會(huì)觸發(fā)onerror).

upload

upload屬性默認(rèn)返回一個(gè) XMLHttpRequestUpload 對(duì)象, 用于上傳資源. 該對(duì)象具有如下方法:

onloadstart

onprogress

onabort

onerror

onload

ontimeout

onloadend

上述方法功能同 xhr 對(duì)象中同名方法一致. 其中, 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

通過(guò)overrideMimeType方法將response的MIME類型設(shè)置為 text/xml;charset=utf-8 , 如下所示:

xhr.overrideMimeType("text/xml; charset = utf-8");
xhr.send();

此時(shí)雖然 response headers 如上圖, 沒有變化, 但 Content-Type 已替換為新值.

xhr.getResponseHeader("Content-Type");//"text/xml; charset = utf-8"

此時(shí), xhr.responseXML 也將返回DOM對(duì)象, 如下圖.

XHR一級(jí)

XHR1 即 XMLHttpRequest Level 1. XHR1時(shí), xhr對(duì)象具有如下缺點(diǎn):

僅支持文本數(shù)據(jù)傳輸, 無(wú)法傳輸二進(jìn)制數(shù)據(jù).

傳輸數(shù)據(jù)時(shí), 沒有進(jìn)度信息提示, 只能提示是否完成.

受瀏覽器 同源策略 限制, 只能請(qǐng)求同域資源.

沒有超時(shí)機(jī)制, 不方便掌控ajax請(qǐng)求節(jié)奏.

XHR二級(jí)

XHR2 即 XMLHttpRequest Level 2. XHR2針對(duì)XHR1的上述缺點(diǎn)做了如下改進(jìn):

支持二進(jìn)制數(shù)據(jù), 可以上傳文件, 可以使用FormData對(duì)象管理表單.

提供進(jìn)度提示, 可通過(guò) xhr.upload.onprogress 事件回調(diào)方法獲取傳輸進(jìn)度.

依然受 同源策略 限制, 這個(gè)安全機(jī)制不會(huì)變. XHR2新提供 Access-Control-Allow-Origin 等headers, 設(shè)置為 * 時(shí)表示允許任何域名請(qǐng)求, 從而實(shí)現(xiàn)跨域CORS訪問(wèn)(有關(guān)CORS詳細(xì)介紹請(qǐng)耐心往下讀).

可以設(shè)置timeout 及 ontimeout, 方便設(shè)置超時(shí)時(shí)長(zhǎng)和超時(shí)后續(xù)處理.

這里就H5新增的FormData對(duì)象舉個(gè)例.

//可直接創(chuàng)建FormData實(shí)例
var data = new FormData();
data.append("name", "louis");
xhr.send(data);
//還可以通過(guò)傳入表單DOM對(duì)象來(lái)創(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的.

那么問(wèn)題來(lái)了, IE7, 8,9的用戶怎么辦? 很遺憾, 這些用戶是比較尷尬的. 對(duì)于IE8,9而言, 只有一個(gè)閹割版的 XDomainRequest 可用,IE7則沒有. 估計(jì)IE7用戶只能哭暈在廁所了.

XDomainRequest

XDomainRequest 對(duì)象是IE8,9折騰出來(lái)的, 用于支持CORS請(qǐng)求非成熟的解決方案. 以至于IE10中直接移除了它, 并重新回到了 XMLHttpRequest 的懷抱.

XDomainRequest 僅可用于發(fā)送 GET POST 請(qǐng)求. 如下即創(chuàng)建過(guò)程.

var xdr = new XDomainRequest();

xdr具有如下屬性:

timeout

responseText

如下方法:

open: 只能接收Method,和url兩個(gè)參數(shù). 只能發(fā)送異步請(qǐng)求.

send

abort

如下事件回調(diào):

onprogress

ontimeout

onerror

onload

除了缺少一些方法外, XDomainRequest 基本上就和 XMLHttpRequest 的使用方式保持一致.

必須要明確的是:

XDomainRequest 不支持跨域傳輸cookie.

只能設(shè)置請(qǐng)求頭的Content-Type字段, 且不能訪問(wèn)響應(yīng)頭信息.

$.ajax

$.ajax是jquery對(duì)原生ajax的一次封裝. 通過(guò)封裝ajax, jquery抹平了不同版本瀏覽器異步http的差異性, 取而代之的是高度統(tǒng)一的api. jquery作為js類庫(kù)時(shí)代的先驅(qū), 對(duì)前端發(fā)展有著深遠(yuǎn)的影響. 了解并熟悉其ajax方法, 不可謂不重要.

參數(shù)列表

$.ajax() 只有一個(gè)參數(shù), 該參數(shù)為key-value設(shè)置對(duì)象. 實(shí)際上, jq發(fā)送的所有ajax請(qǐng)求, 都是通過(guò)調(diào)用該ajax方法實(shí)現(xiàn)的. 它的詳細(xì)參數(shù)如下表:

序號(hào) 參數(shù) 類型 描述
1 accepts PlainObject 用于通知服務(wù)器該請(qǐng)求需要接收何種類型的返回結(jié)果. 如有必要, 推薦在 $.ajaxSetup()?方法中設(shè)置一次.
2 async Boolean 默認(rèn)為true, 即異步.
3 beforeSend Function 請(qǐng)求發(fā)送前的回調(diào), 默認(rèn)傳入?yún)?shù)jqXHR和settings. 函數(shù)內(nèi)顯式返回false將取消本次請(qǐng)求.
4 cache Boolean 請(qǐng)求是否開啟緩存, 默認(rèn)為true, 如不需要緩存請(qǐng)?jiān)O(shè)置為false. 不過(guò), dataType為"script"和"jsonp"時(shí)默認(rèn)為false.
5 complete Function 請(qǐng)求完成后的回調(diào)(請(qǐng)求success?和?error之后均調(diào)用), 默認(rèn)傳入?yún)?shù)jqXHR和textStatus(請(qǐng)求狀態(tài), 取值為 "success","notmodified","error","timeout","abort","parsererror"之一). 從jq1.5開始, complete可以設(shè)置為一個(gè)包含函數(shù)的數(shù)組. 如此每個(gè)函數(shù)將依次被調(diào)用.
6 contents PlainObject 一個(gè)以"{字符串/正則表達(dá)式}"配對(duì)的對(duì)象, 根據(jù)給定的內(nèi)容類型, 解析請(qǐng)求的返回結(jié)果.
7 contentType String 編碼類型, 相對(duì)應(yīng)于http請(qǐng)求頭域的"Content-Type"字段. 默認(rèn)值為"application/x-www-form-urlencoded; charset=UTF-8".
8 context Object 設(shè)置ajax回調(diào)函數(shù)的上下文. 默認(rèn)上下文為ajax請(qǐng)求傳入的參數(shù)設(shè)置對(duì)象. 如設(shè)置為document.body, 那么所有ajax回調(diào)函數(shù)中將以body為上下文.
9 converters PlainObject 一個(gè)數(shù)據(jù)類型到數(shù)據(jù)類型轉(zhuǎn)換器的對(duì)象. 默認(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)同域請(qǐng)求為false, 跨域請(qǐng)求為true.
11 data Object, Array 發(fā)送到服務(wù)器的數(shù)據(jù), 默認(rèn)data為鍵值對(duì)格式對(duì)象, 若data為數(shù)組則按照traditional參數(shù)的值, 自動(dòng)轉(zhuǎn)化為一個(gè)同名的多值查詢字符串. 如{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時(shí)提供的dataType參數(shù)
13 dataType String 預(yù)期服務(wù)器返回的數(shù)據(jù)類型, 可設(shè)置為"xml","html","script","json","jsonp","text"之一, 其中設(shè)置為"xml"或"text"類型時(shí), 數(shù)據(jù)不會(huì)經(jīng)過(guò)處理.
14 error Function 請(qǐng)求失敗時(shí)的回調(diào)函數(shù), 默認(rèn)傳入jqXHR(jq1.4以前為原生xhr對(duì)象),textStatus(請(qǐng)求狀態(tài),取值為null,"timeout","error","abort" 或 "parsererror"),errorString(錯(cuò)誤內(nèi)容), 當(dāng)一個(gè)HTTP錯(cuò)誤發(fā)生時(shí), errorThrown?接收HTTP狀態(tài)的文本部分,比如"Not Found"等. 從jq1.5開始, error可以設(shè)置為一個(gè)包含函數(shù)的數(shù)組. 如此每個(gè)函數(shù)將依次被調(diào)用.注意: 跨域腳本和JSONP請(qǐng)求時(shí)error不被調(diào)用.
15 global Boolean 表示是否觸發(fā)全局ajax事件, 默認(rèn)為true. 設(shè)為false將不再觸發(fā)ajaxStart,ajaxStop,ajaxSend,ajaxError等. 跨站腳本和jsonp請(qǐng)求, 該值自動(dòng)設(shè)置為false.
16 headers PlainObject 設(shè)置請(qǐng)求頭, 格式為k-v鍵值對(duì)對(duì)象. 由于該設(shè)置會(huì)在beforeSend函數(shù)被調(diào)用之前生效, 因此可在beforeSend函數(shù)內(nèi)覆蓋該對(duì)象.
17 ifModified Boolean 只有上次請(qǐng)求響應(yīng)改變時(shí), 才允許請(qǐng)求成功. 它使用HTTP包的Last-Modified 頭信息判斷, 默認(rèn)為false. 若設(shè)置為true, 且數(shù)據(jù)自從上次請(qǐng)求后沒有更改過(guò)就會(huì)報(bào)錯(cuò).
18 isLocal Boolean 運(yùn)行當(dāng)前環(huán)境設(shè)置為"本地",默認(rèn)為false, 若設(shè)置為true, 將影響請(qǐng)求發(fā)送時(shí)的協(xié)議.
19 jsonp String 顯式指定jsonp請(qǐng)求中的回調(diào)函數(shù)的名稱. 如jsonp:cb, jq會(huì)將cb代替callback, 以 "cb=?"傳給服務(wù)器. 從jq1.5開始, 若設(shè)置jsonp:false, 那么需要明確設(shè)置jsonpCallback:"callbackName".
20 jsonpCallback String,Function 為jsonp請(qǐng)求指定一個(gè)回調(diào)函數(shù)名, 以取代jq自動(dòng)生成的隨機(jī)函數(shù)名. 從jq1.5開始, 可以將該屬性設(shè)置為一個(gè)函數(shù), 函數(shù)的返回值就是jsonpCallback的結(jié)果.
21 mimeType String 設(shè)置一個(gè)MIME類型, 以覆蓋xhr的MIM類型(jq1.5新增)
22 password String 設(shè)置認(rèn)證請(qǐng)求中的密碼
23 processData Boolean jq的ajax方法默認(rèn)會(huì)將傳入的data隱式轉(zhuǎn)換為查詢字符串(如"&a=1&b=2"), 以配合 默認(rèn)內(nèi)容類型 "application/x-www-form-urlencoded", 如果不希望轉(zhuǎn)換請(qǐng)?jiān)O(shè)置為false. angular中想要禁用默認(rèn)轉(zhuǎn)換, 需要重寫transformRequest方法.
24 scriptCharset String 僅在"script"請(qǐng)求中使用(如跨域jsonp, dataType為"script"類型). 顯式指定時(shí), 請(qǐng)求中將在script標(biāo)簽上設(shè)置charset屬性, 可在發(fā)現(xiàn)本地和遠(yuǎn)程編碼不一致時(shí)使用.
25 statusCode PlainObject 一組http狀態(tài)碼和回調(diào)函數(shù)對(duì)應(yīng)的鍵值對(duì)對(duì)象. 該對(duì)象以 {404:function(){}} 這種形式表示. 可用于根據(jù)不同的http狀態(tài)碼, 執(zhí)行不同的回調(diào).(jq1.5新增)
26 timeout Number 設(shè)置超時(shí)時(shí)間.
27 traditional Boolean 是否按照默認(rèn)方式序列化data對(duì)象, 默認(rèn)值為false.
28 type String 可以設(shè)置為8種http method之一, jq中不區(qū)分大小寫.
29 url String 請(qǐng)求的uri地址.
30 username String 設(shè)置認(rèn)證請(qǐng)求中的用戶名
31 xhr Function 在回調(diào)內(nèi)創(chuàng)建并返回xhr對(duì)象
32 xhrFields PlainObject 鍵值對(duì)對(duì)象, 用于設(shè)置原生的xhr對(duì)象, 如可用來(lái)設(shè)置withCredentials:true(jq1.5.1新增)
支持promise

$.ajax() 方法返回jqXHR對(duì)象(jq1.5起), 如果使用的不是XMLHttpRequest對(duì)象時(shí), 如jsonp請(qǐng)求, 返回的jqXHR對(duì)象將盡可能模擬原生的xhr. 從jq1.5起, 返回的jqXHR對(duì)象實(shí)現(xiàn)了promise接口, 具有如下新方法.

新方法 被替代的老方法(jq1.8起棄用)
done(function(data, textStatus, jqXHR) {}) success
fail(function(jqXHR, textStatus, errorThrown) {}) error
always(function(data or jqXHR, textStatus, jqXHR or errorThrown) {}) complete

從jq1.6開始, done, fail, always按照FIFO隊(duì)列可以分配多個(gè)回調(diào).

使用轉(zhuǎn)換器

$.ajax() 的轉(zhuǎn)換器可以將支持的數(shù)據(jù)類型映射到其它數(shù)據(jù)類型. 如果需要將自定義數(shù)據(jù)類型映射到已知的類型. 需要使用 contents 選項(xiàng)在響應(yīng)的 "Content-Type" 和實(shí)際數(shù)據(jù)類型之間添加一個(gè)轉(zhuǎn)換函數(shù).

$.ajaxSetup({
  contents: {
    myContentType: /myContentType/
  },
  converters: {
    "myContentType json": function(data) {
      //TODO something
      return newData;
    }
  }
});

轉(zhuǎn)換一個(gè)支持的類型為自定義類型, 然后再返回. 如 text—>myContentType—>json.

$.ajaxSetup({
  contents: {
    myContentType: /myContentType/
  },
  converters: {
    "text myContentType": true,
    "myContentType json": function(data) {
      //TODO something
      return newData;
    }
  }
});
事件觸發(fā)順序

$.ajax()方法觸發(fā)的事件紛繁復(fù)雜, 有將近20個(gè)之多. 為了囊括最多的事件, 這里以一次成功的上傳請(qǐng)求為例, 以下是它們的調(diào)用順序(請(qǐng)求出現(xiàn)錯(cuò)誤時(shí)的順序, 請(qǐng)自行對(duì)應(yīng)).

序號(hào) 事件名稱 是否全局事件 是否能關(guān)閉 默認(rèn)形參
1 $.ajaxPrefilter ?? ? function(options, originalOptions, jqXHR){}
2 $(document).ajaxStar ?? ?? function(){}(只在當(dāng)前無(wú)激活ajax時(shí)觸發(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 success(棄用) ? - function(data, textStatus, jqXHR){}
13 $(document).ajaxSuccess ?? ?? function(event, jqXHR, options){}
14 complete(棄用) ? - function(jqXHR, textStatus){}
15 $(document).ajaxComplete ?? ?? function(event, jqXHR, textStatus)
16 $(document).ajaxStop ?? ?? function(){}
17 xhr.onloadend - - ProgressEvent

從jq1.8起, 對(duì)于函數(shù) ajaxStart, ajaxSend, ajaxSuccess, ajaxComplete, ajaxStop , 只能為document對(duì)象綁定事件處理函數(shù), 為其他元素綁定的事件處理函數(shù)不會(huì)起作用.

Axios

實(shí)際上, 如果你僅僅只是想要一個(gè)不錯(cuò)的http庫(kù), 相比于龐大臃腫的jquery, 短小精悍的Axios可能更加適合你. 原因如下:

Axios支持node, jquery并不支持.

Axios基于promise語(yǔ)法, jq3.0才開始全面支持.

Axios短小精悍, 更加適合http場(chǎng)景, jquery大而全, 加載較慢.

vue作者尤大放棄推薦vue-resource, 轉(zhuǎn)向推薦Axios. 以下為尤大原話.

"最近團(tuán)隊(duì)討論了一下, Ajax 本身跟 Vue 并沒有什么需要特別整合的地方, 使用 fetch polyfill 或是 axios、superagent 等等都可以起到同等的效果, vue-resource 提供的價(jià)值和其維護(hù)成本相比并不劃算, 所以決定在不久以后取消對(duì) vue-resource 的官方推薦."

Axios大小僅12k, 目前最新版本號(hào)為:

語(yǔ)法上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請(qǐng)求. 具體使用攻略, 請(qǐng)戳這里: axios .

如需在網(wǎng)頁(yè)上引入 Axios, 可以鏈接CDN axios | Bootstrap中文網(wǎng)開源項(xiàng)目免費(fèi) CDN 服務(wù) 或者將其下載到本地.

Fetch

說(shuō)到ajax, 就不得不提及fetch, 由于篇幅較長(zhǎng), fetch已從本文中獨(dú)立出來(lái), 請(qǐng)戳 Fetch進(jìn)階指南 .

ajax跨域請(qǐng)求 什么是CORS

CORS是一個(gè)W3C(World Wide Web)標(biāo)準(zhǔn), 全稱是跨域資源共享(Cross-origin resource sharing).它允許瀏覽器向跨域服務(wù)器, 發(fā)出異步http請(qǐng)求, 從而克服了ajax受同源策略的限制. 實(shí)際上, 瀏覽器不會(huì)攔截不合法的跨域請(qǐng)求, 而是攔截了他們的響應(yīng), 因此即使請(qǐng)求不合法, 很多時(shí)候, 服務(wù)器依然收到了請(qǐng)求.(Chrome和Firefox下https網(wǎng)站不允許發(fā)送http異步請(qǐng)求除外)

通常, 一次跨域訪問(wèn)擁有如下流程:

移動(dòng)端CORS兼容性

當(dāng)前幾乎所有的桌面瀏覽器(Internet Explorer 8+, Firefox 3.5+, Safari 4+和 Chrome 3+)都可通過(guò)名為跨域資源共享的協(xié)議支持ajax跨域調(diào)用.

那么移動(dòng)端兼容性又如何呢? 請(qǐng)看下圖:

可見, CORS的技術(shù)在IOS Safari7.1及Android webview2.3中就早已支持, 即使低版本下webview的canvas在使用跨域的video或圖片時(shí)會(huì)有問(wèn)題, 也絲毫不影響CORS的在移動(dòng)端的使用. 至此, 我們就可以放心大膽的去應(yīng)用CORS了.

CORS有關(guān)的headers

1) HTTP Response Header(服務(wù)器提供):

Access-Control-Allow-Origin: 指定允許哪些源的網(wǎng)頁(yè)發(fā)送請(qǐng)求.

Access-Control-Allow-Credentials: 指定是否允許cookie發(fā)送.

Access-Control-Allow-Methods: 指定允許哪些請(qǐng)求方法.

Access-Control-Allow-Headers: 指定允許哪些常規(guī)的頭域字段, 比如說(shuō) Content-Type.

Access-Control-Expose-Headers: 指定允許哪些額外的頭域字段, 比如說(shuō) X-Custom-Header.

該字段可省略. CORS請(qǐng)求時(shí), xhr.getResponseHeader() 方法默認(rèn)只能獲取6個(gè)基本字段: Cache-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragma . 如果需要獲取其他字段, 就需要在Access-Control-Expose-Headers 中指定. 如上, 這樣xhr.getResponseHeader("X-Custom-Header") 才能返回X-Custom-Header字段的值.(該部分摘自阮一峰老師博客)

Access-Control-Max-Age: 指定preflight OPTIONS請(qǐng)求的有效期, 單位為秒.

2) HTTP Request Header(瀏覽器OPTIONS請(qǐng)求默認(rèn)自帶):

Access-Control-Request-Method: 告知服務(wù)器,瀏覽器將發(fā)送哪種請(qǐng)求, 比如說(shuō)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請(qǐng)求

CORS請(qǐng)求分為兩種, ① 簡(jiǎn)單請(qǐng)求; ② 非簡(jiǎn)單請(qǐng)求.

滿足如下兩個(gè)條件便是簡(jiǎn)單請(qǐng)求, 反之則為非簡(jiǎn)單請(qǐng)求.(CORS請(qǐng)求部分摘自阮一峰老師博客)

1) 請(qǐng)求是以下三種之一:

HEAD

GET

POST

2) http頭域不超出以下幾種字段:

Accept

Accept-Language

Content-Language

Last-Event-ID

Content-Type字段限三個(gè)值 application/x-www-form-urlencodedmultipart/form-datatext/plain

對(duì)于簡(jiǎn)單請(qǐng)求, 瀏覽器將發(fā)送一次http請(qǐng)求, 同時(shí)在Request頭域中增加 Origin 字段, 用來(lái)標(biāo)示請(qǐng)求發(fā)起的源, 服務(wù)器根據(jù)這個(gè)源采取不同的響應(yīng)策略. 若服務(wù)器認(rèn)為該請(qǐng)求合法, 那么需要往返回的 HTTP Response 中添加 Access-Control-* 等字段.( Access-Control-* 相關(guān)字段解析請(qǐng)閱讀我之前寫的CORS 跨域訪問(wèn) )

對(duì)于非簡(jiǎn)單請(qǐng)求, 比如Method為POST且Content-Type值為 application/json 的請(qǐng)求或者M(jìn)ethod為 PUTDELETE 的請(qǐng)求, 瀏覽器將發(fā)送兩次http請(qǐng)求. 第一次為preflight預(yù)檢(Method: OPTIONS),主要驗(yàn)證來(lái)源是否合法. 值得注意的是:OPTION請(qǐng)求響應(yīng)頭同樣需要包含 Access-Control-* 字段等. 第二次才是真正的HTTP請(qǐng)求. 所以服務(wù)器必須處理OPTIONS應(yīng)答(通常需要返回20X的狀態(tài)碼, 否則xhr.onerror事件將被觸發(fā)).

以上請(qǐng)求流程圖為:

HTML啟用CORS

http-equiv 相當(dāng)于http的響應(yīng)頭, 它回應(yīng)給瀏覽器一些有用的信息,以幫助正確和精確地顯示網(wǎng)頁(yè)內(nèi)容. 如下html將允許任意域名下的網(wǎng)頁(yè)跨域訪問(wèn).

圖片啟用CORS

通常, 圖片允許跨域訪問(wèn), 也可以在canvas中使用跨域的圖片, 但這樣做會(huì)污染畫布, 一旦畫布受污染, 將無(wú)法讀取其數(shù)據(jù). 比如無(wú)法調(diào)用 toBlob(), toDataURL() 或 getImageData()方法. 瀏覽器的這種安全機(jī)制規(guī)避了未經(jīng)許可的遠(yuǎn)程服務(wù)器圖片被濫用的風(fēng)險(xiǎn).(該部分內(nèi)容摘自 啟用了 CORS 的圖片 - HTML(超文本標(biāo)記語(yǔ)言) | MDN)

因此如需在canvas中使用跨域的圖片資源, 請(qǐng)參考如下apache配置片段(來(lái)自HTML5 Boilerplate Apache server configs).


    
        
            SetEnvIf Origin ":" IS_CORS
            Header set Access-Control-Allow-Origin "*" env=IS_CORS
        
    
ajax文件上傳

ajax實(shí)現(xiàn)文件上傳非常簡(jiǎn)單, 這里我選取原生js, jq, angular 分別來(lái)比較下, 并順便聊聊使用它們時(shí)的注意事項(xiàng).(ajax文件上傳的代碼已上傳至github, 請(qǐng)戳這里預(yù)覽效果: ajax 文件上傳 demo | louis)

1) 為了上傳文件, 我們得先選中一個(gè)文件. 一個(gè)type為file的input框就夠了.

2) 然后用FormData對(duì)象包裹?選中的文件.

var input = document.getElementById("input"),
    formData = new FormData();
formData.append("file",input.files[0]);//key可以隨意定義,只要后臺(tái)能理解就行

3) 定義上傳的URL, 以及方法. github上我搭建了一個(gè) node-webserver, 根據(jù)需要可以自行克隆下來(lái)npm start后便可調(diào)試本篇代碼.

var url = "http://localhost:10108/test",
    method = "POST";
js文件上傳

4.1) 封裝一個(gè)用于發(fā)送ajax請(qǐng)求的方法.

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ā)送一個(gè)post請(qǐng)求, 并且body屬性設(shè)置為formData即可. 遺憾的是, fetch無(wú)法跟蹤上傳的進(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才會(huì)自動(dòng)加上正確的Content-Type
  xhr: function(){
    var xhr = $.ajaxSettings.xhr();//實(shí)際上就是return new window.XMLHttpRequest()對(duì)象
    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, 請(qǐng)參考中文文檔 jQuery.ajax() | jQuery API 中文文檔 .

angular文件上傳

7.1) angular提供了$http方法用于發(fā)送http請(qǐng)求, 該方法返回一個(gè)promise對(duì)象.

$http({
  method: method,
  url: url,
  data: formData,
}).success(function(res) {
  console.log(res);
}).error(function(err, status) {
  console.log(err);
});

angular文件上傳的代碼已上傳至github, 請(qǐng)戳這里預(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以下低版本中, 請(qǐng)參考成熟的實(shí)現(xiàn)方案 angular-file-upload 以及它提供的demo Simple example .

ajax請(qǐng)求二進(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ā)生的錯(cuò)誤.
readyState 表示讀取文件的狀態(tài).默認(rèn)有三個(gè)值:0表示文件還沒有加載;1表示文件正在讀取;2表示文件讀取完成.
result 讀取的文件內(nèi)容.
abort() 取消文件讀取操作, 此時(shí)readyState屬性將置為2.
readAsArrayBuffer() 讀取文件(或blob對(duì)象)為類型化數(shù)組(ArrayBuffer), 類型化數(shù)組允許開發(fā)者以數(shù)組下標(biāo)的方式, 直接操作內(nèi)存, 由于數(shù)據(jù)以二進(jìn)制形式傳遞, 效率非常高.
readAsBinaryString() 讀取文件(或blob對(duì)象)為二進(jìn)制字符串, 該方法已移出標(biāo)準(zhǔn)api, 請(qǐng)謹(jǐn)慎使用.
readAsDataURL() 讀取文件(或blob對(duì)象)為base64編碼的URL字符串, 與window.URL.createObjectURL方法效果類似.
readAsText() 讀取文件(或blob對(duì)象)為文本字符串.
onload() 文件讀取完成時(shí)的事件回調(diào), 默認(rèn)傳入event事件對(duì)象. 該回調(diào)內(nèi), 可通過(guò)this.result 或 event.target.result獲取讀取的文件內(nèi)容.
ajax請(qǐng)求二進(jìn)制圖片并預(yù)覽
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對(duì)象
    reader.onload = function(){
      img.src = this.result;
    }*/
    //方案三
    //img.src = url;//最簡(jiǎn)單方法
    document.body.appendChild(img);
  }
}
xhr.send();
ajax請(qǐng)求二進(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)制文件的讀取, 請(qǐng)移步這篇博客 HTML5新特性之文件和二進(jìn)制數(shù)據(jù)的操作 .

如何等待多個(gè)ajax請(qǐng)求完成

原生js可以使用ES6新增的Promise. ES6的Promise基于 Promises/A+ 規(guī)范(該部分 Fetch入門指南 一文也有提及).

這里先提供一個(gè)解析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方法. 該方法接受一個(gè)或多個(gè)Deferred對(duì)象作為參數(shù), 只有全部成功才調(diào)用resolved狀態(tài)的回調(diào)函數(shù), 但只要其中有一個(gè)失敗,就調(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)返回一個(gè)jqXHR對(duì)象, 可以直接進(jìn)行鏈?zhǔn)秸{(diào)用. then方法的回調(diào)中默認(rèn)傳入相應(yīng)的請(qǐng)求結(jié)果, 每個(gè)請(qǐng)求結(jié)果的都是數(shù)組, 數(shù)組中依次是responseText, 請(qǐng)求狀態(tài), 請(qǐng)求的jqXHR對(duì)象.

angular中可以借助 $q.all() 來(lái)實(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í)際上就是對(duì) Promise.all 的封裝.

ajax與history的兼容

ajax的一大痛點(diǎn)就是無(wú)法支持瀏覽器前進(jìn)和后退操作. 因此早期的Gmail 采用 iframe, 來(lái)模擬ajax的前進(jìn)和后退.

如今, H5普及, pjax大行其道. pajax 就是 ajax+history.pushState 組合的一種技術(shù). 使用它便可以無(wú)刷新通過(guò)瀏覽器前進(jìn)和后退來(lái)改變頁(yè)面內(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 .

pjax

pjax簡(jiǎn)單易用, 僅需要如下三個(gè)api:

history.pushState(obj, title, url) 表示往頁(yè)面history末尾新增一個(gè)歷史項(xiàng)(history entry), 此時(shí)history.length會(huì)+1.

history.replaceState(obj, title, url) 表示替換當(dāng)前歷史項(xiàng)為新的歷史項(xiàng). 此時(shí)history.length保持不變.

window.onpopstate 僅在瀏覽器前進(jìn)和后退時(shí)觸發(fā)(history.go(1), history.back() 及l(fā)ocation.href="xxx" 均會(huì)觸發(fā)), 此時(shí)可在history.state中拿到剛剛?cè)M(jìn)去的state, 即obj對(duì)象(其他數(shù)據(jù)類型亦可).

我們注意到, 首次進(jìn)入一個(gè)頁(yè)面, 此時(shí) history.length 值為1, history.state 為空. 如下:

1) 為了在onpopstate事件回調(diào)中每次都能拿到 history.state , 此時(shí)需要在頁(yè)面載入完成后, 自動(dòng)替換下當(dāng)前url.

history.replaceState("init", title, "xxx.html?state=0");

2) 每次發(fā)送ajax請(qǐng)求時(shí), 在請(qǐng)求完成后, 調(diào)用如下, 從而實(shí)現(xiàn)瀏覽器history往前進(jìn).

history.pushState("ajax請(qǐng)求相關(guān)參數(shù)", title, "xxx.html?state=標(biāo)識(shí)符");

3) 瀏覽器前進(jìn)和后退時(shí), popstate 事件會(huì)自動(dòng)觸發(fā), 此時(shí)我們手動(dòng)取出 history.state , 構(gòu)建參數(shù)并重新發(fā)送ajax請(qǐng)求或者直接取用state值, 從而實(shí)現(xiàn)無(wú)刷新還原頁(yè)面.

window.addEventListener("popstate", function(e) {
    var currentState = history.state;
    //TODO 拼接ajax請(qǐng)求參數(shù)并重新發(fā)送ajax請(qǐng)求, 從而回到歷史頁(yè)面
      //TODO 或者從state中拿到關(guān)鍵值直接還原歷史頁(yè)面
});

popstate 事件觸發(fā)時(shí), 默認(rèn)會(huì)傳入 PopStateEvent 事件對(duì)象. 該對(duì)象具有如下屬性.

如有不懂, 更詳細(xì)講解請(qǐng)移步 : ajax與HTML5 history pushState/replaceState實(shí)例 ? 張?chǎng)涡?鑫空間-鑫生活 .

ajax緩存處理

js中的http緩存沒有開關(guān), 受制于瀏覽器http緩存策略. 原生xhr請(qǐng)求中, 可通過(guò)如下設(shè)置關(guān)閉緩存.

xhr.setRequestHeader("If-Modified-Since","0");
xhr.setRequestHeader("Cache-Control","no-cache");
//或者 URL 參數(shù)后加上  "?timestamp=" + new Date().getTime()

jquery的http緩存是否開啟可通過(guò)在settings中指定cache.

$.ajax({
  url : "url",
  dataType : "xml",
  cache: true,//true表示緩存開啟, false表示緩存不開啟
  success : function(xml, status){    
  }
});

同時(shí)jquery還可以全局設(shè)置是否緩存. 如下將全局關(guān)閉ajax緩存.

$.ajaxSetup({cache:false});

除此之外, 調(diào)試過(guò)程中出現(xiàn)的瀏覽器緩存尤為可惡. 建議開啟隱私瀏覽器或者勾選??控制臺(tái)的 Disable cache 選項(xiàng). (這里以Chrome舉例, 其他瀏覽器類似)

ajax的錯(cuò)誤處理

前面已經(jīng)提過(guò), 通常只要是ajax請(qǐng)求收到了http狀態(tài)碼, 便不會(huì)進(jìn)入到錯(cuò)誤捕獲里.(Chrome中407響應(yīng)頭除外)

實(shí)際上, $.ajax 方法略有區(qū)別, jquery的ajax方法還會(huì)在類型解析出錯(cuò)時(shí)觸發(fā)error回調(diào). 最常見的便是: dataType設(shè)置為json, 但是返回的data并非json格式, 此時(shí) $.ajax 的error回調(diào)便會(huì)觸發(fā).

ajax調(diào)試技巧

有關(guān)調(diào)試, 如果接口只是做小部分修改. 那么可以使用charles(Mac) 或者fiddler(Windows), 做代理, 將請(qǐng)求的資源替換為本地文件, 或者使用其斷點(diǎn)功能, 直接編輯response.

如果是新增接口的調(diào)試, 可以本地搭建node服務(wù). 利用hosts文件配置dns + nginx將http請(qǐng)求轉(zhuǎn)發(fā)到本地node服務(wù)器. 簡(jiǎn)易的node調(diào)試服務(wù)器可參考我的 node-webserver . 如下舉一個(gè)栗子?:

hosts+nginx+node-webserver

假設(shè)我們要調(diào)試的是 www.test.com 的GET接口. 以下所有步驟以Mac為例, 其他系統(tǒng), 請(qǐ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.同時(shí)servers目錄下*.conf文件已自動(dòng)加入到配置文件列表中
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保存并退出
#啟動(dòng)nginx
sudo nginx -s reload #如果啟動(dòng)了只需重啟即可
sudo nginx #如果沒有啟動(dòng),便啟動(dòng)之

3) node-webServer 配置

參考 node-webserver . 啟動(dòng)服務(wù)前只需更改index.js, 在第9行后插入如下內(nèi)容:

"get": {
  "/": {
      getKey : "Welcome to Simple Node  WebServer!"
  },
  "接口api": "你的response內(nèi)容"http://插入的代碼                               
},

如需在nginx中配置CORS, 請(qǐng)看這里: Nginx通過(guò)CORS實(shí)現(xiàn)跨域.

編碼問(wèn)題

XMLHttpRequest 返回的數(shù)據(jù)默認(rèn)的字符編碼是utf-8, post方法提交數(shù)據(jù)默認(rèn)的字符編碼也是utf-8. 若頁(yè)面編碼為gbk等中文編碼, 那么就會(huì)產(chǎn)生亂碼.

后端接口測(cè)試技巧

通常, 如果后端接口開發(fā)OK了, 前端同學(xué)需要通過(guò)一些手段來(lái)確認(rèn)接口是能正常訪問(wèn)的.

使用命令測(cè)試OPTIONS請(qǐng)求
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請(qǐng)求.

GET, POST 請(qǐng)求與GET類似, 其他請(qǐng)求亦然.

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 OK
postman

除此之外, 我們還可以通過(guò)chrome的postman擴(kuò)展進(jìn)行測(cè)試. 請(qǐng)看postman素潔的界面:

postman支持所有類型的http請(qǐng)求, 由于其向chrome申請(qǐng)了cookie訪問(wèn)權(quán)限及所有http(s)網(wǎng)站的訪問(wèn)權(quán)限. 因此可以放心使用它進(jìn)行各種網(wǎng)站api的測(cè)試.

同時(shí), 強(qiáng)烈建議閱讀本文的你升級(jí)postman的使用技巧, 這里有篇: 基于Postman的API自動(dòng)化測(cè)試 , 拿走不謝.

ajax移動(dòng)端兼容性

移動(dòng)端的支持性比較弱, 使用需謹(jǐn)慎. 看表.

  • IOS Safari Opera Mini Android Browser Android Chrome Android UC
    XMLHttpRequest 8.4 - 4.4.4 53

    文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

    轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/81998.html

    相關(guān)文章

    • 前端開發(fā)收集 - 收藏集 - 掘金

      摘要:責(zé)編現(xiàn)代化的方式開發(fā)一個(gè)圖片上傳工具前端掘金對(duì)于圖片上傳,大家一定不陌生。之深入事件機(jī)制前端掘金事件綁定的方式原生的事件綁定方式有幾種想必有很多朋友說(shuō)種目前,在本人目前的研究中,只有兩種半兩種半還有半種的且聽我道來(lái)。 Ajax 與數(shù)據(jù)傳輸 - 前端 - 掘金背景 在沒有ajax之前,前端與后臺(tái)傳數(shù)據(jù)都是靠表單傳輸,使用表單的方法傳輸數(shù)據(jù)有一個(gè)比較大的問(wèn)題就是每次提交數(shù)據(jù)都會(huì)刷新頁(yè)面,用...

      ygyooo 評(píng)論0 收藏0
    • Deep in JS - 收藏集 - 掘金

      摘要:今天同學(xué)去面試,做了兩道面試題全部做錯(cuò)了,發(fā)過(guò)來(lái)給道典型的面試題前端掘金在界中,開發(fā)人員的需求量一直居高不下。 排序算法 -- JavaScript 標(biāo)準(zhǔn)參考教程(alpha) - 前端 - 掘金來(lái)自《JavaScript 標(biāo)準(zhǔn)參考教程(alpha)》,by 阮一峰 目錄 冒泡排序 簡(jiǎn)介 算法實(shí)現(xiàn) 選擇排序 簡(jiǎn)介 算法實(shí)現(xiàn) ... 圖例詳解那道 setTimeout 與循環(huán)閉包的經(jīng)典面...

      enali 評(píng)論0 收藏0
    • javasscript - 收藏集 - 掘金

      摘要:跨域請(qǐng)求詳解從繁至簡(jiǎn)前端掘金什么是為什么要用是的一種使用模式,可用于解決主流瀏覽器的跨域數(shù)據(jù)訪問(wèn)的問(wèn)題。異步編程入門道典型的面試題前端掘金在界中,開發(fā)人員的需求量一直居高不下。 jsonp 跨域請(qǐng)求詳解——從繁至簡(jiǎn) - 前端 - 掘金什么是jsonp?為什么要用jsonp?JSONP(JSON with Padding)是JSON的一種使用模式,可用于解決主流瀏覽器的跨域數(shù)據(jù)訪問(wèn)的問(wèn)題...

      Rango 評(píng)論0 收藏0
    • 查漏補(bǔ)缺 - 收藏集 - 掘金

      摘要:醞釀許久之后,筆者準(zhǔn)備接下來(lái)撰寫前端面試題系列文章,內(nèi)容涵蓋瀏覽器框架分鐘搞定常用基礎(chǔ)知識(shí)前端掘金基礎(chǔ)智商劃重點(diǎn)在實(shí)際開發(fā)中,已經(jīng)非常普及了。 這道題--致敬各位10年阿里的前端開發(fā) - 掘金很巧合,我在認(rèn)識(shí)了兩位同是10年工作經(jīng)驗(yàn)的阿里前端開發(fā)小伙伴,不但要向前輩學(xué)習(xí),我有時(shí)候還會(huì)選擇另一種方法逗逗他們,拿了網(wǎng)上一道經(jīng)典面試題,可能我連去阿里面試的機(jī)會(huì)都沒有,但是我感受到了一次面試1...

      YuboonaZhang 評(píng)論0 收藏0
    • HTML-CSS

      摘要:但是,從字體上來(lái)說(shuō)雪碧圖制作,使用以及相關(guān),圖文。由于采用了編譯,所以能夠保證在瀏覽器不支持標(biāo)準(zhǔn)布局的情況下,回滾到舊版本的,保證移動(dòng)設(shè)備中能呈現(xiàn)出一樣的布局效果。我不想陷入和的紛爭(zhēng),但是有一件事是確定的極大的提升了移動(dòng)端 一勞永逸的搞定 flex 布局 尋根溯源話布局 一切都始于這樣一個(gè)問(wèn)題:怎樣通過(guò) CSS 簡(jiǎn)單而優(yōu)雅的實(shí)現(xiàn)水平、垂直同時(shí)居中。記得剛開始學(xué)習(xí) CSS 的時(shí)候,看到 ...

      xiaokai 評(píng)論0 收藏0

    發(fā)表評(píng)論

    0條評(píng)論

    最新活動(dòng)
    閱讀需要支付1元查看
      <button id="4u8wu"></button>
      <button id="4u8wu"><code id="4u8wu"></code></button>
      <noframes id="4u8wu"></noframes>
      <