Fetch
參考: developers.google.com/web/updates…
現在可能還有一些很舊的程式還在使用XHR,類似下面的寫法:
const request = new XMLHttpRequest()
request.responseType = 'json'
request.open('GET', '/url', true)
request.onload = () => {
console.log(request.response)
}
request.onerror = () => {
console.log('shits happen!')
}
request.send(null)
複製程式碼
這樣子使用XHR進行非同步訪問、讀取資源顯得很繁瑣,相對比Fetch()允許你建立類似XHR的network訪問,但是使用更簡單而且乾淨的API,不需要多次回撥並且記住XHR複雜的API。Fetch API底層是通過Promises實現。
XMLHttpRequest
一個相對完整的XMLHttpRequest至少需要監聽兩個事件(onload、onerror)來實現成功和失敗的回撥,以及呼叫open()和send()
function reqListener() {
var data = JSON.parse(this.responseText);
console.log(data);
}
function reqError(err) {
console.log('Fetch Error :-S', err);
}
var oReq = new XMLHttpRequest();
oReq.onload = reqListener;
oReq.onerror = reqError;
oReq.open('get', './api/some.json', true);
oReq.send();
複製程式碼
Fetch
一個簡單的Fetch例子如下:
fetch('./api/some.json')
.then(
function(response) {
if (response.status !== 200) {
console.log('Looks like there was a problem. Status Code: ' +
response.status);
return;
}
// Examine the text in the response
response.json().then(function(data) {
console.log(data);
});
}
)
.catch(function(err) {
console.log('Fetch Error :-S', err);
});
複製程式碼
Fetch的語法更加語義化、比較好理解。在上面的例子裡我們先判斷response的status碼,如果是200我們才將response解析為JSON
fetch()請求返回的response是Stream物件,因此我們呼叫response.json時由於非同步讀取流物件所以返回的是一個Promise物件。
Fetch用async優化程式碼
由於Fetch底層是用Promise實現,我們可以直接用async來優化上面的程式碼,減少回撥,使其更加語義化、容易理解
async function geturl(){
try{
let res = await fetch('./api/some.json')
if(res.status == 200){
console.log(await res.text())
}
} catch(err){
console.log(err)
}
}
複製程式碼
Response後設資料
在上面的例子裡,我們瞭解了Response物件的status狀態以及怎麼把response物件轉換為JSON物件,讓我們來看看Response物件的其他後設資料:
fetch('users.json').then(function(response) {
console.log(response.headers.get('Content-Type'));
console.log(response.headers.get('Date'));
console.log(response.status);
console.log(response.statusText);
console.log(response.type);
console.log(response.url);
});
複製程式碼
Response型別
當我們發起一個Fetch請求時,返回的response響應會自帶一個response.type屬性(basic、cors、opaque)。response.type屬性說明了非同步資源的來源,同時還有相應的處理方式。
當我們發起一個同源請求時,response.type為basic,而且你可以從response讀取全部資訊。
如果我們訪問一個非同源域名,並且有返回相應的CORs響應頭時,那麼該請求型別是cors。cors和basic很相似,就除了cors響應裡你無法訪問Cache-Control
,Content-Language
,Content-Type
,Expires
,Last-Modified
和Pragma
當我們對一個不同源的域名發起請求時,如果返回的響應頭部沒有CORS資訊,那麼這個response對應的型別就是opaque型別。一個opaque響應是無法讀取返回的資料、狀態,甚至無法確定這個請求是否成功。
我們可以自定義Fetch請求的模式,要求返回對應型別的響應,有以下幾種響應:
- same-origin 只返回同源請求,其他型別會被reject
- cors 接收同源、非同源請求,返回有CORs頭部的響應
- cors-with-forced-preflight 在發出請求前會先做一次安全性檢查
- no-cors 用來發起沒有CORS頭部並且非同源請求,並且會返回opaque響應。但是目前這種型別只能在Service Worker裡使用,在window.fetch裡不能用
fetch('http://some-site.com/cors-enabled/some.json', {mode: 'cors'})
.then(function(response) {
return response.text();
})
.then(function(text) {
console.log('Request successful', text);
})
.catch(function(error) {
log('Request failed', error)
});
複製程式碼
承諾鏈
因為Fetch返回的response是基於Promise實現,所以我們可以像鏈條一樣把幾個Promise串接起來,如下所示:
function status(response) {
if (response.status >= 200 && response.status < 300) {
return Promise.resolve(response)
} else {
return Promise.reject(new Error(response.statusText))
}
}
function json(response) {
return response.json()
}
fetch('users.json')
.then(status)
.then(json)
.then(function(data) {
console.log('Request succeeded with JSON response', data);
}).catch(function(error) {
console.log('Request failed', error);
});
複製程式碼
當然了,我們也可以用async進行程式碼優化
async function geturl(url){
try {
let res = await fetch(url)
if(res.status >= 200 && res.status < 300){
console.log('Request succeeded with JSON response', await res.json())
}
}catch (err){
console.log(err)
}
}
geturl('users.json')
複製程式碼
Post請求
當我們使用Fetch發起Post請求時,需要手動設定method引數和body引數,如下:
fetch(url, {
method: 'post',
headers: {
"Content-type": "application/x-www-form-urlencoded; charset=UTF-8"
},
body: 'foo=bar&lorem=ipsum'
})
.then(json)
.then(function (data) {
console.log('Request succeeded with JSON response', data);
})
.catch(function (error) {
console.log('Request failed', error);
});
複製程式碼
如果沒有顯式指定method引數,那麼預設Get請求
帶Cookie傳送請求
如果我們想要在非同步請求中帶上cookie引數,那麼需要顯式指定credentials引數:
fetch(url, {
credentials: 'include'
})
複製程式碼