动态路由实现原理

189 阅读4分钟

仅简单了实现动态路由权限管理 并没有考虑安全,性能方面,等等其他的因素,简单的一个实现思路(估计还有其他Bug)

  1. 首先我们要在Router里面将静态和动态路由进行区分
  • 静态就是没有权限所有用户都可以访问到的页面,把他们拿出来单独放进一个数组中并导出

  • 动态路由同上设置

    补充:addRoutes的基本使用(作用:动态添加路由配置)

  • router.addRoutes([路由配置对象]) 或者 this.$router.addRoutes([路由配置对象])

  • 示例: 这里可以自己手写一下看一下简单的实现原理

    效果: 点击了按钮之后,就可以在地址中访问/abc了。

    // 按钮
    <button @click="hAddRoute">addRoute</button>
    // 回调
    hAddRoute() {
    	this.$router.addRoutes([{
    		path: '/abc',
    		component: () => import('@/views/abc'),
    		}])
    },
    
  1. 当前的权限菜单渲染实现(src\layout\components\Sidebar\index.vue)使用的数据:this.$router.options.routes 这个数据是固定的,(这个方法只能识别到静态的路由) 因为在router中设置了return 静态路由数组,我们通过addRoutes添加的路由表只存在内存中,并不会改变权限菜单的最终显示 __仅个人看法 了解不是太深~

    • routes() {
           1.return this.$router.options.routes 原来的数据
           2.return this.$router.options.routes.concat(this.$store.state.menu.menuList) 改写为这个
           这里拿到的是一个完整的包含了静态路由和动态路由的数据结构 concat()方法 可以将两个数组合并为一个
           
         },
      
  2. 如果我们希望在调用addRoutes方法之后,要路由数据立刻反映到菜单中,我们需要想一个额外的方法 vuex!

    • 在vuex里面定义管理菜单数据需要的数据和方法

      export default {
        namespaced: true,
        state: {
          // 路由菜单数据的初始值
          menuList: []
        },
        mutations: {
          setMenuList(state, asyncRoutes) {
            // 将动态路由添加起进去
            state.menuList = asyncRoutes
          }
        }
      }
      
  3. 在permission.js中引入我们第一步定义的动态路由数组(用来判断用户的路由信息是否存在于我们写的动态路由中)

    import router, { asyncRouters } from '@/router/index'
    
    asyncRouters 就是我自己在路由定义的动态路由数组的名称
    export const asyncRouters=[
        这里面就是商品管理等等的...那些动态路由的信息,直接 CV 把他们单独拿出来重新定义一个数组导出
    ]
    
    • 在全局路由守卫里面进行判断 (我这里是直接把方法全部写在全局守卫里面了,不推荐,可以封装为方法放在vuex里面在这里面调用更优雅)

    • 注意点:

      • 1.获取用户权限信息后筛选出来的路由名称是和后端约定一致的,你自己的路由名称必须和获取到的保持一致

      • 2.刷新浏览器,会发现跳到了404页面 现在我们的路由设置中的404页处在中间位置而不是所有路由的末尾了

        从route/index.js中的静态路由中删除path:'*'这一项

        // 不需要特殊的权限控制就可以访问的页面
        export const constantRoutes = [
          {
            path: '/login',
            component: () => import('@/views/login/index'),
            hidden: true
          },
          
          // 404 page must be placed at the end !!!
        -  { path: '*', redirect: '/404', hidden: true } //删了直接
        ]
        

        在permission.js中补充在最后

          // 把404加到最后一条
        +  filterRoutes.push( // 404 page must be placed at the end !!!
            { path: '*', redirect: '/404', hidden: true })
        
      • 3.解决刷新出现的白屏bug 这个问题基本你使用了动态添加路由都会出现这个问题(原因百度了解,不是太难)

          // 解决刷新出现的白屏bug
          next({
            ...to, // next({ ...to })的目的,是保证路由添加完了再进入页面 (可以理解为重进一次)
            replace: true // 重进一次, 不保留重复历史
          })
        

      具体代码

      router.beforeEach(async(to, from, next) => {
        // router.addRoutes(store.state.user.menuList)
        document.title = '乐居-' + to.meta.title
        // 进度条
        NProgress.start()
        // 获取到token
        const token = store.state.user.token
        if (token) {
          //此处省略
          } else {
            // 如果路由列表数组大于0了就直接放行
            // 否则可能会进入死循环 (这里可以先把这句去掉自己体验下)
            if (store.state.menu.menuList.length > 0) {
              next()
            } else {
              // 获取到用户的权限信息 就是我们自己分配给这个用户的权限信息
              const res = await getInitMenus()
              // 获取用户的权限信息路由名称
              const menuList = res.data.data.permissionList.map(item => {
                return item.name
              })
              console.log(menuList, '用户权限')
              // 检测用户的路由权限名称在不在动态路由权限列表中
              // 这里的 asyncRouters 就是我们引入进来的动态路由列表
              const fliterRouter = asyncRouters.filter(item => {
                const routerName = item.name
                console.log(routerName)
                return menuList.includes(routerName)
              })
              console.log(fliterRouter)
              // 把404页面动态添加到整个路由的最后面
              fliterRouter.push({ path: '*', redirect: '/404', hidden: true })
              // 在的话 动态添加路由 根据用户权限信息设置路由信息
              router.addRoutes(fliterRouter)
              // 将左侧菜单列表通过vuex渲染到页面中 因为原本无法渲染动态列表
              store.commit('menu/setMenuList', fliterRouter)
              // next()
              next({
                ...to, // next({ ...to })的目的,是保证路由添加完了再进入页面 (可以理解为重进一次)
                replace: true // 重进一次, 不保留重复历史
              })
            }
          }
      
    1. 退出登陆时要重置路由信息

      路由设置是通过router.addRoutes(filterRoutes)来添加的,退出时,并没有清空,再次登陆,又加了一次,所以有重复。

      需要将路由权限重置 (恢复默认) 将来登录后再次追加才可以,不然的话,就会重复添加

      router/index.js文件,有一个重置路由方法 把这个方法放在到你退出登录时的那个函数里面调用即可

      // 重置路由
      export function resetRouter() {
        const newRouter = createRouter()
        router.matcher = newRouter.matcher // 重新设置路由的可匹配路径
      }
      
      import { resetRouter } from '@/router'
      // 退出的action操作
      logout(context) {
        // 1. 移除vuex个人信息
        context.commit('removeUserInfo')
        // 2. 移除token信息
        context.commit('removeToken')
        // 3. 重置路由
        resetRouter()
      }
      
    2. 最后在登录请求发送前 把vuex里面的menuList存储路由信息表清空 否则会有一个Bug 自测