页面重载时调用函数修改状态,Computed状态未更新
问题原因及解决方案
核心原因分析
路由守卫异步执行时机冲突
直接访问/notifications时,若isLoggedIn为false,beforeResolve会触发refreshAccessToken异步操作,但组件的<script setup>会在路由守卫的异步操作完成前执行retrieveNotificationList,此时请求携带的是过期token,导致接口请求失败,notifications状态无法更新。而从其他页面跳转时,isLoggedIn已为true,无需刷新token,请求能正常获取数据。组件未规范使用Pinia Store实例
组件中多次调用useStore()(尽管Pinia保证单例),且未在合适的生命周期钩子中执行数据请求,同时retrieveNotificationList未抛出错误,导致请求失败时无法及时排查问题。路由守卫顺序错误
原代码中beforeResolve写在beforeEach之前,不符合Vue Router的执行顺序(正确顺序是beforeEach→beforeResolve→afterEach),可能导致登录校验逻辑执行顺序混乱。
解决方案
1. 调整路由守卫顺序并完善错误处理
// 先执行登录校验的beforeEach router.beforeEach(async (to, from, next) => { if(to.matched.some(record => record.meta.requiresLogin && !isLoggedIn)){ next({name: 'login'}) } else{ next() } }) // 再执行token刷新的beforeResolve router.beforeResolve((to, from, next) => { if (!isLoggedIn) { useAuthStore().refreshAccessToken() .then(() => { // 刷新成功后更新isLoggedIn状态 isLoggedIn = true next() }) .catch(() => { // 刷新失败跳转登录页 next({name: 'login'}) }) } else { next() } })
2. 规范组件中Store的使用及请求时机
将数据请求放在onMounted钩子中,并复用同一个Store实例,同时处理请求失败的情况:
<template> <router-link to="/">Home</router-link> | <div :class="{'read':notification.read}" v-for="notification in notifications" :key="notification.id" class="notification"> <p>{{ notification.message }}</p> <button @click="deleteNotification(notification.id)">X</button> </div> </template> <script setup> import { useStore } from '../store/index' import { computed, onMounted } from 'vue' // 提前获取Store实例,统一复用 const store = useStore() // 在组件挂载后执行数据请求,确保路由守卫的异步操作已完成 onMounted(async () => { try { await store.retrieveNotificationList() } catch (err) { console.error('获取通知列表失败:', err) // 可在此处添加错误提示逻辑 } }) const notifications = computed(() => { return store.notifications }) function deleteNotification(id) { store.deleteNotification(id) } </script>
3. 完善Store中action的错误处理
修改retrieveNotificationList,在请求失败时抛出错误,方便组件捕获处理:
export const useStore = defineStore('core', { state: () => { return { notifications: null } }, actions: { retrieveNotificationList() { return new Promise((resolve, reject) => { getAPI.get('/api/notifications/') .then(response => { this.notifications = response.data resolve() }) .catch(err => { console.error(err) reject(err) // 抛出错误,让调用方可以捕获 }) }) }, deleteNotification(id) { getAPI.delete('/api/notifications/' + id) .then(() => this.notifications = this.notifications.filter(item => item.id !== id)) .catch(err => console.error('删除通知失败:', err)) } } })
4. 确保请求拦截器使用最新token
检查getAPI的请求拦截器,确保每次请求都携带最新的token(从AuthStore中获取):
// 示例:axios请求拦截器 getAPI.interceptors.request.use(config => { const authStore = useAuthStore() if (authStore.accessToken) { config.headers.Authorization = `Bearer ${authStore.accessToken}` } return config })
内容的提问来源于stack exchange,提问作者Deku
相关产品推荐
相关产品推荐

