從Vue2-manage談?wù)刦etchAPI

在做上一個(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)有很高的兼容性:

截止撰寫(xiě)文章時(shí)的瀏覽器兼容性

不難看出,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í)巧妙。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容