在某些情况,需要添加的路由不确定,需要从后端获取数据,并且后端更新相关的路由时,页面也能够立即渲染出来,这时候就需要使用动态路由。
例如商城后台项目,菜单中的很多路由都是不确定的,即使你写了10个路由,但是后端那边新增了10个路由,那么这时候设置动态添加路由后,就可以自动在第一时间创建出所有的路由,而不需要你手动的写了。
1、设置默认路由以及动态路由
① 这里需要注意动态路由和默认路由都需要加上name
② 动态路由的name建议和path保持一致,不然有可能会出现无限重定向
③ 默认路由的name不需要和path保持一致,取个合适的名字就好
import { createRouter, createWebHashHistory } from "vue-router";
//默认路由
const routes=[
{
path:'/',
name:'admin',
component:Admin,
}
]
//动态路由
const asyncRoutes = [
{
path: '/',
name: '/',
component: Index,
meta: {
title: '后台首页'
}
}, {
path: '/goods/list',
name: '/goods/list',
component: GoodList,
meta: {
title: '商品管理'
}
}, {
path: '/category/list',
name: '/category/list',
component: CategoryList,
meta: {
title: '分类列表'
}
}
]
2、制作动态添加路由方法
这个方法需要用到递归,因为路由可能包含子路由,也就是一级导航和二级导航,就是可以一层一层的往下找,如果匹配到后端给的对应的路由,并且这个路由不存在,那就自动添加到动态路由中进行渲染。
思路:
① arr这个后端传过来的路由数组遍历后,找到他每一条路由(e.frontpath),然后再从动态路由(asyncRoutes)中查找里面的路由是否和后端传过来的arr路由匹配。
② 如果匹配并且这个路由不存在(!router.hasRoute(item.path)),那么利用嵌套路由(router.addRoute(‘admin’,item))进行添加。
③ 再次判断子路由(e.child)是否存在并且有数据,(这个child需要你查看后端传过来的数据,可能字段是不一样的),如果子路由有数据,那么再次执行findAndAddRoutesByMenus()这个方法,实现递归。
//router下面的index.js
import { createRouter, createWebHashHistory } from "vue-router";
export const router = createRouter({
history: createWebHashHistory(),
routes
})
export const addRoutes = (menus)=>{
const findAndAddRoutesByMenus = (arr)=>{
arr.forEach(e => {
let item = asyncRoutes.find(o=>o.path == e.frontpath)
if(item && !router.hasRoute(item.path)){
router.addRoute('admin',item)
}
if(e.child && e.child.length){
findAndAddRoutesByMenus(e.child)
}
})
}
findAndAddRoutesByMenus(menus)
}
3、执行addRoutes ()方法
执行时机最好是获取到后端传来的路由数据之后,我这边是在全局前置守卫中利用vuex来获取数据,res.menu就是数据中的路由信息,那么在获取信息数据之后,执行addRoutes ()方法,并将res.menus传入方法中。
import { router, addRoutes } from '~/router'
router.beforeEach(async (to, from, next) => {
if (token) {
const res = await store.dispatch('getinfon')
addRoutes(res.menus)
}
})
4、特别要注意的一点
按照上述步骤完成后,你一刷新页面就会提示找不到页面,这里引用vue官方的原话:
动态路由主要通过两个函数实现。router.addRoute() 和 router.removeRoute()。它们只注册一个新的路由,也就是说,如果新增加的路由与当前位置相匹配,就需要你用 router.push() 或 router.replace() 来手动导航,才能显示该新路由。
思路:
① 在addRoutes 方法内创建一个hasNewRoute(是否有新的路由)变量,在动态添加路由后设置为true
② 在全局前置守卫中也定义相同的hasNewRoute变量,通过赋值的形式传递给hasNewRoute
③ 如果hasNewRoute为true,那么就手动跳转(next(to.fullPath))到对应的页面,否则直接next()
//router下面的index.js
import { createRouter, createWebHashHistory } from "vue-router";
export const addRoutes = (menus)=>{
let hasNewRoute = false
const findAndAddRoutesByMenus = (arr)=>{
arr.forEach(e => {
let item = asyncRoutes.find(o=>o.path == e.frontpath)
if(item && !router.hasRoute(item.path)){
router.addRoute('admin',item)
hasNewRoute=true
}
if(e.child && e.child.length){
findAndAddRoutesByMenus(e.child)
}
})
}
findAndAddRoutesByMenus(menus)
return hasNewRoute
}
import { router, addRoutes } from '~/router'
router.beforeEach(async (to, from, next) => {
let hasNewRoute = false
if (token) {
const res = await store.dispatch('getinfon')
hasNewRoute = addRoutes(res.menus)
}
hasNewRoute ? next(to.fullPath) : next()
})