vue.js

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript类库 > vue.js > Vue Axios热更新导致拦截器重复注册

Vue+Axios热更新导致响应拦截器重复注册问题的解决方案

作者:Mr Xu_

在使用 Vue 3(或 Vue 2)配合 Vite / Webpack 开发时,我们经常会遇到一个隐性陷阱:开发环境下热更新会导致 Axios 响应拦截器被重复注册,因此本文将结合实际代码,深入剖析该问题的成因,并介绍一种稳定、可靠的解决方案,需要的朋友可以参考下

在使用 Vue 3(或 Vue 2)配合 Vite / Webpack 开发时,我们经常会遇到一个“隐性陷阱”:开发环境下热更新(HMR, Hot Module Replacement)会导致 Axios 响应拦截器被重复注册。这个问题在控制台中表现为接口响应日志被多次打印、错误提示重复弹出,甚至引发逻辑混乱。

本文将结合实际代码,深入剖析该问题的成因,并介绍一种稳定、可靠的解决方案。

一、问题现象

假设我们在 request.js 中配置了 Axios 的响应拦截器:

// request.js
import axios from 'axios'
 
axios.interceptors.response.use(
  (res) => {
    console.log('响应拦截器执行')
    // 处理响应逻辑...
    return res.data
  },
  (err) => {
    // 错误处理...
    return Promise.reject(err)
  }
)

当你在开发过程中修改任意文件触发热更新后,再次发起请求,会发现控制台中 "响应拦截器执行" 被打印了 两次、四次甚至更多次

这说明:每次热更新都会重新执行 request.js 模块,从而重复注册新的拦截器。而旧的拦截器并未被清除,导致多个拦截器实例同时生效。

二、错误尝试:使用局部变量控制

很多开发者第一反应是用一个布尔变量来防止重复注册:

let isResponseRegistered = false
 
if (!isResponseRegistered) {
  axios.interceptors.response.use(...)
  isResponseRegistered = true
}

但你会发现,这个方案在热更新下依然失效——控制台还是打印了两次日志。

为什么?

因为 热更新会重新执行整个模块(包括变量声明)。也就是说,每次 HMR 触发时,isResponseRegistered 都会被重置为 false,于是拦截器又被注册了一次。

关键点:模块级变量在热更新时会被重新初始化,无法跨更新周期保持状态。

三、正确方案:将标志挂载到 Axios 实例上

要解决这个问题,必须使用一个 不会被热更新重置的对象属性 来记录是否已注册拦截器。

Axios 本身是一个对象(函数对象),我们可以直接在其上挂载自定义属性:

// 防止热更新重复注册响应拦截器
if (!axios.__myResponseInterceptor__) {
  axios.interceptors.response.use(
    (res) => {
      console.log('响应拦截器执行')
      // ...处理逻辑
      return res.data
    },
    (err) => {
      return Promise.reject(err)
    }
  )
  axios.__myResponseInterceptor__ = true // 标记已注册
}

为什么这个方案有效?

这是一种被社区广泛验证的有效做法,类似方案也用于防止重复注册全局组件、插件等。

四、完整代码示例(来自实际项目)

以下是从你提供的 request.js 中提取的核心逻辑:

// 防止响应拦截器被重复注册
if (!axios.__myResponseInterceptor__) {
  axios.interceptors.response.use(
    (res) => {
      // 二进制数据直接返回
      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 // 关键:标记已注册
}

该方案成功解决了热更新下的重复注册问题,且不影响生产环境(生产环境无 HMR,只执行一次)。

五、其他可行方案(补充)

使用 import.meta.hot?.accept() 手动管理副作用(Vite 特有)
可在模块卸载时移除拦截器,但实现复杂,不推荐。

将拦截器注册移到 main.js 或入口文件
减少被 HMR 影响的概率,但若入口文件也被更新,仍可能失效。

使用单例模式封装 Axios 实例
例如导出一个 createAxiosInstance() 函数,并缓存实例。但需确保调用方不重复创建。

相比之下,挂载标志位到 axios 对象上是最简单、可靠、低侵入性的方案。

六、总结

方案是否有效原因
局部变量 let flag = false热更新重置变量
挂载到 axios 对象上axios 模块稳定,属性持久
移到 main.js⚠️降低概率,但非根治
手动 HMR 清理✅ 但复杂需要监听模块更新事件

最佳实践:在开发 Axios 封装库时,务必考虑 HMR 场景,使用 axios.__xxx__ 标志位防止重复注册拦截器。

希望本文能帮助你彻底理解并解决这一“开发期幽灵 bug”。

到此这篇关于Vue+Axios热更新导致响应拦截器重复注册问题的解决方案的文章就介绍到这了,更多相关Vue Axios热更新导致拦截器重复注册内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文