項(xiàng)目實(shí)戰(zhàn)基礎(chǔ)請(qǐng)求

前言

傳送地址:

前端設(shè)計(jì)模式之工廠模式

前端設(shè)計(jì)模式之代理模式

前端設(shè)計(jì)模式之策略模式

設(shè)計(jì)模式已經(jīng)連載到了第 3 期,由于下一期的觀察者模式要配合團(tuán)隊(duì)的埋點(diǎn)性能博客一起上要暫緩一段時(shí)間,所以這一篇算彩蛋篇,推出一個(gè)開箱即用型的 fetch 項(xiàng)目實(shí)戰(zhàn)。

封裝 fetch 步驟

封裝基礎(chǔ) fetch

未封裝之前的 fecth 如下使用

fetch('https://www.baidu.com/search/error.html') // 返回一個(gè)Promise對(duì)象
.then((res) => {
    return res.text() // res.text()是一個(gè)Promise對(duì)象
})
.then((res) => {
    console.log(res) // res是最終的結(jié)果
})

如上是直接使用 fecth 的方法,但在項(xiàng)目中直接引用會(huì)有很多不便的地方,所以我們先簡(jiǎn)單封裝一下,比如跨域配置、超時(shí)、各種請(qǐng)求等等的配置。

import qs from 'qs'

class Fetch {
    constructor(config = {}) {
        this.config = {
            cache: 'no-cache', // * default, no-cache, reload, force-cache, only-if-cached
            credentials: 'same-origin', // include, same-origin, *omit
            headers: {},
            mode: 'cors', // no-cors, cors, *same-origin
            redirect: 'follow', // manual, *follow, error
            referrer: 'no-referrer', // *client, no-referrer
            timeOut: 3000,
            BASE_URL: '',
            ...config
        }
    }

    send({ url, params, method = "GET", headers }) {
        // 發(fā)送 ajax 請(qǐng)求
        const { BASE_URL } = this.config
        const ajax = new Promise((resolve) => {
            fetch(BASE_URL ? `${BASE_URL}/${url}` : url, {
                ...this.config,
                body: params,
                headers,
                method,
            }).then((response) => {
                return response.json()
            }).then((data) => {
                resolve(data)
            })
        })
        // 設(shè)置超時(shí)時(shí)間
        const time = new Promise((reject) => {
            console.log(this.config.timeOut)
            setTimeout(() => {
                reject('time out')
            }, this.config.timeOut);
        })
        return Promise.race([ajax, time])
    }

    // 封裝請(qǐng)求
    get({ url, query, headers }) {
        return this.send({ url: `${url}?${qs.stringify(query)}`, headers, method: 'GET' })
    }

    post({ url, params, headers }) {
        return this.send({ url, params, headers, method: 'POST' })
    }
}

const newFetch = new Fetch();

newFetch.get({
    url: 'https://api.github.com/users/octocat',
    params: {
        test: 1
    }
}).then(data => {
    console.log(data)
}).catch(err => {
    console.log(err)
})

如上我們簡(jiǎn)單的封裝了一個(gè)可以發(fā)送 get、 post 請(qǐng)求的 fetch 類,加入了超時(shí)跟網(wǎng)關(guān),簡(jiǎn)單的項(xiàng)目可以隨便用起來(lái)了,但我們既然要做到開箱即用,那就根據(jù)實(shí)際的項(xiàng)目發(fā)生的情況,再進(jìn)一步的定制。

封裝請(qǐng)求參數(shù)類型

this.dataOperation = {
    JSON: {
        headers: {
            'Content-Type': 'application/json', // 告訴服務(wù)器,我們提交的數(shù)據(jù)類型為 json 格式
        },
        formatting(params) {
            return JSON.stringify(params)
        }
    },
    FormData: {
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded' // 告訴服務(wù)器,我們提交的數(shù)據(jù)類型為 FormData 格式
        },
        formatting(params) {
            let _formData = new FormData();
            Object.keys(params).forEach(key => {
                _formData.append(key, params[key]);
            })
            return _formData
        }
    }
}

preSend({ url, params, headers, method }) {
    const { requestType } = this.config
    const FetchConfig = {
        ...this.FetchConfig,
        method,
        headers: {
            ...this.dataOperation[requestType].headers,
            ...headers
        },
    };
    if (!!params) FetchConfig.body = this.dataOperation[requestType].formatting(params);
    return this.send({
        url,
        FetchConfig
    })
}

post({ url, query, params = {}, headers }) {
    return this.preSend({ url: query ? `${url}?${qs.stringify(query)}` : url, params, headers, method: 'POST' })
}

如上,我們根據(jù)策略模式 + 代理模式將發(fā)送請(qǐng)求報(bào)多包了一層,這樣我們可以在初始化的時(shí)候,選擇項(xiàng)目請(qǐng)求參數(shù)類型,一般來(lái)說(shuō)一個(gè)項(xiàng)目并不會(huì)使用多種請(qǐng)求類型,所以我們暫不提供請(qǐng)求參數(shù)類型的方法傳參配置,簡(jiǎn)化我們請(qǐng)求方法的參數(shù)數(shù)量。

增加 Get 請(qǐng)求緩存配置

一般來(lái)說(shuō),正常的 get 請(qǐng)求可以配置 304 緩存等,但是從網(wǎng)絡(luò)通信請(qǐng)求質(zhì)量跟減少請(qǐng)求數(shù)量來(lái)說(shuō),當(dāng)數(shù)據(jù)數(shù)據(jù)過(guò)大或者請(qǐng)求數(shù)量過(guò)多的時(shí)候,從本地直接讀取數(shù)據(jù)的性能提升會(huì)更加有效果,所以我們可以將之前代理模式里面的緩存實(shí)戰(zhàn)例子結(jié)合 get 請(qǐng)求封裝起來(lái),一起搭配使用

get({ url, query, headers }) { // 優(yōu)化 get 請(qǐng)求,添加緩存處理
    const key = query ? `${url}?${qs.stringify(query)}` : url
    if (this.cacheStorage) {
        if (this.cacheStorage.getItem(key)) {
            return Promise.resolve(this.cacheStorage.getItem(key))
        } else {
            return this.preSend({ url: key, headers, method: 'GET' }).then(data => {
                this.cacheStorage.setItem(key, data)
                return data
            })
        }
    } else {
        return this.preSend({ url: key, headers, method: 'GET' })
    }
}

這邊的緩存封裝,是在初始化工具類的時(shí)候就已經(jīng)把緩存類型跟緩存時(shí)間一起初始化完成了,這樣比較方便后續(xù)業(yè)務(wù)方使用,如果全部放在參數(shù)里面的話,靈活性提高,但是方法使用成本會(huì)提高。

業(yè)務(wù)請(qǐng)求使用

根據(jù)之前的項(xiàng)目經(jīng)驗(yàn)總結(jié)一下業(yè)務(wù)側(cè)的使用:

  1. 直接將請(qǐng)求方法根據(jù)業(yè)務(wù)類型包一層方法,然后在需要的業(yè)務(wù)側(cè)直接調(diào)用即可,統(tǒng)一處理某類請(qǐng)求的返回?cái)?shù)據(jù),數(shù)據(jù)與視圖分離,利于拓展
  2. 將請(qǐng)求方法寫在 vuex,redux 這種狀態(tài)管理中,再去實(shí)際的業(yè)務(wù)側(cè)調(diào)用,可以做到數(shù)據(jù)共享跨組件、頁(yè)面共享
  3. 綜合考慮使用過(guò)程中,如果請(qǐng)求業(yè)務(wù)不涉及跨組件、跨頁(yè)面調(diào)用的時(shí)候,可以直接將業(yè)務(wù)請(qǐng)求寫在當(dāng)前代碼中,這樣維護(hù)起來(lái)會(huì)舒服點(diǎn)

尾聲

完整的 demo 地址:項(xiàng)目實(shí)戰(zhàn) demo,喜歡的朋友可以 star 一下,后續(xù)會(huì)根據(jù)設(shè)計(jì)模式博文的推出,逐步的將此項(xiàng)目繼續(xù)拓展出來(lái)。

?著作權(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)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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