手摸手教你实现vue的动态路由(router.v4.x版本)

6,984 阅读4分钟

前言

  • 路由版本 ,router.4x
"vue-router": "^4.0.0-0",

动态路由用到的router4api

官方文档

1、 获取路由列表 getRoutes()
2、 添加路由 addRoute(name) 为某个路由添加单个路由, 注意不是addRoutes(), 已废弃!!!
3、 删除路由 removeRoute(name) 删除某个路由,注销的时候删除添加的路由。

整体流程

1、用户登陆,获取后端返回的路由, 存入vuex
2、使用router.addRoute动态添加到路由
3、使用router.getRoutes读取路由
4、注销的时候,removeRoute删除添加的路由 LF1XL_(01CT87D[5JP2T]XY.png

实现效果

不同用户登陆显示不同侧边路由导航

y1DBTiDKwc.gif

核心代码介绍

1、模拟的动态路由列表

//模拟后端传过来的路由
export const authRouter = [
  {
    path: "/allSeePage",
    name: "所有人可见",
    component: "allSeePage",
    meta: {
      isSideBar: 1
    }
  },
  {
    path: "/adminPage",
    name: "管理员可见",
    component: "adminPage",
    meta: {
      isSideBar: 1
    }
  }
]

2、登陆添加路由

  • 登陆事件
const onSubmit = () => {
//触发登陆,保存信息,添加路由
  store.dispatch("login", userInfo.value).then(() => {
    console.log("登陆跳转")
    router.push({ path: "/home" })
  })
}
  • vuex-添加路由, 刷新后路由会消失, 我们要重新添加
ADD_ROUTE(state) {
  console.log("路由添加前", router.getRoutes())
  //路由未添加之前是3个,我们判断是否添加过,没添加过就添加
  if (router.getRoutes().length === 3) {
    let addRouterList = filterAsyncRouter(
      JSON.parse(JSON.stringify(state.userInfo.routerList)) //这里深拷贝下,不然会出问题
    )
    addRouterList.forEach((i) => {
      console.log("添加路由", i)
      router.addRoute("home", i)
    })
  }
  console.log("路由添加后", router.getRoutes())
}

3、渲染菜单

利用router.getRoutes(),获取已经添加的路由,此方法会把所有层级变为一层
注意:这里获取到的route对象与router3.x的版本不一样,route没有parent属性了, 通过meta来判断是否是菜单

const routerList = router.getRoutes() //这里获取到的route对象与router3.x的版本不一样,需要注意下
menuList.value = routerList.filter((route) => {
  const isSidebar = route.meta.isSideBar ? route.meta.isSideBar : 0
  if (isSidebar) {
    return route
  }
})

4、 vuex注销-删除路由

//注销
logout({ commit, state }) {
  return new Promise((resolve) => {
    console.log(state.userInfo.token, "注销了")
    //拷贝一下
    const delRouterList = JSON.parse(
      JSON.stringify(state.userInfo.routerList)
    )
    //删除添加的路由,如果路由是多层的 递归下。。
    delRouterList.forEach((route) => {
      router.removeRoute(route.name)
    })
    //删除路由,清空用户信息
    commit("SET_USER_INFO", {
      userName: "",
      password: "",
      token: "",
      routerList: []
    })
    resolve("注销 success, 清空路由,用户信息")
  })
}

路由钩子函数beforeEach

  • 刷新后触发vuex的添加路由

image.png

  • 代码
router.beforeEach(async (to, from, next) => {
  //获取用户信息
  let { userInfo } = store.state
  const { username } = userInfo
  console.log("用户角色", username ? username : "未登陆")
  //有用户信息
  if (username) {
    //触发添加路由方法,里面会判断是否需要添加
    await store.dispatch("addRoute")
    let { routerList } = userInfo
    //根据to.name来判断是否为动态路由, 是否有人知道还有更好的判断方法?
    if (!to.name) {
      //当前路由是动态的,确定是有的, 有就跳自己,没有就跳404,, tip: 刷新后动态路由的to.name为空
      if (routerList.findIndex((i) => i.path === to.path) !== -1) {
        next({ ...to, replace: true })
      } else {
        next("/404")
      }
    } else {
      console.log(28, router.getRoutes())
      next()
    }
  }
  //无用户信息
  else {
    //没有权限访问,跳入没有权限页面/或者登陆页面
    // 跳转之前要判断一下是否为需要跳转的界面,不然会进入死循环
    if (to.path === "/login") {
      next()
    } else {
      ElMessage.error("请先登陆!")
      next("/login")
    }
  }
})

路由组件动态component引入

  • filterAsyncRouter 路由懒加载导入, 路由的component与前端文件路径绑定,支持多层嵌套
  • 注意 这里引入与router3不一样已经备注。
export const loadView = (view) => {
  // 路由懒加载
  //return (resolve) => require([`@/views/${view}`], resolve) router3版本
  return () => Promise.resolve(require(`@/views/${view}`).default) //router4版本
}
//为权限路由异步添加组件
export const filterAsyncRouter = (routeList) => {
  return routeList.filter((route) => {
    console.log(9, route)
    if (route.component) {
      // 如果不是布局组件就只能是页面的引用了
      // 利用懒加载函数将实际页面赋值给它
      route.component = loadView(route.component)
      // 判断是否存在子路由,并递归调用自己
      if (route.children && route.children.length) {
        route.children = filterAsyncRouter(route.children)
      }
      return true
    }
  })
}

遇到的坑

1、router.getRoutes获取的路由层级只有一层!。4.0的路由版本已经有children属性了。
2、永远不要使用router.options.routes来获取路由,因为动态修改路由这里面不会变化。
3、路由f5刷新后,之前动态添加的路由都会丢失。。 需要重新添加
4、404页面,钩子函数内进行判断就好。 不要写这句 { path: '*', redirect: '/404' }

可以参考花裤衩大佬的提问

写在最后

  • 代码内可能有Bug,希望可以在仓库issues提出。
  • beforeEach判断是否为动态路由那是否有更好的解决方式?
  • 代码地址-vue3-dynamic-router
  • 感谢阅读✿