在做上一個(gè)全站的管理系統(tǒng)時(shí),用了完整的前后端分離開(kāi)發(fā)。在項(xiàng)目的前期方案制定中參考了vue2-manage的部分實(shí)現(xiàn),覺(jué)得有一些實(shí)現(xiàn)方案是自己所沒(méi)有想到的比較精妙的方案。對(duì)fetchAPI的封裝便是其中一個(gè)。
同時(shí)歡迎大家從我的博客閱讀這篇文章,和我的其他的微不足道的創(chuàng)作:
Here`s Chino! - ——From dawn to dusk.
一、Fetch API
對(duì)于我這樣的小白fetchAPI這一技術(shù)算是比較新了,在以前的學(xué)習(xí)中都沒(méi)有接觸到過(guò),經(jīng)過(guò)查詢mdn文檔發(fā)現(xiàn)這一技術(shù)確實(shí)沒(méi)有很高的兼容性:

不難看出,fetchAPI對(duì)IE的兼容性尤其不好.....而edge則處于可以湊合用的狀態(tài)。移動(dòng)端還算樂(lè)觀,兼容性略好一點(diǎn)。
AJAX已經(jīng)相當(dāng)成熟了,對(duì)于fetchAPI的存在意義,MDN的描述為“ Fetch API 提供了一個(gè)獲取資源的接口(包括跨域請(qǐng)求)。任何使用過(guò)XMLHttpRequest的人都能輕松上手,但新的API提供了更強(qiáng)大和靈活的功能集。
比較通俗的講,fetch為請(qǐng)求定義了Request和Response對(duì)象的標(biāo)準(zhǔn)(雖然現(xiàn)在兼容性依然一片狼藉)。這種標(biāo)準(zhǔn)不光滿足了網(wǎng)絡(luò)請(qǐng)求的需要,在應(yīng)用中所有需要請(qǐng)求和相應(yīng)的地方都可以運(yùn)用,甚至是自定義一種相應(yīng)也未嘗不可。對(duì)于瀏覽器中的網(wǎng)絡(luò)請(qǐng)求,window接口上直接就可以使用fetch()方法,所以它是個(gè)全局方法。
這個(gè)新穎的API返回的是Promise對(duì)象,這就意味著使用ES6的async/await新特性變?yōu)榭赡?,可以更方便的處理邏輯方面的?wèn)題而不會(huì)產(chǎn)生callback hell。具體體現(xiàn)為:
//fetch API
let requestConfig = {
credentials: 'include',
method: type,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
mode: "cors",
cache: "force-cache"
}
const response = await fetch(url, requestConfig);
const responseJson = await response.json()
fetchAPI可以安全的使用await,相比之下,axios需要.then(),請(qǐng)求部分和業(yè)務(wù)代碼雜糅在一起,遠(yuǎn)沒(méi)有fetchAPI簡(jiǎn)潔。
二、封裝
在fetchAPI的運(yùn)用中我們已經(jīng)體會(huì)到了fetchAPI的簡(jiǎn)潔之處,但是其缺點(diǎn)同樣明顯——兼容性不足。那么如果我們要在項(xiàng)目中使用fetchAPI,就需要對(duì)其進(jìn)行封裝,不支持fetchAPI特性的環(huán)境我們要將其轉(zhuǎn)換成普通的 XMLHTTPRequest,乃至 ActiveX (IE就應(yīng)該被徹底消滅啊啊啊?。┧越酉聛?lái)我直接把其項(xiàng)目中關(guān)于fetch的封裝代碼帖上來(lái),加上我認(rèn)為最詳盡的注釋。(因?yàn)槲覀€(gè)人也沒(méi)有更好的方案了
//fetch.js
import { baseUrl } from './env'
export default async(url = '', data = {}, type = 'GET', method = 'fetch') => {
//url拼接
type = type.toUpperCase();
url = baseUrl + url;
if (type == 'GET') {
let dataStr = ''; //數(shù)據(jù)拼接字符串
Object.keys(data).forEach(key => {
dataStr += key + '=' + data[key] + '&';
})
if (dataStr !== '') {
dataStr = dataStr.substr(0, dataStr.lastIndexOf('&'));
url = url + '?' + dataStr;
}
}
if (window.fetch && method == 'fetch') {
let requestConfig = {
//攜帶cookie
credentials: 'include',
method: type,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
//跨域
mode: "cors",
cache: "force-cache"
}
if (type == 'POST') {
Object.defineProperty(requestConfig, 'body', {
value: JSON.stringify(data)
})
}
try {
const response = await fetch(url, requestConfig);
const responseJson = await response.json();
return responseJson
} catch (error) {
throw new Error(error)
}
} else {
//將XMLHTTPRrequest和ActiveX也用Promise的方法返回
return new Promise((resolve, reject) => {
let requestObj;
if (window.XMLHttpRequest) {
requestObj = new XMLHttpRequest();
} else {
requestObj = new ActiveXObject;
}
let sendData = '';
if (type == 'POST') {
sendData = JSON.stringify(data);
}
requestObj.open(type, url, true);
requestObj.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
requestObj.send(sendData);
requestObj.onreadystatechange = () => {
if (requestObj.readyState == 4) {
if (requestObj.status == 200) {
let obj = requestObj.response
if (typeof obj !== 'object') {
obj = JSON.parse(obj);
}
resolve(obj)
} else {
reject(requestObj)
}
}
}
})
}
}
其中值得一提的是,在拼接url的時(shí)候引用的baseURL是這樣返回的
//env.js
/**
* 配置編譯環(huán)境和線上環(huán)境之間的切換
*
* baseUrl: 域名地址
* routerMode: 路由模式
* baseImgPath: 圖片存放地址
*
*/
let baseUrl = '';
let routerMode = 'hash';
let baseImgPath;
if (process.env.NODE_ENV == 'development') {
baseUrl = '';
baseImgPath = '/img/';
}else{
baseUrl = '//elm.cangdu.org';
baseImgPath = '//elm.cangdu.org/img/';
}
export {
baseUrl,
routerMode,
baseImgPath
}
這樣就完成了開(kāi)發(fā)和線上模式的切換,屬實(shí)巧妙。