fetch 是 XMLHttpRequest 的升級版,使用js指令碼發出網路請求,但是與 XMLHttpRequest 不同的是,fetch 方式使用 Promise,相比 XMLHttpRequest 更加簡潔。所以我們告別XMLHttpRequest,引入 fetch 如何使用?
一、fetch介紹
fetch() 是一個全域性方法,提供一種簡單,合理的方式跨網路獲取資源。它的請求是基於 Promise 的,需要詳細學習 Promise ,請點選《 Promise詳解 》。它是專門為了取代傳統的 xhr 而生的。
1.1、fetch使用語法
fetch(url,options).then((response)=>{
//處理http響應
},(error)=>{
//處理錯誤
})
url :是傳送網路請求的地址。
options:傳送請求引數,
- body - http請求引數
- mode - 指定請求模式。預設值為cros:允許跨域;same-origin:只允許同源請求;no-cros:只限於get、post和head,並且只能使用有限的幾個簡單標頭。
- cache - 使用者指定快取。
- method - 請求方法,預設GET
- signal - 用於取消 fetch
- headers - http請求頭設定
- keepalive - 用於頁面解除安裝時,告訴瀏覽器在後臺保持連線,繼續傳送資料。
- credentials - cookie設定,預設omit,忽略不帶cookie,same-origin同源請求帶cookie,inclue無論跨域還是同源都會帶cookie。
1.2、response 物件
fetch 請求成功後,響應 response 物件如圖:
- status - http狀態碼,範圍在100-599之間
- statusText - 伺服器返回狀態文字描述
- ok - 返回布林值,如果狀態碼2開頭的,則true,反之false
- headers - 響應頭
- body - 響應體。響應體內的資料,根據型別各自處理。
- type - 返回請求型別。
- redirected - 返回布林值,表示是否發生過跳轉。
1.3、讀取內容方法
response 物件根據伺服器返回的不同型別資料,提供了不同的讀取方法。分別有:
- response.text() -- 得到文字字串
- response.json() - 得到 json 物件
- response.blob() - 得到二進位制 blob 物件
- response.formData() - 得到 fromData 表單物件
- response.arrayBuffer() - 得到二進位制 arrayBuffer 物件
上述 5 個方法,返回的都是 promise 物件,必須等到非同步操作結束,才能得到伺服器返回的完整資料。
1.4、response.clone()
stream 物件只能讀取一次,讀取完就沒了,這意味著,上邊的五種讀取方法,只能使用一個,否則會報錯。
因此 response 物件提供了 clone() 方法,建立 respons 物件副本,實現多次讀取。如下:將一張圖片,讀取兩次:
const response1 = await fetch('flowers.jpg'); const response2 = response1.clone(); const myBlob1 = await response1.blob(); const myBlob2 = await response2.blob(); image1.src = URL.createObjectURL(myBlob1); image2.src = URL.createObjectURL(myBlob2);
1.5、response.body()
body 屬性返回一個 ReadableStream 物件,供使用者操作,可以用來分塊讀取內容,顯示下載的進度就是其中一種應用。
const response = await fetch('flower.jpg'); const reader = response.body.getReader(); while(true) { const {done, value} = await reader.read(); if (done) { break; } console.log(`Received ${value.length} bytes`) }
response.body.getReader() 返回一個遍歷器,這個遍歷器 read() 方法每次都會返回一個物件,表示本次讀取的內容塊。
二、請求時 POST 和 GET 分別處理
請求方式不同,傳值方式也不同。xhr 會分別處理 get 和 post 資料傳輸,還有請求頭設定,同樣 fetch 也需要分別處理。
2.1、get 方式
只需要在url中加入傳輸資料,options中加入請求方式。如下面程式碼所示:
<input type="text" id="user"><br> <input type="password" id="pas"><br> <button onclick="login()">提交</button> <script> function login(){ fetch(`http://localhost:80/fetch.html?user=${user.value}&pas=${pas.value}`,{ method:'GET' }).then(response=>{ console.log('響應',response) }) } </script>
2.2、post 方式
使用 post 傳送請求時,需要設定請求頭、請求資料等。
將上個例項,改寫成 post 方式提交資料,程式碼如下:
fetch(`http://localhost:80/ES6練習題/53fetch.html`,{ method:'POST', headers:{ 'Content-Type':'application/x-www-form-urlencoded;charset=UTF-8' }, body:`user=${user.value}&pas=${pas.value}` }).then(response=>{ console.log('響應',response) })
如果是提交json資料時,需要把json轉換成字串。如
body:JSON.stringify(json)
如果提交的是表單資料,使用 formData轉化下,如:
body:new FormData(form)
上傳檔案,可以包含在整個表單裡一起提交,如:
const input = document.querySelector('input[type="file"]'); const data = new FormData(); data.append('file', input.files[0]); data.append('user', 'foo'); fetch('/avatars', { method: 'POST', body: data });
上傳二進位制資料,將 bolb 或 arrayBuffer 資料放到body屬性裡,如:
let blob = await new Promise(resolve => canvasElem.toBlob(resolve, 'image/png') ); let response = await fetch('/article/fetch/post/image', { method: 'POST', body: blob });
三、fetch 常見坑
3.1、fetch相容性
fetch原生支援率如圖:
fetch 是相對較新的技術,IE瀏覽器不支援,還有其他低版本瀏覽器也不支援,因此如果使用fetch時,需要考慮瀏覽器相容問題。
解決辦法:引入 polyfill 完美支援 IE8 以上。
- 由於 IE8 是 ES3,需要引入 ES5 的 polyfill: es5-shim, es5-sham
- 引入 Promise 的 polyfill:es6-promise
- 引入 fetch 探測庫:fetch-detector
- 引入 fetch 的 polyfill: fetch-ie8
- 可選:如果你還使用了 jsonp,引入 fetch-jsonp
- 可選:開啟 Babel 的 runtime 模式,現在就使用 async/await
polyfill 的原理就是探測fetch是否支援,如果不支援則用 xhr 實現。支援 fetch 的瀏覽器,響應中文會亂碼,所以使用 fetch-detector 和 fetch-ie8 解決亂碼問題。
3.2、fetch預設不帶cookie
傳遞cookie時,必須在header引數內加上 credentials:'include',才會像 xhr 將當前cookie 帶有請求中。
3.3、異常處理
fetch 不同於 xhr ,xhr 自帶取消、錯誤等方法,所以伺服器返回 4xx 或 5xx 時,是不會丟擲錯誤的,需要手動處理,通過 response 中的 status 欄位來判斷。