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

Nuxt3自定义$fetch刷新Token后Pinia状态未更新问题

Nuxt3 + Pinia 401 Token重试后状态不更新问题解决

问题场景

开发Nuxt3应用时,自定义了$fetch插件处理API请求、Token过期刷新及失败重试,重试机制能成功获取新数据,但Pinia存储和组件状态未同步更新:

  • 组件创建时调用transactionsStore.fetchTransactionsList获取数据
  • 触发401后插件成功刷新Token并重试请求,但新数据未同步到Pinia和组件

核心原因

  1. useAsyncData 缓存与Promise链断裂:第一次请求401失败后,useAsyncData的Promise已进入reject状态,插件重试请求是独立发起的,未关联原useAsyncData实例,导致data.value无更新,Pinia状态自然不会同步。
  2. 重试结果未反馈给原调用逻辑:插件中重试队列的请求仅被调用,未将结果返回给fetchTransactionsList方法,Pinia内的状态赋值代码无法重新执行。

解决方案

1. 修复插件的错误处理逻辑,确保重试结果返回给原Promise

调整handleResponseError函数,让原请求Promise等待重试结果,并处理队列请求的resolve:

function handleResponseError(nuxtApp, authStore) {
  return async ({ request: requestURL, options, response }) => {
    const isAuthenticated = authStore?.isAuthenticated;

    if (response.status === 401 && isAuthenticated) {
      if (!isRefreshingToken) {
        isRefreshingToken = true;
        refreshAttempts++;

        if (refreshAttempts > MAX_REFRESH_ATTEMPTS) {
          handleLogout(nuxtApp, authStore);
          throw new Error('刷新次数超限');
        }

        try {
          const refreshResponse = await authStore.requestNewAccessToken();

          if (refreshResponse.success) {
            // 重试原请求并返回结果
            const retryResult = await api(requestURL, { ...options });
            // 处理队列请求,调用resolve返回结果
            await Promise.all(refreshAndRetryQueue.map(({ requestURL, options, resolve }) => 
              api(requestURL, { ...options }).then(resolve)
            ));
            refreshAndRetryQueue.length = 0;
            return retryResult; // 让原Promise拿到重试结果
          } else {
            handleLogout(nuxtApp, authStore);
            throw new Error('Token刷新失败');
          }
        } catch (err) {
          handleLogout(nuxtApp, authStore);
          throw err;
        } finally {
          isRefreshingToken = false;
        }
      } else {
        // 排队时返回Promise,等待刷新完成后重试
        return new Promise((resolve) => {
          refreshAndRetryQueue.push({
            requestURL,
            options,
            resolve // 保存resolve函数,后续重试后触发
          });
        });
      }
    }
    // 其他错误直接抛出
    throw response;
  };
}

2. 调整Pinia的action逻辑,移除useAsyncData缓存干扰

改用原生async/await手动管理状态,避免useAsyncData的缓存阻碍重试结果更新:

async fetchTransactionsList(query) {
  const api = useApiRepository();
  this.transactionsStatus = 'pending';
  try {
    const response = await api.getTransactionsList({
      ...query,
      per_page: 20,
    });
    this.transactionsData = response.data ?? {};
    this.transactionsStatus = 'success';
    this.transactionsError = null;
    return { status: 'success', data: response, error: null };
  } catch (error) {
    this.transactionsStatus = 'error';
    this.transactionsError = error;
    return { status: 'error', data: null, error };
  }
}

3. 确保请求头使用最新Token

在handleRequest函数中,每次请求都从authStore获取最新Token,保证重试请求携带新Token:

function handleRequest({ options }) {
  const authStore = useAuthStore();
  if (authStore.accessToken) {
    options.headers = {
      ...options.headers,
      Authorization: `Bearer ${authStore.accessToken}`
    };
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 13:32:12