▶fetch的瀏覽器兼容
㈠fetch默認不攜帶cookie
⑴fetch發送請求默認是不發送cookie的,不管是同域還是跨域;
需要設置 :
fetch(url, {credentials: 'include'})
可以配置其credentials
項,其有3個值:
①omit
: 默認值,忽略cookie的發送
②same-origin
: 表示cookie只能同域發送,不能跨域發送
③include
: cookie既可以同域發送,也可以跨域發送
⑵fetch默認對服務端通過Set-Cookie
頭設置的cookie也會忽略,若想選擇接受來自服務端的cookie信息,也必須要配置credentials選項;
㈡fetch請求對某些錯誤http狀態不會reject
⑴這主要是由fetch返回promise導致的,因為fetch返回的promise在某些錯誤的http狀態下如400、500等不會reject,相反它會被resolve;
⑵只有網絡錯誤會導致請求不能完成時,fetch 才會被 reject;所以一般會對fetch請求做一層封裝
⑶示例:
function checkStatus(response) { if (response.status >= 200 && response.status < 300) { return response; } const error = new Error(response.statusText); error.response = response; throw error; } function parseJSON(response) { return response.json(); } export default function request(url, options) { let opt = options||{}; return fetch(url, {credentials: 'include', ...opt}) .then(checkStatus) .then(parseJSON) .then((data) => ( data )) .catch((err) => ( err ));
㈢fetch不支持超時timeout處理
⑴fetch不像大多數ajax庫那樣對請求設置超時timeout,它沒有有關請求超時的feature,所以在fetch標准添加超時feature之前,都需要polyfill該特性。
⑵實際上,我們真正需要的是abort()
, timeout可以通過timeout+abort
方式來實現,起到真正超時丟棄當前的請求。
⑶而在目前的fetch指導規范中,fetch並不是一個具體實例,而只是一個方法;
⑷其返回的promise實例根據Promise指導規范標准是不能abort的,也不能手動改變promise實例的狀態,只能由內部來根據請求結果來改變promise的狀態。
⑸因此:
實現fetch的timeout功能,其思想就是新創建一個可以手動控制promise狀態的實例,
根據不同情況來對新promise實例進行resolve或者reject,從而達到實現timeout的功能;
⑹目前可以有兩種不同的解決方法:
①第一種:單純setTimeout方式
var oldFetchfn = fetch; //攔截原始的fetch方法 window.fetch = function(input, opts){//定義新的fetch方法,封裝原有的fetch方法 return new Promise(function(resolve, reject){ var timeoutId = setTimeout(function(){ reject(new Error("fetch timeout")) }, opts.timeout); oldFetchfn(input, opts).then( res=>{ clearTimeout(timeoutId); resolve(res) }, err=>{ clearTimeout(timeoutId); reject(err) } ) }) }
在上面基礎上可以模擬類似XHR的abort
功能:
var oldFetchfn = fetch; window.fetch = function(input, opts){ return new Promise(function(resolve, reject){ var abort_promise = function(){ reject(new Error("fetch abort")) }; var p = oldFetchfn(input, opts).then(resolve, reject); p.abort = abort_promise; return p; }) }
②第二種方法:利用Promise.race方法
Promise.race方法接受一個promise實例數組參數,表示多個promise實例中任何一個最先改變狀態,那么race方法返回的promise實例狀態就跟着改變。
var oldFetchfn = fetch; //攔截原始的fetch方法 window.fetch = function(input, opts){//定義新的fetch方法,封裝原有的fetch方法 var fetchPromise = oldFetchfn(input, opts); var timeoutPromise = new Promise(function(resolve, reject){ setTimeout(()=>{ reject(new Error("fetch timeout")) }, opts.timeout) }); retrun Promise.race([fetchPromise, timeoutPromise]) }
⑺timeout不是請求連接超時的含義,它表示請求的response時間,包括請求的連接、服務器處理及服務器響應回來的時間;
⑻fetch的timeout即使超時發生了,本次請求也不會被abort丟棄掉,它在后台仍然會發送到服務器端,只是本次請求的響應內容被丟棄而已;
㈣fetch不支持JSONP
⑴fetch是與服務器端進行異步交互的,而JSONP是外鏈一個javascript資源,並不是真正ajax,所以fetch與JSONP沒有什么直接關聯,當然至少目前是不支持JSONP的。
⑵我們要實現一個JSONP,只不過這個JSONP的實現要與fetch的實現類似,即基於Promise來實現一個JSONP
;而其外在表現給人感覺是fetch支持JSONP一樣;
⑶JSONP的實現步驟如下:
①首先需要用npm安裝fetch-jsonp
npm install fetch-jsonp --save-dev
②然后在像下面一樣使用:
fetchJsonp('/users.jsonp', { timeout: 3000, jsonpCallback: 'custom_callback' }) .then(function(response) { return response.json() }).catch(function(ex) { console.log('parsing failed', ex) })
㈤fetch不支持progress事件
⑴XHR是原生支持progress事件的
⑵但是呢,根據fetch的指導規范標准,其內部設計實現了Request
和Response
類;
其中Response封裝一些方法和屬性,通過Response實例可以訪問這些方法和屬性:
⑶例如response.json()
、response.body
等等;
⑷response.body
是一個可讀字節流對象,其實現了一個getRender()
方法,其具體作用是:
getRender()方法用於讀取響應的原始字節流,該字節流是可以循環讀取的,直至body內容傳輸完成;
㈥fetch跨域問題
⑴XHR2是支持跨域請求的,只不過要滿足瀏覽器端支持CORS
,服務器通過Access-Control-Allow-Origin
來允許指定的源進行跨域,僅此一種方式。
⑵fetch也是支持跨域請求的,只不過其跨域請求做法與XHR2一樣,需要客戶端與服務端支持;
⑶fetch還支持一種跨域,不需要服務器支持的形式,具體可以通過其mode
的配置項來說明。
⑷fetch的mode
配置項有3個值:
①same-origin
:該模式是不允許跨域的,它需要遵守同源策略,否則瀏覽器會返回一個error告知不能跨域;其對應的response type為basic
。
②cors
: 該模式支持跨域請求,顧名思義它是以CORS的形式跨域;當然該模式也可以同域請求不需要后端額外的CORS支持;其對應的response type為cors
。
③no-cors
: 該模式用於跨域請求但是服務器不帶CORS響應頭,也就是服務端不支持CORS;這也是fetch的特殊跨域請求方式;其對應的response type為opaque
。
⑸針對跨域請求,cors模式是常見跨域請求實現,但是fetch自帶的no-cors
跨域請求模式則較為陌生,該模式有一個比較明顯的特點:
該模式允許瀏覽器發送本次跨域請求,但是不能訪問響應返回的內容,這也是其response type為opaque透明的原因。
⑹這與<img/>發送的請求類似,只是該模式不能訪問響應的內容信息;但是它可以被其他APIs進行處理,例如ServiceWorker。
⑺另外,該模式返回的repsonse可以在Cache API中被存儲起來以便后續的對它的使用,這點對script、css和圖片的CDN資源是非常合適的,
因為這些資源響應頭中都沒有CORS頭。
總的來說,fetch的跨域請求是使用CORS方式,需要瀏覽器和服務端的支持。
㈦不可以取消
1.fetch不支持abort,不支持超時控制,使用setTimeout及Promise.reject的實現的超時控制並不能阻止請求過程繼續在后台運行,造成了流量的浪費。
2.fetch沒有辦法原生監測請求的進度,而XHR可以