关于router动态添加路由和菜单所遇到的坑和解决方法,记录以防止下次遇到
学习和借鉴了一下:花裤衩大佬github的编写风格,有想学习的可以去瞻仰一下大佬的风格
- 在store里面对获取的路由信息进行处理
import layout from "@/views/layout";
const menu = {
state: {
routers: [],
addRoutes: [],
},
mutations: {
//获取路由信息
SET_ROUTERS: (state, routers) => {
state.routers = routers
},
//获取拼装好的路由路径
SET_ADD_ROUTERS: (state, addRoutes) => {
state.addRoutes = addRoutes
},
},
actions: {
//获取路由信息
getRouters({commit}, menuData) {
commit('SET_ROUTERS', menuData.data)
let menu = filterChildren(menuData.data)
//添加路由
commit('SET_ADD_ROUTERS', menu)
//左侧菜单路由
commit('SET_SIDEBAR_ROUTERS',menu)
}
}
}
//拼接路由
function filterChildren(router) {
return router.filter((route) => {
//判断是否含有下级路由
if (route.children && route.children.length > 0) {
route.component = layout
route.children = filterChildren(route.children)
} else {
route.component = loadView(route.component)
}
return true
})
}
// 拼接路径
function loadView(view) {
return () => import(`@/views/${view}`);
}
export default menu
-
创建permission.js在根目录下,在main.js里面引用
import NProgress from "nprogress"; import store from "./store"; import router from "./router"; const NoLoginList = ['/clear'] import menuData from '@/assets/model/menuData.json' router.beforeEach(async (to, from, next) => { let token = store.state.user.token; if (token) { //有登录并在登录页,直接进入首页 if (to.path === "/login") { next("/"); } else { //非登录页直接放过 //模拟后台请求数据 await store .dispatch('getRouters', menuData) router.addRoutes(store.state.menu.addRoutes) next() } } else { //未登录,如果是免登录页面直接进入 if (NoLoginList.indexOf(to.path) !== -1) { next(); } else if (to.path === '/login') { //多加一步判断是否是去登录,以免进入路由死循环里 next() } else { //非登录页,也不是免登录页就跳转到登录页 next({path: "/login", query: {redirect: to.fullPath}}); } } });- 错误信息
[vue-router] router.addRoutes() is deprecated and has been removed in Vue Router 4. Use router.addRoute() instead.原因:addRoutes()已弃用,并且已在Vue路由器4中删除。 解决:改为addRoute()就可以了
-
记录报错内容
- 使用router.addRoute()
router.beforeEach(async (to, from, next) => { //其他代码省略 router.addRoute(store.state.menu.addRoutes) //其他代码省略 });运行报错信息
//错误:[vue路由器]“路径”在路由配置中是必需的 Uncaught (in promise) Error: [vue-router] "path" is required in a route configuration.原因:添加一条新的路由规则记录作为现有路由的子路由。不能直接添加数据类型的路由信息,需要一条一条的添加 如果该路由规则有 name,并且已经存在一个与之相同的名字,则会覆盖它
解决:一条一条添加就行
- 使用router.addRoute()
router.beforeEach(async (to, from, next) => { //其他代码省略 store.state.menu.addRoutes.forEach((item)=>{ router.addRoute(item) }) next() //其他代码省略 });运行报错信息:页面出现空白
在路由的前置守卫里面, 使用 addRoutes 钩子后, 直接调用 next() 如果当前页面的 路由 是通过 addRoutes 添加进去的 刷新页面就会出现 对应的路由组件不会渲染的情况, 也不会报错,页面会显示为空白
详细原因未知: 大致理解为, 当进入路由的前置钩子 (router.beforEach) 的时候, 路由的结构是不会发生变化的, 至少本次跳转, 路由的结构是不会变化的 也就是相当于我们举报一次活动,明天能看,但我们今天就要看一样,所以也就不能使用了
- 使用next({...to, replace: true})解决页面空白问题
router.beforeEach(async (to, from, next) => { //其他代码省略 store.state.menu.addRoutes.forEach((item)=>{ router.addRoute(item) }) // 这一行就是解决空白问题的最终办法, 重新进当前路由 next({...to, replace: true}) //其他代码省略 });运行报错信息:加入这行代码会造成页面陷入死循环,每次都去添加,重进当前路由, 解决方法:只使用一次:在初次加载时添加路由,其他页面路由跳转不在添加 4. 加入isAddRouter判断是否是初次加载动态路由
let isAddRouter = false router.beforeEach(async (to, from, next) => { //其他代码省略 //判断是否是初次进入渲染动态路由,当跳转其他路由时不要再次添加和获取 if (!isAddRouter) { store.state.menu.addRoutes.forEach((item) => { router.addRoute(item) }) // 这一行就是解决空白问题的最终办法, 重新进当前路由 next({...to, replace: true}) } else { next() } //其他代码省略 }); -
最终permission.js代码
import NProgress from "nprogress";
import store from "./store";
import router from "./router";
const NoLoginList = ['/clearToken'] //免登录页面
import menuData from '@/assets/model/menuData.json'
let isAddRouter = false
router.beforeEach(async (to, from, next) => {
NProgress.start()
let token = store.state.user.token;
if (token) {
//有登录并在登录页,直接进入首页
if (to.path === "/login") {
next("/");
} else {
//非登录页直接放过
//判断是否是初次进入渲染动态路由,当跳转其他路由时不要再次添加和获取
if (!isAddRouter) {
//模拟后台请求数据
await store
.dispatch('getRouters', menuData)
//渲染之后改变状态,截止动态路由的再次渲染
isAddRouter = true
// router.options.routes里的数据需要手动添加,
// router.addroutes/router.addroute添加的数据是不会自动到这里面的
router.options.routes = [...store.state.menu.addRoutes]
store.state.menu.addRoutes.forEach((item)=>{
router.addRoute(item)
})
// 这一行就是解决问题的最终办法, 重新进当前路由
next({...to, replace: true})
} else {
next()
}
}
NProgress.done()
} else {
//未登录,如果是免登录页面直接进入
if (NoLoginList.indexOf(to.path) !== -1) {
next();
} else if (to.path === '/login') {
//多加一步判断是否是去登录,以免进入路由死循环里
next()
} else {
//非登录页,也不是免登录页就跳转到登录页
next({path: "/login", query: {redirect: to.fullPath}});
}
NProgress.done()
}
});