You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Nuxt页面与组件同时使用fetch导致refresh token接口重复调用问题

问题根因
  • 你当前场景下main页面和<Categories/>组件的请求是并行发起的,两个请求同时检测到access token过期,同时触发了refresh token逻辑。你现有的refresh逻辑没有做并发控制,所以会触发两次refresh调用。
  • 你把组件请求放到mounted中依旧复现的原因是:客户端渲染阶段两个请求仍然是近似同时发起,依旧存在并发冲突,和使用fetch还是mounted钩子无关。
  • 单请求页面不会出现该问题,是因为没有并发的过期请求触发多次refresh。
解决思路

方案1:改造refresh token公共逻辑(最推荐)

在你的请求拦截器中添加刷新锁和请求排队逻辑,不管有多少个并发过期请求,始终只调用1次refresh接口,不需要修改任何业务代码,组件可以保留自己的fetch逻辑。
示例代码(以axios拦截器为例,fetch封装同理):

// 全局变量,标记是否正在刷新token
let isRefreshing = false
// 等待刷新完成后重发的请求队列
let requestQueue = []

// 请求拦截器逻辑
axios.interceptors.request.use(config => {
  const accessToken = getAccessToken()
  if (accessToken) {
    config.headers.Authorization = `Bearer ${accessToken}`
  }
  return config
})

// 响应拦截器逻辑
axios.interceptors.response.use(
  response => response,
  async error => {
    const originalRequest = error.config
    // 判断是token过期错误
    if (error.response.status === 401 && !originalRequest._retry) {
      if (isRefreshing) {
        // 已经在刷新token,把当前请求加入队列等待
        return new Promise(resolve => {
          requestQueue.push(token => {
            originalRequest.headers.Authorization = `Bearer ${token}`
            resolve(axios(originalRequest))
          })
        })
      }

      originalRequest._retry = true
      isRefreshing = true

      try {
        // 调用refresh接口获取新token
        const newToken = await refreshToken()
        saveNewToken(newToken)
        // 重发队列里的所有请求
        requestQueue.forEach(cb => cb(newToken))
        requestQueue = []
        // 重发当前请求
        originalRequest.headers.Authorization = `Bearer ${newToken}`
        return axios(originalRequest)
      } catch (err) {
        // 刷新失败,跳转到登录页
        location.href = '/login'
        return Promise.reject(err)
      } finally {
        isRefreshing = false
      }
    }
    return Promise.reject(error)
  }
)

方案2:调整请求触发时机,避免并发

如果你暂时不想改造公共请求逻辑,可以手动错开两个请求的执行时间:

  1. 在main页面定义一个isMainRequestDone的响应式变量,初始值为false
  2. 等main页面的fetch逻辑执行完成后,把isMainRequestDone设为true
  3. 将该变量作为props传给<Categories/>组件,组件内监听该变量,当变为true时再发起自己的数据请求,从业务层面避免两个请求同时检测到token过期。

方案3:利用Nuxt的fetch调度逻辑

你可以给<Categories/>组件设置fetchOnServer: false,同时在页面的fetch完成后手动调用组件的$fetch方法:

<!-- main页面 -->
<template>
  <Categories ref="categories" />
</template>
<script>
export default {
  async fetch() {
    await this.$store.dispatch(
      "pub/getalll",
      "/api/u/all?limit=" + this.pageSize + "&page=" + this.currentPage
    );
    // main页面请求完成后再调用组件的fetch
    this.$nextTick(() => {
      this.$refs.categories.$fetch()
    })
  }
}
</script>

对应的<Categories/>组件设置fetchOnServer: false,避免服务端提前执行fetch。

内容的提问来源于stack exchange,提问作者eMan_8888

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 16:24:03