Nuxt3自定义$fetch刷新Token后Pinia状态未更新问题
Nuxt3 + Pinia 401 Token重试后状态不更新问题解决
问题场景
开发Nuxt3应用时,自定义了$fetch插件处理API请求、Token过期刷新及失败重试,重试机制能成功获取新数据,但Pinia存储和组件状态未同步更新:
- 组件创建时调用
transactionsStore.fetchTransactionsList获取数据 - 触发401后插件成功刷新Token并重试请求,但新数据未同步到Pinia和组件
核心原因
- useAsyncData 缓存与Promise链断裂:第一次请求401失败后,
useAsyncData的Promise已进入reject状态,插件重试请求是独立发起的,未关联原useAsyncData实例,导致data.value无更新,Pinia状态自然不会同步。 - 重试结果未反馈给原调用逻辑:插件中重试队列的请求仅被调用,未将结果返回给
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
相关产品推荐
相关产品推荐

