本文摘抄自:Ajax知識體系大梳理
地址:http://louiszhai.github.io/2016/11/02/ajax/
本文內容並不完整,請到原文閱讀。
if (window.XMLHttpRequest){ // If IE7, Mozilla, Safari, etc: Use native object var xmlHttp = new XMLHttpRequest() } else { if (window.ActiveXObject){ // …otherwise, use the ActiveX control for IE5.x and IE6 var xmlHttp = new ActiveXObject(「Microsoft.XMLHTTP」); } }
XMLHttpRequest對象建立。該方案爲msdn的方案。
全平臺兼容方案:
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; }
該方案爲網友提供的方案。css
onreadystatechange回調事件html
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回調中默認會傳入Event實例, 以下:前端
只讀屬性, status表示http請求的狀態, 初始值爲0. 若是服務器沒有顯式地指定狀態碼, 那麼status將被設置爲默認值, 即200.vue
只讀屬性, statusText表示服務器的響應狀態信息, 它是一個 UTF-16 的字符串, 請求成功且status==20X時, 返回大寫的OK
. 請求失敗時返回空字符串. 其餘狀況下返回相應的狀態描述. 好比: 301的 Moved Permanently
, 302的 Found
, 303的 See Other
, 307 的 Temporary Redirect
, 400的 Bad Request
, 401的 Unauthorized
等等.html5
ProgressEvent對象具備三個重要的Read only屬性.node
onprogress事件回調方法在 readyState==3
狀態時開始觸發, 默認傳入 ProgressEvent 對象, 可經過e.loaded/e.total
來計算加載資源的進度, 該方法用於獲取資源的下載進度.jquery
注意: 該方法適用於 IE10+ 及其餘現代瀏覽器.ios
xhr.onprogress = function(e){ console.log('progress:', e.loaded/e.total); }
onload事件回調方法在ajax請求成功後觸發, 觸發時機在 readyState==4
狀態以後.nginx
想要捕捉到一個ajax異步請求的成功狀態, 而且執行回調, 通常下面的語句就足夠了:git
xhr.onload = function(){ var s = xhr.status; if((s >= 200 && s < 300) || s == 304){ var resp = xhr.responseText; //TODO ... } }
ontimeout方法在ajax請求超時時觸發, 經過它能夠在ajax請求超時時作一些後續處理.
xhr.ontimeout = function(e) { console.error("請求超時!!!") }
均爲只讀屬性, response表示服務器的響應內容, 相應的, responseText表示服務器響應內容的文本形式.
只讀屬性, responseXML表示xml形式的響應數據, 缺省爲null, 若數據不是有效的xml, 則會報錯.
responseType表示響應的類型, 缺省爲空字符串, 可取 "arraybuffer"
, "blob"
, "document"
, "json"
, and"text"
共五種類型.
responseURL返回ajax請求最終的URL, 若是請求中存在重定向, 那麼responseURL表示重定向以後的URL.
withCredentials是一個布爾值, 默認爲false, 表示跨域請求中不發送cookies等信息. 當它設置爲true時, cookies
,authorization headers
或者TLS客戶端證書
均可以正常發送和接收. 顯然它的值對同域請求沒有影響.
注意: 該屬性適用於 IE10+, opera12+及其餘現代瀏覽器.
abort方法用於取消ajax請求, 取消後, readyState 狀態將被設置爲 0
(UNSENT
). 以下, 調用abort 方法後, 請求將被取消.
getResponseHeader方法用於獲取ajax響應頭中指定name的值. 若是response headers中存在相同的name, 那麼它們的值將自動以字符串的形式鏈接在一塊兒.
console.log(xhr.getResponseHeader('Content-Type'));//"text/html"
getAllResponseHeaders方法用於獲取全部安全的ajax響應頭, 響應頭以字符串形式返回. 每一個HTTP報頭名稱和值用冒號分隔, 如key:value, 並以\r\n結束.
xhr.onreadystatechange = function() { if(this.readyState == this.HEADERS_RECEIVED) { console.log(this.getAllResponseHeaders()); } } //Content-Type: text/html"
以上, readyState === 2
狀態時, 就意味着響應頭已接受完整. 此時即可以打印出完整的 response headers.
既然能夠獲取響應頭, 那麼天然也能夠設置請求頭, setRequestHeader就是幹這個的. 以下:
//指定請求的type爲json格式 xhr.setRequestHeader("Content-type", "application/json"); //除此以外, 還能夠設置其餘的請求頭 xhr.setRequestHeader('x-requested-with', '123456');
onerror方法用於在ajax請求出錯後執行. 一般只在網絡出現問題時或者ERR_CONNECTION_RESET時觸發(若是請求返回的是407狀態碼, chrome下也會觸發onerror).
upload屬性默認返回一個 XMLHttpRequestUpload
對象, 用於上傳資源. 該對象具備以下方法:
上述方法功能同 xhr 對象中同名方法一致. 其中, onprogress 事件回調方法可用於跟蹤資源上傳的進度.
xhr.upload.onprogress = function(e){ var percent = 100 * e.loaded / e.total |0; console.log('upload: ' + precent + '%'); }
overrideMimeType方法用於強制指定response 的 MIME 類型, 即強制修改response的 Content-Type
. 以下, 服務器返回的response的 MIME 類型爲 text/plain
.
1
2
|
xhr.getResponseHeader(
'Content-Type');//"text/plain"
xhr.responseXML;
//null
|
經過overrideMimeType方法將response的MIME類型設置爲 text/xml;charset=utf-8
, 以下所示:
1
2
|
xhr.overrideMimeType(
"text/xml; charset = utf-8");
xhr.send();
|
此時雖然 response headers 如上圖, 沒有變化, 但 Content-Type
已替換爲新值.
1
|
xhr.getResponseHeader(
'Content-Type');//"text/xml; charset = utf-8"
|
此時, xhr.responseXML
也將返回DOM對象, 以下圖.
XHR1 即 XMLHttpRequest Level 1. XHR1時, xhr對象具備以下缺點:
同源策略
限制, 只能請求同域資源.XHR2 即 XMLHttpRequest Level 2. XHR2針對XHR1的上述缺點作了以下改進:
xhr.upload.onprogress
事件回調方法獲取傳輸進度.同源策略
限制, 這個安全機制不會變. XHR2新提供 Access-Control-Allow-Origin
等headers, 設置爲 *
時表示容許任何域名請求, 從而實現跨域CORS訪問(有關CORS詳細介紹請耐心往下讀).這裏就H5新增的FormData對象舉個例.
//可直接建立FormData實例 var data = new FormData(); data.append("name", "louis"); xhr.send(data); //還能夠經過傳入表單DOM對象來建立FormData實例 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用戶只能哭暈在廁所了.
XDomainRequest 對象是IE8,9折騰出來的, 用於支持CORS請求非成熟的解決方案. 以致於IE10中直接移除了它, 並從新回到了 XMLHttpRequest 的懷抱.
XDomainRequest 僅可用於發送 GET
和 POST
請求. 以下即建立過程.
var xdr = new XDomainRequest();
xdr具備以下屬性:
以下方法:
以下事件回調:
除了缺乏一些方法外, XDomainRequest 基本上就和 XMLHttpRequest 的使用方式保持一致.
必需要明確的是:
$.ajax是jquery對原生ajax的一次封裝. 經過封裝ajax, jquery抹平了不一樣版本瀏覽器異步http的差別性, 取而代之的是高度統一的api. jquery做爲js類庫時代的先驅, 對前端發展有着深遠的影響. 瞭解並熟悉其ajax方法, 不可謂不重要.
$.ajax() 只有一個參數, 該參數爲key-value設置對象. 實際上, jq發送的全部ajax請求, 都是經過調用該ajax方法實現的. 它的詳細參數以下表:
序號 | 參數 | 類型 | 描述 |
---|---|---|---|
1 | accepts | PlainObject | 用於通知服務器該請求須要接收何種類型的返回結果. 若有必要, 推薦在$.ajaxSetup() 方法中設置一次. |
2 | async | Boolean | 默認爲true, 即異步. |
3 | beforeSend | Function | 請求發送前的回調, 默認傳入參數jqXHR和settings. 函數內顯式返回false將取消本次請求. |
4 | cache | Boolean | 請求是否開啓緩存, 默認爲true, 如不須要緩存請設置爲false. 不過, dataType爲」script」和」jsonp」時默認爲false. |
5 | complete | Function | 請求完成後的回調(請求success 和 error 以後均調用), 默認傳入參數jqXHR和textStatus(請求狀態, 取值爲 「success」,」notmodified」,」error」,」timeout」,」abort」,」parsererror」之一). 從jq1.5開始, complete能夠設置爲一個包含函數的數組. 如此每一個函數將依次被調用. |
6 | contents | PlainObject | 一個以」{字符串/正則表達式}」配對的對象, 根據給定的內容類型, 解析請求的返回結果. |
7 | contentType | String | 編碼類型, 相對應於http請求頭域的」Content-Type」字段. 默認值爲」application/x-www-form-urlencoded; charset=UTF-8」. |
8 | context | Object | 設置ajax回調函數的上下文. 默認上下文爲ajax請求傳入的參數設置對象. 如設置爲document.body, 那麼全部ajax回調函數中將以body爲上下文. |
9 | converters | PlainObject | 一個數據類型到數據類型轉換器的對象. 默認爲 {"* text": window.String, "text html": true, "text json": jQuery.parseJSON, "text xml": jQuery.parseXML} . 如設置converters:{"json jsonp": function(msg){}} |
10 | crossDomain | Boolean | 默認同域請求爲false, 跨域請求爲true. |
11 | data | Object, Array | 發送到服務器的數據, 默認data爲鍵值對格式對象, 若data爲數組則按照traditional 參數的值, 自動轉化爲一個同名的多值查詢字符串. 如{a:1,b:2}將轉換爲」&a=1&b=2」. |
12 | dataFilter | Function | 處理XMLHttpRequest原始響應數據的回調, 默認傳入data和type參數, data是Ajax返回的原始數據, type是調用$.ajax時提供的dataType參數 |
13 | dataType | String | 預期服務器返回的數據類型, 可設置爲」xml」,」html」,」script」,」json」,」jsonp」,」text」之一, 其中設置爲」xml」或」text」類型時, 數據不會通過處理. |
14 | error | Function | 請求失敗時的回調函數, 默認傳入jqXHR(jq1.4之前爲原生xhr對象),textStatus(請求狀態,取值爲null,」timeout」,」error」,」abort」 或 「parsererror」),errorString(錯誤內容), 當一個HTTP錯誤發生時, errorThrown 接收HTTP狀態的文本部分,好比」Not Found」等. 從jq1.5開始, error能夠設置爲一個包含函數的數組. 如此每一個函數將依次被調用.注意: 跨域腳本和JSONP請求時error不被調用. |
15 | global | Boolean | 表示是否觸發全局ajax事件, 默認爲true. 設爲false將再也不觸發ajaxStart,ajaxStop,ajaxSend,ajaxError等. 跨站腳本和jsonp請求, 該值自動設置爲false. |
16 | headers | PlainObject | 設置請求頭, 格式爲k-v鍵值對對象. 因爲該設置會在beforeSend函數被調用以前生效, 所以可在beforeSend函數內覆蓋該對象. |
17 | ifModified | Boolean | 只有上次請求響應改變時, 才容許請求成功. 它使用HTTP包的Last-Modified 頭信息判斷, 默認爲false. 若設置爲true, 且數據自從上次請求後沒有更改過就會報錯. |
18 | isLocal | Boolean | 運行當前環境設置爲」本地」,默認爲false, 若設置爲true, 將影響請求發送時的協議. |
19 | jsonp | String | 顯式指定jsonp請求中的回調函數的名稱. 如jsonp:cb, jq會將cb代替callback, 以 「cb=?」傳給服務器. 從jq1.5開始, 若設置jsonp:false, 那麼須要明確設置jsonpCallback:」callbackName」. |
20 | jsonpCallback | String,Function | 爲jsonp請求指定一個回調函數名, 以取代jq自動生成的隨機函數名. 從jq1.5開始, 能夠將該屬性設置爲一個函數, 函數的返回值就是jsonpCallback的結果. |
21 | mimeType | String | 設置一個MIME類型, 以覆蓋xhr的MIM類型(jq1.5新增) |
22 | password | String | 設置認證請求中的密碼 |
23 | processData | Boolean | jq的ajax方法默認會將傳入的data隱式轉換爲查詢字符串(如」&a=1&b=2」), 以配合 默認內容類型 「application/x-www-form-urlencoded」, 若是不但願轉換請設置爲false. angular中想要禁用默認轉換, 須要重寫transformRequest方法. |
24 | scriptCharset | String | 僅在」script」請求中使用(如跨域jsonp, dataType爲」script」類型). 顯式指定時, 請求中將在script標籤上設置charset 屬性, 可在發現本地和遠程編碼不一致時使用. |
25 | statusCode | PlainObject | 一組http狀態碼和回調函數對應的鍵值對對象. 該對象以 {404:function(){}} 這種形式表示. 可用於根據不一樣的http狀態碼, 執行不一樣的回調.(jq1.5新增) |
26 | timeout | Number | 設置超時時間. |
27 | traditional | Boolean | 是否按照默認方式序列化data對象, 默認值爲false. |
28 | type | String | 能夠設置爲8種http method之一, jq中不區分大小寫. |
29 | url | String | 請求的uri地址. |
30 | username | String | 設置認證請求中的用戶名 |
31 | xhr | Function | 在回調內建立並返回xhr對象 |
32 | xhrFields | PlainObject | 鍵值對對象, 用於設置原生的xhr對象, 如可用來設置withCredentials:true(jq1.5.1新增) |
$.ajax() 方法返回jqXHR對象(jq1.5起), 若是使用的不是XMLHttpRequest對象時, 如jsonp請求, 返回的jqXHR對象將盡量模擬原生的xhr. 從jq1.5起, 返回的jqXHR對象實現了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隊列能夠分配多個回調.
$.ajax() 的轉換器能夠將支持的數據類型映射到其它數據類型. 若是須要將自定義數據類型映射到已知的類型. 須要使用contents
選項在響應的 「Content-Type」 和實際數據類型之間添加一個轉換函數.
1
2
3
4
5
6
7
8
9
10
11
|
$.ajaxSetup({
contents: {
myContentType: /myContentType/
},
converters: {
"myContentType json": function(data) {
//TODO something
return newData;
}
}
});
|
轉換一個支持的類型爲自定義類型, 而後再返回. 如 text—>myContentType—>json.
1
2
3
4
5
6
7
8
9
10
11
12
|
$.ajaxSetup({
contents: {
myContentType: /myContentType/
},
converters: {
"text myContentType": true,
"myContentType json": function(data) {
//TODO something
return newData;
}
}
});
|
$.ajax()方法觸發的事件紛繁複雜, 有將近20個之多. 爲了囊括最多的事件, 這裏以一次成功的上傳請求爲例, 如下是它們的調用順序(請求出現錯誤時的順序, 請自行對應).
序號 | 事件名稱 | 是否全局事件 | 是否能關閉 | 默認形參 |
---|---|---|---|---|
1 | $.ajaxPrefilter | ✔️ | ❌ | function(options, originalOptions, jqXHR){} |
2 | $(document).ajaxStar | ✔️ | ✔️ | function(){}(只在當前無激活ajax時觸發) |
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起, 對於函數 ajaxStart
, ajaxSend
, ajaxSuccess
, ajaxComplete
, ajaxStop
, 只能爲document
對象綁定事件處理函數, 爲其餘元素綁定的事件處理函數不會起做用.
實際上, 若是你僅僅只是想要一個不錯的http庫, 相比於龐大臃腫的jquery, 短小精悍的Axios可能更加適合你. 緣由以下:
「最近團隊討論了一下, Ajax 自己跟 Vue 並無什麼須要特別整合的地方, 使用 fetch polyfill 或是 axios、superagent 等等均可以起到同等的效果, vue-resource 提供的價值和其維護成本相比並不划算, 因此決定在不久之後取消對 vue-resource 的官方推薦.」
語法上Axios基本就和promise同樣, 在then方法中處理回調, 在catch方法中處理異常. 以下:
1
2
3
4
5
6
7
|
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 .
如需在網頁上引入 Axios, 能夠連接CDN axios | Bootstrap中文網開源項目免費 CDN 服務 或者將其下載到本地.
說到ajax, 就不得不說起fetch, 因爲篇幅較長, fetch已從本文中獨立出來, 請戳 Fetch進階指南 .
CORS是一個W3C(World Wide Web)標準, 全稱是跨域資源共享(Cross-origin resource sharing).它容許瀏覽器向跨域服務器, 發出異步http請求, 從而克服了ajax受同源策略的限制. 實際上, 瀏覽器不會攔截不合法的跨域請求, 而是攔截了他們的響應, 所以即便請求不合法, 不少時候, 服務器依然收到了請求.(Chrome和Firefox下https網站不容許發送http異步請求除外)
一般, 一次跨域訪問擁有以下流程:
當前幾乎全部的桌面瀏覽器(Internet Explorer 8+, Firefox 3.5+, Safari 4+和 Chrome 3+)均可經過名爲跨域資源共享的協議支持ajax跨域調用.
那麼移動端兼容性又如何呢? 請看下圖:
可見, CORS的技術在IOS Safari7.1及Android webview2.3中就早已支持, 即便低版本下webview的canvas在使用跨域的video或圖片時會有問題, 也絲絕不影響CORS的在移動端的使用. 至此, 咱們就能夠放心大膽的去應用CORS了.
1) HTTP Response Header(服務器提供):
Access-Control-Allow-Origin: 指定容許哪些源的網頁發送請求.
Access-Control-Allow-Credentials: 指定是否容許cookie發送.
Access-Control-Allow-Methods: 指定容許哪些請求方法.
Access-Control-Allow-Headers: 指定容許哪些常規的頭域字段, 好比說 Content-Type.
Access-Control-Expose-Headers: 指定容許哪些額外的頭域字段, 好比說 X-Custom-Header.
該字段可省略. CORS請求時, xhr.getResponseHeader() 方法默認只能獲取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請求默認自帶):
3) 如下全部的header name 是被拒絕的:
Proxy-
或 Sec-
開頭的header nameCORS請求分爲兩種, ① 簡單請求; ② 非簡單請求.
知足以下兩個條件即是簡單請求, 反之則爲非簡單請求.(CORS請求部分摘自阮一峯老師博客)
1) 請求是如下三種之一:
2) http頭域不超出如下幾種字段:
application/x-www-form-urlencoded
、multipart/form-data
、text/plain
對於簡單請求, 瀏覽器將發送一次http請求, 同時在Request頭域中增長 Origin
字段, 用來標示請求發起的源, 服務器根據這個源採起不一樣的響應策略. 若服務器認爲該請求合法, 那麼須要往返回的 HTTP Response 中添加 Access-Control-*
等字段.( Access-Control-*
相關字段解析請閱讀我以前寫的CORS 跨域訪問 )
對於非簡單請求, 好比Method爲POST
且Content-Type值爲 application/json
的請求或者Method爲 PUT
或DELETE
的請求, 瀏覽器將發送兩次http請求. 第一次爲preflight預檢(Method: OPTIONS),主要驗證來源是否合法. 值得注意的是:OPTION請求響應頭一樣須要包含 Access-Control-*
字段等. 第二次纔是真正的HTTP請求. 因此服務器必須處理OPTIONS應答(一般須要返回20X的狀態碼, 不然xhr.onerror事件將被觸發).
以上請求流程圖爲:
http-equiv 至關於http的響應頭, 它迴應給瀏覽器一些有用的信息,以幫助正確和精確地顯示網頁內容. 以下html將容許任意域名下的網頁跨域訪問.
1
|
<meta http-equiv="Access-Control-Allow-Origin" content="*">
|
一般, 圖片容許跨域訪問, 也能夠在canvas中使用跨域的圖片, 但這樣作會污染畫布, 一旦畫布受污染, 將沒法讀取其數據. 好比沒法調用 toBlob(), toDataURL() 或 getImageData()方法. 瀏覽器的這種安全機制規避了未經許可的遠程服務器圖片被濫用的風險.(該部份內容摘自 啓用了 CORS 的圖片 - HTML(超文本標記語言) | MDN)
所以如需在canvas中使用跨域的圖片資源, 請參考以下apache配置片斷(來自HTML5 Boilerplate Apache server configs).
1
2
3
4
5
6
7
8
|
<IfModule mod_setenvif.c>
<IfModule mod_headers.c>
<FilesMatch "\.(cur|gif|ico|jpe?g|png|svgz?|webp)$">
SetEnvIf Origin ":" IS_CORS
Header set Access-Control-Allow-Origin "*" env=IS_CORS
</FilesMatch>
</IfModule>
</IfModule>
|
ajax實現文件上傳很是簡單, 這裏我選取原生js, jq, angular 分別來比較下, 並順便聊聊使用它們時的注意事項.(ajax文件上傳的代碼已上傳至github, 請戳這裏預覽效果: ajax 文件上傳 demo | louis)
1) 爲了上傳文件, 咱們得先選中一個文件. 一個type爲file的input框就夠了.
1
|
<input id="input" type="file">
|
2) 而後用FormData對象包裹📦選中的文件.
1
2
3
|
var input = document.getElementById("input"),
formData =
new FormData();
formData.append(
"file",input.files[0]);//key能夠隨意定義,只要後臺能理解就行
|
3) 定義上傳的URL, 以及方法. github上我搭建了一個 node-webserver, 根據須要能夠自行克隆下來npm start後即可調試本篇代碼.
1
2
|
var url = "http://localhost:10108/test",
method =
"POST";
|
4.1) 封裝一個用於發送ajax請求的方法.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
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) 上傳文件並綁定事件.
1
2
3
4
5
6
7
8
9
10
|
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.");
}
|
上傳結果以下所示:
5) fetch只要發送一個post請求, 而且body屬性設置爲formData便可. 遺憾的是, fetch沒法跟蹤上傳的進度信息.
1
2
3
4
5
6
7
8
|
fetch(url, {
method: method,
body: formData
}).then(
function(res){
console.log(res);
}).catch(
function(e){
console.log(e);
});
|
jq提供了各式各樣的上傳插件, 其原理都是利用jq自身的ajax方法.
6) jq的ajax提供了xhr屬性用於自定義各類事件.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
$.ajax({
type: method,
url: url,
data: formData,
processData : false,
contentType : false ,//必須false纔會自動加上正確的Content-Type
xhr:
function(){
var xhr = $.ajaxSettings.xhr();//實際上就是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上傳結果以下所示:
有關jq ajax更多的api, 請參考中文文檔 jQuery.ajax() | jQuery API 中文文檔 .
7.1) angular提供了$http方法用於發送http請求, 該方法返回一個promise對象.
1
2
3
4
5
6
7
8
9
|
$http({
method: method,
url: url,
data: formData,
}).success(
function(res) {
console.log(res);
}).error(
function(err, status) {
console.log(err);
});
|
angular文件上傳的代碼已上傳至github, 請戳這裏預覽效果: angular 文件上傳 demo | louis.
低版本angular中文件上傳的功能並不完整, 直到angular1.5.5纔在$http中加入了eventHandler和uploadEventHandlers等方法, 使得它支持上傳進度信息. 以下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
$http({
method: method,
url: url,
eventHandlers: {
progress: function(c) {//下載進度
console.log('Progress -> ' + c);
}
},
uploadEventHandlers: {
progress: function(e) {//上傳進度
console.log('UploadProgress -> ' + e);
}
},
data: formData,
}).success(
function(res) {
console.log(res);
}).error(
function(err, status) {
console.log(err);
});
|
angular1.5.5如下低版本中, 請參考成熟的實現方案 angular-file-upload 以及它提供的demo Simple example .
處理二進制文件主要使用的是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 | 表示讀取文件期間發生的錯誤. |
readyState | 表示讀取文件的狀態.默認有三個值:0表示文件尚未加載;1表示文件正在讀取;2表示文件讀取完成. |
result | 讀取的文件內容. |
abort() | 取消文件讀取操做, 此時readyState 屬性將置爲2. |
readAsArrayBuffer() | 讀取文件(或blob對象)爲類型化數組(ArrayBuffer), 類型化數組容許開發者以數組下標的方式, 直接操做內存, 因爲數據以二進制形式傳遞, 效率很是高. |
讀取文件(或blob對象)爲二進制字符串, 該方法已移出標準api, 請謹慎使用. | |
readAsDataURL() | 讀取文件(或blob對象)爲base64編碼的URL字符串, 與window.URL.createObjectURL方法效果相似. |
readAsText() | 讀取文件(或blob對象)爲文本字符串. |
onload() | 文件讀取完成時的事件回調, 默認傳入event事件對象. 該回調內, 可經過this.result 或 event.target.result獲取讀取的文件內容. |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
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依然佔據着內存
img.onload =
function() {
window.URL.revokeObjectURL(img.src);//釋放內存
};
//方案二
/*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();
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
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);
//該方法已被移出標準api,建議使用reader.readAsText(blob);
reader.onload=
function(){
document.body.innerHTML = "<div>" + this.result + "</div>";
}
}
}
xhr.send();
|
有關二進制文件的讀取, 請移步這篇博客 HTML5新特性之文件和二進制數據的操做 .
原生js可使用ES6新增的Promise. ES6的Promise基於 Promises/A+ 規範(該部分 Fetch入門指南 一文也有說起).
這裏先提供一個解析responses的函數.
1
2
3
4
5
6
7
|
function todo(responses){
responses.forEach(
function(response){
response.json().then(
function(res){
console.log(res);
});
});
}
|
原生js使用 Promise.all
方法. 以下:
1
2
3
4
5
6
7
8
|
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對象做爲參數, 只有所有成功才調用resolved狀態的回調函數, 但只要其中有一個失敗,就調用rejected狀態的回調函數. 其實, jq的Deferred是基於 Promises/A規範實現, 但並不是徹底遵循. (傳送門: jQuery 中的 Deferred 和 Promises (2) ).
1
2
3
4
5
6
7
|
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默認返回一個jqXHR對象, 能夠直接進行鏈式調用. then方法的回調中默認傳入相應的請求結果, 每一個請求結果的都是數組, 數組中依次是responseText, 請求狀態, 請求的jqXHR對象.
angular中能夠藉助 $q.all()
來實現. 別忘了, $q
須要在controller中注入. 此外, $q
相關講解可參考 AngularJS: ng.$q 或 Angular $q service學習筆記 .
1
2
3
4
5
6
7
8
|
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()
實際上就是對 Promise.all
的封裝.
ajax的一大痛點就是沒法支持瀏覽器前進和後退操做. 所以早期的Gmail 採用 iframe, 來模擬ajax的前進和後退.
現在, H5普及, pjax大行其道. pajax 就是 ajax+history.pushState 組合的一種技術. 使用它即可以無刷新經過瀏覽器前進和後退來改變頁面內容.
先看下兼容性.
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簡單易用, 僅須要以下三個api:
咱們注意到, 首次進入一個頁面, 此時 history.length
值爲1, history.state
爲空. 以下:
1) 爲了在onpopstate事件回調中每次都能拿到 history.state
, 此時須要在頁面載入完成後, 自動替換下當前url.
1
|
history.replaceState(
"init", title, "xxx.html?state=0");
|
2) 每次發送ajax請求時, 在請求完成後, 調用以下, 從而實現瀏覽器history往前進.
1
|
history.pushState(
"ajax請求相關參數", title, "xxx.html?state=標識符");
|
3) 瀏覽器前進和後退時, popstate
事件會自動觸發, 此時咱們手動取出 history.state
, 構建參數並從新發送ajax請求或者直接取用state值, 從而實現無刷新還原頁面.
1
2
3
4
5
|
window.addEventListener("popstate", function(e) {
var currentState = history.state;
//TODO 拼接ajax請求參數並從新發送ajax請求, 從而回到歷史頁面
//TODO 或者從state中拿到關鍵值直接還原歷史頁面
});
|
popstate
事件觸發時, 默認會傳入 PopStateEvent
事件對象. 該對象具備以下屬性.
若有不懂, 更詳細講解請移步 : ajax與HTML5 history pushState/replaceState實例 « 張鑫旭-鑫空間-鑫生活 .
js中的http緩存沒有開關, 受制於瀏覽器http緩存策略. 原生xhr請求中, 可經過以下設置關閉緩存.
1
2
3
|
xhr.setRequestHeader(
"If-Modified-Since","0");
xhr.setRequestHeader(
"Cache-Control","no-cache");
//或者 URL 參數後加上 "?timestamp=" + new Date().getTime()
|
jquery的http緩存是否開啓可經過在settings中指定cache.
1
2
3
4
5
6
7
|
$.ajax({
url : 'url',
dataType : "xml",
cache: true,//true表示緩存開啓, false表示緩存不開啓
success :
function(xml, status){
}
});
|
同時jquery還能夠全局設置是否緩存. 以下將全局關閉ajax緩存.
1
|
$.ajaxSetup({
cache:false});
|
除此以外, 調試過程當中出現的瀏覽器緩存尤其可惡. 建議開啓隱私瀏覽器或者勾選☑️控制檯的 Disable cache
選項. (這裏以Chrome舉例, 其餘瀏覽器相似)
前面已經提過, 一般只要是ajax請求收到了http狀態碼, 便不會進入到錯誤捕獲裏.(Chrome中407響應頭除外)
實際上, $.ajax
方法略有區別, jquery的ajax方法還會在類型解析出錯時觸發error回調. 最多見的即是: dataType設置爲json, 可是返回的data並不是json格式, 此時 $.ajax
的error回調便會觸發.
有關調試, 若是接口只是作小部分修改. 那麼可使用charles(Mac) 或者fiddler(Windows), 作代理, 將請求的資源替換爲本地文件, 或者使用其斷點功能, 直接編輯response.
若是是新增接口的調試, 能夠本地搭建node服務. 利用hosts文件配置dns + nginx將http請求轉發到本地node服務器. 簡易的node調試服務器可參考個人 node-webserver . 以下舉一個栗子🌰:
假設咱們要調試的是 www.test.com 的GET接口. 如下全部步驟以Mac爲例, 其餘系統, 請自行搜索🔍文件路徑.
1) hosts配置.
1
2
|
sudo vim /etc/hosts
#新增一行 127.0.0.1 www.test.com
|
2) nginx 配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
brew install nginx
#安裝
#安裝成功後進入目標目錄
cd /usr/local/etc/nginx/
cd servers #默認配置入口爲nginx.conf.同時servers目錄下*.conf文件已自動加入到配置文件列表中
vim test.conf
#粘貼以下內容
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 . 啓動服務前只需更改index.js, 在第9行後插入以下內容:
1
2
3
4
5
6
|
'get': {
'/': {
getKey : 'Welcome to Simple Node WebServer!'
},
'接口api': '你的response內容'//插入的代碼
},
|
如需在nginx中配置CORS, 請看這裏: Nginx經過CORS實現跨域.
XMLHttpRequest 返回的數據默認的字符編碼是utf-8, post方法提交數據默認的字符編碼也是utf-8. 若頁面編碼爲gbk等中文編碼, 那麼就會產生亂碼.
一般, 若是後端接口開發OK了, 前端同窗須要經過一些手段來確認接口是能正常訪問的.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
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狀態碼爲200, 表示容許OPTIONS請求.
GET, POST 請求與GET相似, 其餘請求亦然.
1
2
3
4
|
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
|
除此以外, 咱們還能夠經過chrome的postman擴展進行測試. 請看postman素潔的界面:
postman支持全部類型的http請求, 因爲其向chrome申請了cookie訪問權限及全部http(s)網站的訪問權限. 所以能夠放心使用它進行各類網站api的測試.
同時, 強烈建議閱讀本文的你升級postman的使用技巧, 這裏有篇: 基於Postman的API自動化測試 , 拿走不謝.
移動端的支持性比較弱, 使用需謹慎. 看錶.
IOS Safari | Opera Mini | Android Browser | Android Chrome | Android UC | |
---|---|---|---|---|---|
XMLHttpRequest | 8.4 | - | 4.4.4 | 53 | 11(part) |
fetch | - | - | 52 | 53 | - |
本篇爲ajax而生, 通篇介紹 XMLHTTPRequest 相關的知識, 力求簡明, 本欲爲梳理知識, 爲讀者答疑解惑, 但因本人理解所限, 不免有所侷限, 但願正在閱讀的你取其精華去其糟粕. 謝謝.
本文就討論這麼多內容,你們有什麼問題或好的想法歡迎在下方參與留言和評論.
本文做者: louis
本文連接: http://louiszhai.github.io/2016/11/02/ajax/
參考文章