Vue+Axios熱更新導(dǎo)致響應(yīng)攔截器重復(fù)注冊問題的解決方案
在使用 Vue 3(或 Vue 2)配合 Vite / Webpack 開發(fā)時(shí),我們經(jīng)常會遇到一個(gè)“隱性陷阱”:開發(fā)環(huán)境下熱更新(HMR, Hot Module Replacement)會導(dǎo)致 Axios 響應(yīng)攔截器被重復(fù)注冊。這個(gè)問題在控制臺中表現(xiàn)為接口響應(yīng)日志被多次打印、錯(cuò)誤提示重復(fù)彈出,甚至引發(fā)邏輯混亂。
本文將結(jié)合實(shí)際代碼,深入剖析該問題的成因,并介紹一種穩(wěn)定、可靠的解決方案。
一、問題現(xiàn)象
假設(shè)我們在 request.js 中配置了 Axios 的響應(yīng)攔截器:
// request.js
import axios from 'axios'
axios.interceptors.response.use(
(res) => {
console.log('響應(yīng)攔截器執(zhí)行')
// 處理響應(yīng)邏輯...
return res.data
},
(err) => {
// 錯(cuò)誤處理...
return Promise.reject(err)
}
)當(dāng)你在開發(fā)過程中修改任意文件觸發(fā)熱更新后,再次發(fā)起請求,會發(fā)現(xiàn)控制臺中 "響應(yīng)攔截器執(zhí)行" 被打印了 兩次、四次甚至更多次!
這說明:每次熱更新都會重新執(zhí)行 request.js 模塊,從而重復(fù)注冊新的攔截器。而舊的攔截器并未被清除,導(dǎo)致多個(gè)攔截器實(shí)例同時(shí)生效。
二、錯(cuò)誤嘗試:使用局部變量控制
很多開發(fā)者第一反應(yīng)是用一個(gè)布爾變量來防止重復(fù)注冊:
let isResponseRegistered = false
if (!isResponseRegistered) {
axios.interceptors.response.use(...)
isResponseRegistered = true
}但你會發(fā)現(xiàn),這個(gè)方案在熱更新下依然失效——控制臺還是打印了兩次日志。
為什么?
因?yàn)?熱更新會重新執(zhí)行整個(gè)模塊(包括變量聲明)。也就是說,每次 HMR 觸發(fā)時(shí),isResponseRegistered 都會被重置為 false,于是攔截器又被注冊了一次。
關(guān)鍵點(diǎn):模塊級變量在熱更新時(shí)會被重新初始化,無法跨更新周期保持狀態(tài)。
三、正確方案:將標(biāo)志掛載到 Axios 實(shí)例上
要解決這個(gè)問題,必須使用一個(gè) 不會被熱更新重置的對象屬性 來記錄是否已注冊攔截器。
Axios 本身是一個(gè)對象(函數(shù)對象),我們可以直接在其上掛載自定義屬性:
// 防止熱更新重復(fù)注冊響應(yīng)攔截器
if (!axios.__myResponseInterceptor__) {
axios.interceptors.response.use(
(res) => {
console.log('響應(yīng)攔截器執(zhí)行')
// ...處理邏輯
return res.data
},
(err) => {
return Promise.reject(err)
}
)
axios.__myResponseInterceptor__ = true // 標(biāo)記已注冊
}為什么這個(gè)方案有效?
axios是從node_modules引入的模塊,在 HMR 中 通常不會被重新加載(屬于“穩(wěn)定的依賴”)。- 因此,掛載在
axios上的屬性__myResponseInterceptor__在熱更新期間保持不變。 - 即使
request.js被反復(fù)執(zhí)行,也能準(zhǔn)確判斷攔截器是否已存在。
這是一種被社區(qū)廣泛驗(yàn)證的有效做法,類似方案也用于防止重復(fù)注冊全局組件、插件等。
四、完整代碼示例(來自實(shí)際項(xiàng)目)
以下是從你提供的 request.js 中提取的核心邏輯:
// 防止響應(yīng)攔截器被重復(fù)注冊
if (!axios.__myResponseInterceptor__) {
axios.interceptors.response.use(
(res) => {
// 二進(jìn)制數(shù)據(jù)直接返回
if (res.request?.responseType === 'blob' || ...) {
return res.data
}
if (!res.data) {
return { code: 200, data: null, message: '熱更新中' }
}
switch (Number(res.data.code)) {
case 401:
router.push('/login')
break
case 200:
if (res.data.token) {
localStorage.setItem('token', res.data.token)
}
return res.data
default:
return res.data
}
},
(err) => {
return Promise.reject(err)
}
)
axios.__myResponseInterceptor__ = true // 關(guān)鍵:標(biāo)記已注冊
}該方案成功解決了熱更新下的重復(fù)注冊問題,且不影響生產(chǎn)環(huán)境(生產(chǎn)環(huán)境無 HMR,只執(zhí)行一次)。
五、其他可行方案(補(bǔ)充)
使用 import.meta.hot?.accept() 手動管理副作用(Vite 特有)
可在模塊卸載時(shí)移除攔截器,但實(shí)現(xiàn)復(fù)雜,不推薦。
將攔截器注冊移到 main.js 或入口文件
減少被 HMR 影響的概率,但若入口文件也被更新,仍可能失效。
使用單例模式封裝 Axios 實(shí)例
例如導(dǎo)出一個(gè) createAxiosInstance() 函數(shù),并緩存實(shí)例。但需確保調(diào)用方不重復(fù)創(chuàng)建。
相比之下,掛載標(biāo)志位到 axios 對象上是最簡單、可靠、低侵入性的方案。
六、總結(jié)
| 方案 | 是否有效 | 原因 |
|---|---|---|
| 局部變量 let flag = false | ? | 熱更新重置變量 |
| 掛載到 axios 對象上 | ? | axios 模塊穩(wěn)定,屬性持久 |
| 移到 main.js | ?? | 降低概率,但非根治 |
| 手動 HMR 清理 | ? 但復(fù)雜 | 需要監(jiān)聽模塊更新事件 |
最佳實(shí)踐:在開發(fā) Axios 封裝庫時(shí),務(wù)必考慮 HMR 場景,使用 axios.__xxx__ 標(biāo)志位防止重復(fù)注冊攔截器。
希望本文能幫助你徹底理解并解決這一“開發(fā)期幽靈 bug”。
到此這篇關(guān)于Vue+Axios熱更新導(dǎo)致響應(yīng)攔截器重復(fù)注冊問題的解決方案的文章就介紹到這了,更多相關(guān)Vue Axios熱更新導(dǎo)致攔截器重復(fù)注冊內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
手把手教你如何使用Vite構(gòu)建vue項(xiàng)目
這篇文章主要介紹了如何使用Vite構(gòu)建vue項(xiàng)目的相關(guān)資料,本文主要介紹了Vite構(gòu)建Vue項(xiàng)目的詳細(xì)步驟,包括檢查node.js和pnpm的安裝,構(gòu)建Vite+Vue項(xiàng)目,利用HBuilderX導(dǎo)入項(xiàng)目,需要的朋友可以參考下2024-10-10
如何利用vue-cli監(jiān)測webpack打包與啟動時(shí)長
這篇文章主要給大家介紹了關(guān)于如何利用vue-cli監(jiān)測webpack打包與啟動時(shí)長的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2022-02-02
vue3中獲取dom元素和操作實(shí)現(xiàn)方法
ref是Vue3中一個(gè)非常重要的功能,它可以用來獲取DOM節(jié)點(diǎn),從而實(shí)現(xiàn)對DOM節(jié)點(diǎn)的操作,下面這篇文章主要給大家介紹了關(guān)于vue3中獲取dom元素和操作實(shí)現(xiàn)的相關(guān)資料,需要的朋友可以參考下2023-06-06
使用vue-cli(vue腳手架)快速搭建項(xiàng)目的方法
本篇文章主要介紹了使用vue-cli(vue腳手架)快速搭建項(xiàng)目的方法,vue-cli 是一個(gè)官方發(fā)布 vue.js 項(xiàng)目腳手架,使用 vue-cli 可以快速創(chuàng)建 vue 項(xiàng)目,感興趣的小伙伴們可以參考一下2018-05-05
Vue實(shí)現(xiàn)計(jì)數(shù)器案例
這篇文章主要為大家詳細(xì)介紹了Vue計(jì)數(shù)器案例的實(shí)現(xiàn)方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-06-06
前端vue-cli項(xiàng)目中使用img圖片和background背景圖的幾種方法
這篇文章主要介紹了前端vue-cli項(xiàng)目中使用img圖片和background背景圖的幾種方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11
Vue用mixin合并重復(fù)代碼的實(shí)現(xiàn)
這篇文章主要介紹了Vue用mixin合并重復(fù)代碼的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11

