Vue Router守卫中如何等待Vuex的Axios API完成再校验权限
路由守卫await不生效的核心原因
- 绝大多数场景下问题出在Vuex的
FETCH_MEaction没有正确返回Promise:如果写action时只是在内部调用了axios请求,没有把异步请求的结果作为返回值抛出,那store.dispatch()方法拿到的返回值是undefined,此时不管加多少await,都没法等待接口完成 - 其次容易踩的坑是没有把
router.beforeEach的守卫回调标记为async函数,await语法在普通同步函数作用域里本身就无法正常生效
可直接落地的修改步骤
1. 修正Vuex中FETCH_ME action的写法
先看反面的错误写法,这是最常见的问题来源:
// 错误写法:没有return异步结果,dispatch返回值为undefined actions: { FETCH_ME({ commit }) { axios.get('/api/me').then(res => { commit('SET_ME', res.data) }) } }
改成带async标记、正确return结果的写法:
// 正确写法:整个action为async函数,完整返回异步请求的Promise actions: { async FETCH_ME({ commit }) { const res = await axios.get('/api/me') commit('SET_ME', res.data) return res // 这行return是await能生效的核心,不能省 } }
2. 重写全局路由守卫为async回调
确保守卫本身是async函数,逻辑顺序调整为:先判断登录态,没有me就等待接口拉取,接口完成后再执行权限校验:
router.beforeEach(async (to, from, next) => { // 第一步:读取本地登录标识,无token时不需要拉取用户信息 const token = localStorage.getItem('access_token') if (!token) { // 公开路由直接放行,其余跳登录页 return to.meta.isPublic ? next() : next('/login') } // 第二步:从store读取当前用户信息 let currentMe = store.state.user.me // 第三步:如果store中没有me(页面刷新后的清空场景),就等待接口拉取 if (!currentMe) { try { // 此时因为action已经正确返回Promise,await会等接口完全完成后再执行后续逻辑 await store.dispatch('FETCH_ME') // 拉取完成后重新从store取最新的me值 currentMe = store.state.user.me } catch (err) { // 接口失败(比如token过期),清除本地登录态,跳登录页 localStorage.removeItem('access_token') return next('/login') } } // 第四步:接口数据拿到手之后,再执行权限校验 const allowed = hasPermission(currentMe, to.meta.requiredRole) if (allowed) { next() } else { next('/403') } })
常见优化补充
- 不要用固定2秒的sleep临时方案:网络慢时接口2秒内返回不了还是会出问题,网络快时又会无意义拉长路由跳转等待时间,体验很差
- 可以在store中加个请求状态标记:如果
FETCH_ME已经在请求流程中,后续触发的路由守卫不需要重复发起dispatch,直接复用正在等待的Promise即可,避免短时间内重复调用用户信息接口 - 校验逻辑里一定要给await的接口请求加catch捕获,避免接口异常时路由守卫直接卡死
内容的提问来源于stack exchange,提问作者lbris
相关产品推荐
相关产品推荐

