瀏覽器在 XMLHttpRequest
類上定義了他們的 HTTP API。這個類的每個實例都表示一個獨立的請求/響應,而且這個對象的屬性和方法容許指定請求細節和提取響應數據。W3C在 XMLHttpRequest
規範的基礎上制定了2級 XMLHttpRequest
(下文簡稱 XHR2
)標準草案,且大部分瀏覽器都已經支持了。javascript
XMLHttpRequest
以前,我想先簡單說一下HTTP請求組成部分和響應的組成部分HTTP 請求的各部分有指定的順序:請求方法和URL首先到達,而後是請求頭,最後是主體。XHMLHttpRequest
實現一般直到調用send()方法纔開始啓動網絡。但 XMLHttpRequest
API的設計彷佛使每一個方法都將寫入網絡流。這意味着調用 XMLHttpRequest
上的方法的順序必須匹配 HTTP 請求的架構。例如,setRequestHeader() 方法的調用必須在調用 open() 以前可是在 send() 以後,不然就會跑出異常。下面我門會按照這個順序來介紹。java
使用 XMLHttpRequest
API的第一件事就是先實例化;程序員
var request = new XMLHttpRequest();
複製代碼
咱們也能重用已經存在的 XMLHttpRequest對象,可是這將會停止以前經過該對象發起的任何請求,但通常不會這麼用數據庫
request.open('POST', url);
複製代碼
第一個參數指定HTTP方法或動做,注意: 不區分大小寫,但一般都是使用大寫來匹配HTTP協議。json
第二個參數是URL,這個URL能夠是相對url也能夠是絕對的url。跨域
第三個參數用來設置請求的異步仍是同步瀏覽器
若是請求一個受保護的URL,把用戶和密碼做爲第4和第5個參數傳遞。緩存
request.setRequestheader('Content-Type': 'text/plain');
複製代碼
若是對相同的頭調用 setRequestHeader()
屢次,新值不會取代以前指定的值,相反,HTTP請求將包含這個頭的多個副本或這個頭將指定多個值。安全
setRequestHeader()
沒有辦法設置下面的 header
,XMLHttpRequest
將自動添加這些頭而防止僞造他們。相似的,XMLHttpRequest
對象自動處理 cookie
,鏈接時間,字符集和編碼判斷,因此沒法向 setRequestHeader()
傳遞這些頭:服務器
Accept-Charset | Accept-Encoding | Connection | Content-Length | Cookie |
Cookie2 | Content-Transfer-Encoding | Date | Expect | Host |
Keep-Live | Referer | TE | Trailer | Transfer-Encoding |
Upgrage | User-Agent | Via |
當 send()
方法傳入 XML
文檔時,沒有指定 Content-Type
,XMLHttpRequest
會自動設置一個合適的頭。相似的若是給 send()
傳入一個字符串但沒有指定 Content-Type
,那麼 XMLHttpRequest
將會自動添加 text/plain; charset=utf-8
頭。
當使用 GET
方法時,不須要調用 setRequestHeader()
這個方法,由於 GET
請求只能進行 url編碼(application/x-www-form-urlencoded)
,而若是使用 POST
方法且傳遞的參數是以 ‘&’ 和 ‘=’ 符號進行鍵值鏈接時,Content-Type
頭必須設置 application/x-www-form-urlencoded
。
使用 XMLHttpRequest
發起HTTP請求的最後一步就是指定可選的請求主體並向服務器發送它:
request.send(null);
複製代碼
setRequestHeader()
指定的 Content-type
頭。XMLHttpRequest
屬性readystatechange
XMLHttpRequest
對象一般異步使用:發送請求後,send()
方法當即返回,直到響應返回。爲了在響應準備就緒的時候獲得通知,必須監聽 XMLHttpRequest
對象上的 readystatechange
事件。
readyState
它是一個整數, 他指定了 HTTP
請求的狀態。
abort()
方法重置。open()
方法已調用,請求鏈接已經創建。可是 send()
方法未調用,請求數據未發送。send()
方法已調用,HTTP
請求已發送到 Web 服務器。接收到頭信息HTTP
響應已經徹底接收status
服務器返回的http狀態碼,當 readyState 小於 3 的時候讀取這一屬性會致使一個異常。
statusText
以數字和文字的形式返回 HTTP 狀態碼。
getRequestHeader()/getAllRequestHeaders()
使用這兩個方法均可以查詢到響應頭。XMLHttpRequest
會自動處理 cookie
,他會從 getAllRequestHeaders()
頭返回集中過濾掉 cookie
頭。而若是給 getRequestHeader()
傳遞 Set-Cookie
和 Set-Cookie2
則會返回 null
。
responseText
responseText
接受到服務器的相應數據 返回的值是一個json字符串 經過 JSON.parse(xhr.responseText)
能夠獲得數據對象
responseXML
responseXML屬性能夠獲得 XML 的 Document形式的數據。
XHR2規範定義了不少有用的事件集,在這個新的事件模型中,XMLHttpRequest
對象在請求的不一樣階段觸發不一樣類型的事件,因此它再也不須要檢查 readyState
屬性 。
onloadstart
: 當調用 send()
時,觸發單個 loadstart
事件
onprogress
: xhr對象會發生 progress
事件,一般每隔50ms左右觸發一次,因此可使用這個事件給用戶反饋請求的進度。若是請求快速完成,他可能不會觸發 progress
事件。注意這裏的 progress
是下載的進度,xhr2 額外的定義了上傳 upload
屬性,用來定義上傳的相關事件。
onload
: 當事件完成時,觸發 load
事件,load
事件的處理程序應該檢查xhr對象的 status
狀態碼來肯定收到的是 200 仍是 404
ontimeout
: 若是請求超時,會觸發 timeout
事件。
onerror
: 大多重定向這樣的網絡錯誤會阻止請求的完成,但這些狀況發生時會觸發 error
事件。
onabort
: 若是請求停止,會觸發 abort
事件。
onloadend
: 對於任何具體的請求,瀏覽器將只會觸發 load/abort/timeout/error
事件中的一個。一旦這些事件觸發之後,瀏覽器將會觸發 loadend
事件。
loaded
: 目前傳輸的字節數值total
: 傳輸的數據的總體長度(單位字節),Content-Length == total
,若是不知道內容的長度則 total == 0
;lengthComputable
: 若是知道內容的長度則 lengthComputable == true
, 不然 falsexhr.onprogress = function(event) {
if (event.lengthComputable) {
const progress = event.loaded / event.total * 100;
}
}
複製代碼
XHR2
新增的 upload
屬性XHR2
中新增了一個 upload
屬性,這個屬性值是一個對象,他定義了 addEventListener()
和 整個 progress
事件集合,好比說 onprogress
和 onload
。(但 upload
沒有定義 onreadystatechange
屬性,upload
僅能觸發新的事件類型)。
onloadstart
: 和 XMLHttpRequest
中的 loadstart
事件同樣。
onprogress
: 和 XMLHttpRequest
中的 progress
事件同樣。
onload
: 和 XMLHttpRequest
中的 load
事件同樣。
ontimeout
: 和 XMLHttpRequest
中的 timeout
事件同樣。
onerror
: 和 XMLHttpRequest
中的 error
事件同樣。
onabort
: 和 XMLHttpRequest
中的 abort
事件同樣。
onloadend
: 和 XMLHttpRequest
中的 loadend
事件同樣。
upload
屬性上定義的事件主要用在上傳文件時。咱們可使用 upload
上的 onloadstart,onprogress
分別監聽文件開始上傳和上傳過程當中進度的變化。
const input = document.getElementsByTagName('input')[0];
input.addEventListener('change', function() {
var file = this.files[0];
if (!file) return;
var xhr = new XMLHttpRequest();
xhr.open('POST', url);
xhr.send(file);
}, false);
複製代碼
文件類型是更普通的二進制大對象 Blob
類型中的一個字類型。XHR2
容許向 send()
方法傳入任何 Blob
對象。若是沒有顯示的設置 Content-Type
頭,這個 Blob
對象的 type
屬性用於設置待上傳的 Content-Type
頭。
XHR2
定義了新的 FormData
API, 它容易實現多部分請求主體。首先,使用 FormData()
構造函數建立 FormData
對象,而後按需屢次調用這個對象的 append()
方法把個體部分(string/File/Blob對象)
添加到請求中。最後把 FormData
對象傳遞給 send()
方法。send()
方法將對請求定義合適的邊界字符串和設置 Content-Type
頭。
經過調用 XMLHttpRequest
對象的 abort()
方法來取消正在進行的 HTTP
請求。當調用 abort()
方法後會觸發 xhr 對象的 onabort
事件。
XHR2
定義了 timeout
屬性來指定請求自動終止的毫秒數。同時也定義了 timeout
事件,當超時發生時觸發。
// 封裝一個request方法
const request = (url, formData, cb) => {
// 初始化
const xhr = new XMLHttpRequest();
// 定義請求的方法/動做和url
xhr.open('POST', url);
// 設置超時時間,單位是毫秒
xhr.timeout = 2000;
xhr.ontimeout = function() {
console.log('timeout');
};
// 開始上傳
xhr.upload.onloadstart = function() {
console.log('開始上傳');
};
// 上傳的進度
xhr.upload.onprogress = function(event) {
// 只有當 lengthComputable 爲true是,loaded 纔有值
if (event.lengthComputable) {
const value = Math.ceil((event.loaded / event.total) * 100);
cb && cb({
status: 'loading',
progress: value,
data: null,
});
}
};
// 監聽事件完成, 完成並不必定表明請求成功,因此須要判斷 status 狀態碼
xhr.onload = function() {
const resp: Response = {
status: 'success',
progress: 100,
data: null,
};
if (xhr.status === 200) {
resp.data = JSON.parse(xhr.responseText);
cb && cb(resp);
} else {
resp.status = 'error';
cb && cb(resp);
}
};
xhr.onerror = function() {
cb && cb({
status: 'error',
progress: 0,
data: null,
});
};
xhr.onabort = function() {
console.log('onabort');
};
xhr.onloadend = function() {
console.log('上傳結束');
};
xhr.send(formData);
return xhr;
};
複製代碼
做爲同源策略的一部分,XMLHttpRequest
對象一般僅能夠發起和文檔具備相同服務器的 HTTP
請求。這個限制關閉了安全裏漏洞,但同時也阻止了大量可以使用的跨域請求。好在 XHR2
經過在 HTTP
響應中選擇發送合適的 CORS(Cross-Origin Resource Sharing,跨域資源共享)
容許跨域訪問網站。在平常開發中使用跨域請求並不須要進行的額外的其餘設置,只要瀏覽器支持 CORS
跨域請求就行。 雖然實現 CORS
支持跨域的請求工做不須要作任務的事情,但有一些安全細節須要瞭解:
xhr.open()
方法傳入第四和第五個參數(用戶名和密碼)時,將不會經過跨域請求發送cookie
的。若是須要攜帶 cookie
,那麼能夠在調用 send()
方法以前設置 XMLHttpRequest
的withCredentials
屬性爲 true
CORS
跨域請求,能夠直接經過檢測 XMLHttpRequest
的 withCredentials
的屬性是否存在便可。XMLHttpRequest
的跨域請求一樣包含簡單請求和非簡單請求,非簡單請求又會進行預檢請求,具體 CORS
的相關知識能夠查看以前的分享的文章點擊這裏。XMLHttpRequest
API很是的好用,並且目前市面上的主瀏覽器也基本上都支持。相比 fetch
而言,兼容性確定是更勝一籌,惟一不足的是不支持 Promise
,可是這也難不倒咱們程序員,本身封裝一層就能夠了。更爲重要的是 XMLHttpRequest
支持超時設置和停止請求,還有進度事件,這些都是 'fetch' 所不具有的。