web開發(fā)中如何優(yōu)雅的解決"重復(fù)請求"問題
前言
在我們web開發(fā)過程中,很多地方需要我們?nèi)∠貜?fù)的請求。但是哪種場合需要我們?nèi)∠??我們?nèi)绾稳∠??帶著這些問題我們閱讀本文。
閱讀完本文,你將了解以下內(nèi)容:
需要取消重復(fù)請求的場景
我們?nèi)绾稳∠貜?fù)請求
axios如何取消重復(fù)的請求
封裝axios
如何給開源的項目提供源碼
如何在本地調(diào)試npm包
提出問題
最近做的項目中,用的用戶經(jīng)常遇到這樣的問題:
- 用戶頻繁切換篩選條件去請求數(shù)據(jù),初次的篩選條件數(shù)據(jù)量大。用的時間比較多。后面的篩選條件的數(shù)據(jù)量小。導(dǎo)致后面請求的數(shù)據(jù)先返回。內(nèi)容先顯示在頁面上。但是等一段時間,初次(或者前面)的請求數(shù)據(jù)返回了, 會覆蓋后面的請求的數(shù)據(jù)。這就導(dǎo)致了篩選條件和內(nèi)容不一致的情況。
- 用戶點擊了一次提交按鈕,接口沒有很快響應(yīng),導(dǎo)致頁面沒辦法做邏輯語句判斷的提示。用戶覺得可能沒提交上,便會快速又點了按鈕幾次。如果后端沒有去重的判斷,就會導(dǎo)致數(shù)據(jù)中有很多條重復(fù)的數(shù)據(jù)。
這些問題給用戶的體驗是很不友好的。那么取消無用的請求是很有必要的。
解決思路
我們用的請求庫是axios。那么我們可以在請求的時候攔截請求判斷當(dāng)前的請求是否重復(fù),如果重復(fù)我們就取消當(dāng)前的請求。大致的實現(xiàn)過程如下:
我們把目前處于pending的請求存儲(假如我們放在一個數(shù)組)起來。每個請求發(fā)送之前我們都要判斷當(dāng)前這個請求是否已經(jīng)存在于這個數(shù)組。如果存在,說明請求重復(fù)了,我們就在數(shù)組中找到重復(fù)的請求并且取消。如果不存在,說明這個請求不是重復(fù)的,正常發(fā)送并且把這個請求api添加在數(shù)據(jù)中,等請求結(jié)束之后刪除數(shù)組中的這個api。
我們這個解決思路有了,但是axios如何取消請求的呢?我們先來了解下
axios 如何取消請求
查看axios文檔發(fā)現(xiàn)axios提供了兩種取消請求的方法(http://www.axios-js.com/zh-cn/docs/#%E5%8F%96%E6%B6%88)
第一種方法
通過axios.CancelToken.source生成取消令牌token和取消方法cancel
const CancelToken = axios.CancelToken; const source = CancelToken.source(); axios.get('/user/12345', { cancelToken: source.token }).catch(function(thrown) { if (axios.isCancel(thrown)) { console.log('Request canceled', thrown.message); } else { // 處理錯誤 } }); axios.post('/user/12345', { name: 'new name' }, { cancelToken: source.token }) // 取消請求 (消息參數(shù)是可選的) source.cancel('Operation canceled by the user.');
第二種方式
通過傳遞一個 executor 函數(shù)到 CancelToken 的構(gòu)造函數(shù)來創(chuàng)建 cancel token
const CancelToken = axios.CancelToken; let cancel; axios.get('/user/12345', { cancelToken: new CancelToken(function executor(c) { // executor 函數(shù)接收一個 cancel 函數(shù)作為參數(shù) cancel = c; }) }); // 取消請求 cancel();
封裝axios
解決取消請求的思路有了,取消請求的辦法也有了,那么剩下的就是封裝了
由于同事之前已經(jīng)封裝了axios——very-axios(https://github.com/verymuch/very-axios) (基于 axios 進行二次封裝,更簡單、更統(tǒng)一地使用 axios)。那么我們就這個基礎(chǔ)上提一個pr吧。那么從現(xiàn)在開始我們就一步一步的來實現(xiàn),這個過程包含了【如何給開源的項目貢獻代碼】【如何在本地調(diào)試npm】如果已經(jīng)了解的同學(xué)可以直接略過。
準(zhǔn)備工作
由于同事已經(jīng)封裝了axios并且已經(jīng)開源了。那么我貢獻代碼的方式主要有兩種:
- 代碼倉庫的管理者給我們添加這個倉庫的寫入權(quán)限,如果這樣,我們就可以直接提push。
- 如果我們沒有權(quán)限(大多數(shù)情況)。我們使用經(jīng)典的fork & pull request 的方式來提交代碼。
我們采用的第二種方式。我們?nèi)?very-axios(https://github.com/verymuch/very-axios) 把代碼fork到自己的倉庫(如果你還沒有自己的github,需要自己注冊下哦)。
那么你回到自己的github倉庫下面就會看有一個一摸一樣的項目
那么我們現(xiàn)在就可以git clone這個倉庫的代碼到本地,新建branch進行開發(fā),就比如我新建了一個這樣的branch:
現(xiàn)在已經(jīng)有本地的代碼了,但是我們?nèi)绾伪镜鼗{(diào)試npm包呢?那就需要npm link 了
首先在我們要修改的npm 包中npm link:
之后我們會得到
/Users/shuliqi/.nvm/versions/node/v12.17.0/lib/node_modules/very-axios -> /Users/shuliqi/study/axios/very-axios
這意思就是我們把very-axios鏈接到全局的node_modules
然后我們進入我們my-project-of-axios 目錄下面執(zhí)行npm link very-axios 如圖:
這意思就是very-axios被安裝在my-project-of-axios 下面了。very-axios的修改都會同步到my-project-of-axios。就實現(xiàn)本地測試了。
我們在my-project-of-axios中的HelloWorld.vue文件中做列子。
如果這里看的不是很懂的同學(xué)可以看看這兩篇文章:如何在本地調(diào)試npm包(https://github.com/allenGKC/Blog/issues/13)。如何使用 GitHub Flow 給開源項目貢獻代碼(https://juejin.im/post/6844903636863041550)
開始封裝
準(zhǔn)備工作完成了, 那我們開始封裝的事情。根據(jù)我們之前的思路。我們采用axios 如何取消請求的第一種方式。
聲明一個Map。用來存儲每個請求的 標(biāo)識 和 取消的函數(shù)
// 存儲每個請求的標(biāo)識和取消的函數(shù) this.pendingAjax = new Map();
自定一個字段來讓用戶自己決定是否需要取消重復(fù)的請求
// 是否取消重復(fù)的請求 cancelDuplicated = false,
自定一個字段來讓用戶是否有全局的統(tǒng)一的設(shè)置重復(fù)標(biāo)識的函數(shù)。如果沒有設(shè)置全局的統(tǒng)一的函數(shù),則默認是請求的method 和url作為重復(fù)的標(biāo)識
// 生成重復(fù)標(biāo)識的方式 duplicatedKeyFn, this.duplicatedKeyFn = isFunction(duplicatedKeyFn) ? duplicatedKeyFn : (config) => `${config.method}${config.url}`;
添加請求
/** * 將請求添加到pendingAjax * @param {Object} config */ addPendingAjax(config) { // 是否需要取消重復(fù)的請求 if (!this.cancelDuplicated) return const veryConfig = config.veryConfig || {}; const duplicatedKey = JSON.stringify({ duplicatedKey: veryConfig.duplicatedKey || this.duplicatedKeyFn(config), type: REQUEST_TYPE.DUPLICATED_REQUEST, }); config.cancelToken = config.cancelToken || new axios.CancelToken((cancel) => { // 如果pendingAjax中不存在當(dāng)前請求,添加進去 if (duplicatedKey && !this.pendingAjax.has(duplicatedKey)) { this.pendingAjax.set(duplicatedKey, cancel); } }); }
這里面我們可以使用duplicatedKey字段來讓用戶對單一請求自定義重復(fù)的標(biāo)識?;蛘呖梢允褂靡粋€函數(shù)duplicatedKeyFn統(tǒng)一的讓用戶自定義重復(fù)的標(biāo)識
刪除請求
/** * 從pendingAjax中刪除請求 * @param {Object} config */ removePendingAjax(config) { // 是否需要取消重復(fù)的請求 if (!this.cancelDuplicated) return const veryConfig = config.veryConfig || {}; const duplicatedKey = JSON.stringify({ duplicatedKey: veryConfig.duplicatedKey || this.duplicatedKeyFn(config), type: REQUEST_TYPE.DUPLICATED_REQUEST, }); // 如果pendingAjax中存在當(dāng)前請求, 取消當(dāng)前請求并將其刪除 if (duplicatedKey && this.pendingAjax.has(duplicatedKey)) { const cancel = this.pendingAjax.get(duplicatedKey); cancel(duplicatedKey); this.pendingAjax.delete(duplicatedKey); } }
封裝好了, 我們在哪里使用呢?肯定是在請求開始之前和請求完成之后使用。
在請求之前
// 攔截請求 this.axios.interceptors.request.use((config) => { // 在請求開始之前檢查先前的請求 this.removePendingAjax(config); // 將當(dāng)前請求添加到pendingAjax this.addPendingAjax(config); // ... });
在請求完成之后去掉該請求
// 攔截響應(yīng) this.axios.interceptors.response.use(response => { removePending(response) return response }, error => { // ... })
到現(xiàn)在已經(jīng)完成了該有的功能, 但是取消請求的錯誤我們不該返回給用戶。所以:
(err) => { // 類型是否為重復(fù)請求 let isDuplicatedType; try { const errorType = (JSON.parse(error.message) || {}).type isDuplicatedType = errorType === REQUEST_TYPE.DUPLICATED_REQUEST; } catch (error) { isDuplicatedType = false } if (isDuplicatedType) return; }
我們在請求完成之后的err里面做一個判斷,判斷如果當(dāng)前請求是取消的類型,我們就不返回給用戶錯誤的提示信息。
總結(jié)
至此,完成了我們的封裝。完成的pr地址:(https://github.com/verymuch/very-axios/pull/1)。本文測試npm包的項目地址:(https://github.com/shuliqi/my-project-of-axios)
到此這篇關(guān)于web開發(fā)中如何優(yōu)雅的解決"重復(fù)請求"問題的文章就介紹到這了,更多相關(guān)axios取消重復(fù)請求內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
element-ui配合node實現(xiàn)自定義上傳文件方式
這篇文章主要介紹了element-ui配合node實現(xiàn)自定義上傳文件方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-09-09Vue3+Vite實現(xiàn)動態(tài)路由的詳細實例代碼
我們在開發(fā)大型系統(tǒng)的時候一般都需要動態(tài)添加路由,下面這篇文章主要給大家介紹了關(guān)于Vue3+Vite實現(xiàn)動態(tài)路由的相關(guān)資料,文中通過圖文以及實例代碼介紹的非常詳細,需要的朋友可以參考下2022-08-08