vue 动态路由的实现
与后端同学定制路由结构 (以下为json)
后端会根据当前用户权限动态返回路由结构 前端不再需要考虑权限问题
[{ "id": 1, "name": "Nested", "code": null, "description": null, "url": "/nested", "generatemenu": 0, "sort": 0, "parentId": null, "permName": null, "redirect": "/nested/menu1", "title": "Nested", "icon": "nested", "children": [{ "id": 2, "name": "Menu1", "code": null, "description": null, "url": "menu1", "generatemenu": 0, "sort": 0, "parentId": 1, "permName": null, "redirect": "", "title": "Menu1", "icon": "menu1", "children": [{ "id": 4, "name": "Menu1-1", "code": null, "description": null, "url": "menu1-1", "generatemenu": 0, "sort": 0, "parentId": 2, "permName": null, "redirect": "", "title": "Menu1-1", "icon": "", "children": null }, { "id": 5, "name": "Menu1-2", "code": null, "description": null, "url": "menu1-2", "generatemenu": 0, "sort": 0, "parentId": 2, "permName": null, "redirect": "", "title": "Menu1-2", "icon": "", "children": null }]
}, { "id": 3, "name": "Menu2", "code": null, "description": null, "url": "menu2", "generatemenu": 0, "sort": 0, "parentId": 1, "permName": null, "redirect": "", "title": "Menu2", "icon": "menu2", "children": null }]
}] 复制代码
解析后端初始路由数据为可用数据
当然这不是直接用于渲染路由 我们需要进行递归处理成为我们想要的数据
router/_import
export default file => { return map[file] || null
}
const map = {
Nested: () => import('@/views/layout/Layout'),
Menu1: () => import('@/views/nested/menu1/index'), 'Menu1-1': () => import('@/views/nested/menu1/menu1-1'), 'Menu1-2': () => import('@/views/nested/menu1/menu1-2')
} 复制代码
处理后端原始路由数据
../utils/addRouter
递归写入比之前版本的递归删除更加稳定,代码量也更少
import _import from '../router/_import' // 获取组件的方法 /**
* 生成路由
* @param {Array} routerlist 格式化路由
* @returns
*/ export function addRouter(routerlist) { const router = []
routerlist.forEach(e => { let e_new = { path: e.url,
name: e.name,
component: _import(e.name)
} if (e.children) {
e_new = Object.assign({}, e_new, { children: addRouter(e.children) })
} if (e.redirect) {
e_new = Object.assign({}, e_new, { redirect: e.redirect })
} if (e.generatemenu == 0) {
e_new = Object.assign({}, e_new, { hidden: true })
} if (e.icon !== '' && e.title !== '') {
e_new = Object.assign({}, e_new, { meta: { title: e.title, icon: e.icon }
})
} else if (e.title !== '' && e.icon === '') {
e_new = Object.assign({}, e_new, { meta: { title: e.title }})
}
router.push(e_new)
}) return router
} 复制代码
处理后的路由
我们处理后的路由后面需要与现有的router进行拼接,这里需要根据需求 修改处理路由的规则
[{ "name": "Nested", "redirect": "/nested/menu1", "children": [{ "name": "Menu1", "children": [{ "name": "Menu1-1", "children": null, "path": "menu1-1", "meta": { "title": "Menu1-1" }
}, { "name": "Menu1-2", "children": null, "path": "menu1-2", "meta": { "title": "Menu1-2" }
}], "path": "menu1", "meta": { "title": "Menu1", "icon": "menu1" }
}, { "name": "Menu2", "children": null, "path": "menu2", "component": null, "meta": { "title": "Menu2", "icon": "menu2" }
}], "path": "/nested", "meta": { "title": "Nested", "icon": "nested" }
}] 复制代码
(核心)合并路由
以上的都是准备工作,就是为了将初始路由
与后端返回的动态路由
进行拼接
这部分代码也是优化的核心
import router from './router' import store from './store' import { getToken, removeToken } from './utils/auth' import NProgress from 'nprogress' // Progress 进度条 import 'nprogress/nprogress.css' // Progress 进度条样式 import { Message } from 'element-ui' import { getRouter } from './api/login' import { addRouter } from './utils/addRouter' const whiteList = ['/login'] var data = false // 本次demo用变量凑合一下,项目里面应该放到vuex内 router.beforeEach((to, from, next) => {
NProgress.start() if (getToken()) { // 判断cookice是否存在 不存在即为未登录 if (to.path !== '/login') { if (data) { // 获取了动态路由 data一定true,就无需再次请求 直接放行 next()
} else { // data为false,一定没有获取动态路由,就跳转到获取动态路由的方法 gotoRouter(to, next)
}
} else {
Message({ message: '您已经登录', type: 'info' })
next('/')
}
} else {
data = false if (whiteList.indexOf(to.path) !== -1) { // 免登陆白名单 直接进入 next()
} else { if (to.path !== '/login') { // 重定向到登录页面 不能这么写 因为假如之前的角色是 管理员页面 后又登陆了非管理员 重定向的页面就可能不存在,就会导致404 // next(`/login?redirect=${to.path}`) next('/login')
} else {
next()
}
}
}
})
router.afterEach(() => {
NProgress.done() // 结束Progress }) function gotoRouter(to, next) {
getRouter(store.getters.token) // 获取动态路由的方法 .then(res => { console.log('解析后端动态路由', res.data.data) const asyncRouter = addRouter(res.data.data) // 进行递归解析 // 一定不能写在静态路由里面,否则会出现,访问动态路由404的情况.所以在这列添加 asyncRouter.push({ path: '*', redirect: '/404', hidden: true }) return asyncRouter
})
.then(asyncRouter => {
router.addRoutes(asyncRouter) // vue-router提供的addRouter方法进行路由拼接 data = true // 记录路由获取状态 store.dispatch('setRouterList', asyncRouter) // 存储到vuex store.dispatch('GetInfo')
next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 })
.catch(e => { console.log(e)
removeToken()
})
} 复制代码
Vuex内部的逻辑
import { StaticRouterMap } from '../../router/index' state: { //..... RouterList: [] // 动态路由 },
mutations: { set_router: (state, RouterList) => {
state.RouterList = RouterList
}
},
action: { // 动态设置路由 此为设置设置途径 setRouterList({ commit }, routerList) {
commit('set_router', StaticRouterMap.concat(routerList)) // 进行路由拼接并存储 },
} 复制代码
相对之前的逻辑要简单很多
修改侧边栏的应用路由地址
需要注意的是 通过 addRoutes合并的路由 不会被this.$router.options.routes
获取到,所以需要将获取的路由拼接到this.$router.options.routes
上
最后修改渲染侧边栏部分部分的代码
src\views\layout\components\Sidebar\index.vue
computed: { // .... routes() { return this.$store.getters.routerList
}, // .... }
作者:小绿和小蓝
链接:https://juejin.im/post/5caeb3756fb9a068967791b3
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。