vue-router导航守卫动态菜单显示空白,刷新才恢复

本文介绍如何解决Vue.js应用中路由守卫导致的异步加载菜单问题。通过调整代码逻辑,确保动态菜单加载完成后才允许页面跳转,避免出现空白页现象。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原先的代码:

--- main.js ---

import router from './router'
import store from './store'

router.beforeEach((to, from, next) => {
  //如果存在token
  if (window.sessionStorage.getItem("tokenStr")) {
    //是不是要去登录页,登录了就不给去了
    if (to.path == '/') {
      next('/home')
    } else {
      //如果登录了并且不是去登录页,就获取一遍菜单
		initMenu(router,store)
      if (!window.sessionStorage.getItem('user')) {
        //如果没有用户信息就获取
        return getRequest('/system/config/info').then(resp => {
          if (resp && resp.success != false) {
            //存入用户信息
            window.sessionStorage.setItem('user', JSON.stringify(resp))
            next()
          }
        })
      } else {
        next();
      }
    }
  } else {
    //不存在token要去登录页就放行,不去登录页就强制进入登录页并带上重定向参数
    if (to.path == '/') {
      next();
    } else {
      next('/?redirect=' + to.path);
    }
  }
})
--- menu.js ---

if (store.state.routes.length > 0) {
	next()
}
getRequest('/system/config/menu').then(data => {
  // console.log(data)
  if (data) {
    let fmtRoutes = formatRoutes(data);
    //添加到路由
    console.log("添加菜单")
    router.addRoutes(fmtRoutes)
    store.commit("initRoutes", fmtRoutes);
  }
})

export const formatRoutes = (routes) => {
    let fmtRoutes = [];
    routes.forEach(router => {
        let {
            path,
            component,
            name,
            iconCls,
            children,
        } = router;
        if (children && children instanceof Array) {
            //递归
            children = formatRoutes(children);
        }
        let fmtRouter = {
            path: path,
            name: name,
            iconCls: iconCls,
            children: children,
            component(resolve) {
                if (component.startsWith("Home")) {
                    require(['../views/' + component + '.vue'], resolve);
                } else if (component.startsWith("Manage")) {
                    require(['../views/mng/' + component + '.vue'], resolve);
                } else if (component.startsWith("Sys")) {
                    require(['../views/sys/' + component + '.vue'], resolve);
                } else if (component.startsWith("Obj")) {
                    require(['../views/obj/' + component + '.vue'], resolve);
                }
            }
        }
        fmtRoutes.push(fmtRouter)
    })
    return fmtRoutes;
}

很显然我原先的方法是在路由导航守卫中进行条件判断然后调用获取动态菜单的方法,然而,经过调试,这种通过调用的方式,还没等菜单获取完,路由守卫就继续执行下去了,也就是异步执行,类似于ajax里面的嵌套,于是我们便得到了空白页。
所以,我们需要让动态菜单先加载完毕,我这边用的方法是把获取菜单的方法搬过来

修改后的代码:

--- main.js ---

router.beforeEach((to, from, next) => {
 //如果存在token
 if (window.sessionStorage.getItem("tokenStr")) {
   //是不是要去登录页,登录了就不给去了
   if (to.path == '/') {
     next('/home')
   } else {
     //如果登录了并且不是去登录页,就获取一遍菜单
     if (store.state.routes.length > 0) {
       if (!window.sessionStorage.getItem('user')) {
         //如果没有用户信息就获取
         console.log("无用户信息")
         return getRequest('/system/config/info').then(resp => {
           if (resp && resp.success != false) {
             //存入用户信息
             window.sessionStorage.setItem('user', JSON.stringify(resp))
             next()
             return;
           }
         })
       } else {
         next();
         return;
       }
     }
     getRequest('/system/config/menu').then(data => {
       // console.log(data)
       if (data) {
         //进行菜单的格式化操作
         let fmtRoutes = formatRoutes(data);
         //添加到路由
         console.log("添加菜单")
         router.addRoutes(fmtRoutes)
         store.commit("initRoutes", fmtRoutes);
         next({...to,replace:true})
       }
     })


   }
 } else {
   //不存在token要去登录页就放行,不去登录页就强制进入登录页并带上重定向参数
   if (to.path == '/') {
     next();
   } else {
     next('/?redirect=' + to.path);
   }
 }
})

但即使你搬过来,还是阻止不了异步执行,于是,在搬过来之后还多写了这么一个东西
next({...to,replace:true})(关于这句话的详细释义,我把参考文章放在下面链接了)
我的理解如下:意思是先暂时中断当前的路由守卫,带着…to的参数再去访问路由守卫,形成了一个嵌套,那第二遍执行的时候仍然会执行这句话,这不就无限嵌套了吗,所以为了防止无限嵌套,必须要有一个终止条件,那我的终止条件就是当我的菜单添加成功并添加到vuex中之后,然后再一层一层结束最后回到最外层,再顺序执行,这样就可以保证菜单先加载完了。

--- 终止条件 ---
if (store.state.routes.length > 0) {
  next()
  return
}

本文参考:https://blog.csdn.net/qq_41912398/article/details/109231418

<think>嗯,用户遇到了在Vue3中使用Pinia管理动态路由时,进入菜单功能正常,但刷新页面后出现空白的问题。我需要帮他们找出原因并提供解决方案。 首先,可能的原因是什么。动态路由通常是在用户登录后或根据权限从后端获取的,然后通过router.addRoute添加到Vue Router中。但刷新页面时,Vue应用会重新初始化,Pinia的状态可能丢失,导致动态路由没有被重新添加。这时候如果直接访问该路由,因为路由不存在,就会显示空白或者404。 那用户提到使用Pinia管理动态路由的状态。Pinia默认情况下是存储在内存中的,所以刷新后状态会丢失。所以需要持久化Pinia的状态,这样刷新时能恢复动态路由的数据。 接下来,步骤应该是:在应用初始化时,先恢复Pinia中的路由数据,然后重新添加动态路由,最后才挂载Vue实例。否则,可能会出现路由还未添加就渲染组件的情况,导致空白。 另外,检查路由守卫的设置是否正确。在进入路由前,确保动态路由已经加载完毕。如果路由守卫中没有等待动态路由加载,可能会导致跳转到不存在的路由。 还有一个可能是,动态路由对应的组件路径是否正确,或者组件是否被正确导入。如果组件路径错误,加载失败也会导致空白页面。 那具体的解决方案应该分几步: 1. 使用持久化插件,比如pinia-plugin-persistedstate,将Pinia中的路由数据保存到localStorage或sessionStorage,这样刷新后可以恢复数据。 2. 在应用启动时,先获取保存的路由数据,然后通过router.addRoute动态添加路由。确保在挂载Vue实例前完成路由的添加。 3. 在路由守卫中,检查动态路由是否已加载,如果没有,先加载再继续导航,避免路由不存在的问题。 然后,我需要给出具体的代码示例,比如如何在main.ts或路由守卫中处理恢复动态路由的逻辑,以及如何配置Pinia的持久化。 可能还需要处理404页面的情况,当用户刷新一个不存在的动态路由时,应重定向到首页或登录页。 另外,注意异步加载的问题。如果动态路由的数据是异步获取的,要确保在路由添加完成后再进行导航,可能需要使用await或Promise来处理异步操作。 最后,总结一下步骤,并提醒用户检查各个部分是否正确配置,比如Pinia的持久化设置、路由添加的时机、组件路径是否正确等。</think>在Vue3中使用Pinia管理动态路由时出现刷新页面空白的问题,可以通过以下步骤系统性解决: 一、问题原因分析 1. 动态路由未持久化:Pinia存储的菜单数据默认在内存中,刷新后丢失 2. 路由加载时序问题:Vue实例挂载时动态路由尚未完成注册 3. 异步加载未处理:动态路由加载未使用await导致注册未完成就渲染 二、解决方案实现 ```typescript // main.ts import { createApp } from &#39;vue&#39; import { createPinia } from &#39;pinia&#39; import App from &#39;./App.vue&#39; import router from &#39;./router&#39; // 创建带有持久化的Pinia实例 const pinia = createPinia() pinia.use(piniaPluginPersistedstate) // 需要安装持久化插件 const app = createApp(App) // 先初始化路由再挂载 async function initializeApp() { // 从持久化存储恢复路由 const routeStore = useRouteStore(pinia) await routeStore.restoreRoutes() // 从localStorage恢复路由数据 // 动态添加路由 routeStore.dynamicRoutes.forEach(route => { router.addRoute(route) }) // 挂载应用 app.use(pinia) app.use(router) app.mount(&#39;#app&#39;) } initializeApp() ``` 三、关键配置要点 1. Pinia持久化配置(推荐使用`pinia-plugin-persistedstate`): ```typescript // stores/routeStore.ts export const useRouteStore = defineStore(&#39;route&#39;, { state: () => ({ dynamicRoutes: [] as RouteRecordRaw[] }), persist: { storage: sessionStorage, // 推荐使用sessionStorage paths: [&#39;dynamicRoutes&#39;] } }) ``` 2. 路由守卫增强处理: ```typescript // router/index.ts router.beforeEach(async (to, from, next) => { const routeStore = useRouteStore() if (!routeStore.hasRoutes) { await routeStore.fetchRoutes() // 异步获取路由数据 routeStore.dynamicRoutes.forEach(route => { router.addRoute(route) }) return next(to.fullPath) // 重新跳转当前路由 } next() }) ``` 四、常见问题排查 1. 检查浏览器控制台是否存在以下错误: - 组件加载失败错误(需确认组件路径正确) - 路由重复注册警告(需检查路由重复添加问题) 2. 确保动态路由包含404回退处理: ```typescript router.addRoute({ path: &#39;/:pathMatch(.*)*&#39;, redirect: &#39;/404&#39; }) ``` 3. 络请求优化:建议在app初始化阶段预加载路由所需数据 五、最佳实践建议 1. 路由元信息标记:在动态路由中添加身份验证标记 ```typescript meta: { requiresAuth: true } ``` 2. 服务端配合:建议服务端返回路由的组件路径映射 ```json { "path": "/dashboard", "componentPath": "Dashboard/index.vue" } ``` 3. 使用import.meta.glob实现动态组件加载: ```typescript const modules = import.meta.glob(&#39;../views/**/*.vue&#39;) const component = modules[`../views/${componentPath}`] ``` 按照以上方案实施后,可有效解决动态路由刷新空白问题,同时保证路由状态的持久化和加载时序的正确性。如果仍有问题,建议检查浏览器存储是否被禁用,或组件路径是否存在大小写不一致问题。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值