- 在我的使用中,我把动态路由以下分为两种方案。按项目需求选取。
:较适合c端,根据用户等级,可体验产品不同功能。
-前端分好权限,把路由配置写死,后端返回该用户的角色之后,前端动态添加该角色有拥有权限的路由即可。(前后端交流较少)
:较适合后台管理系统,上级管理员给下级管理赋权各个层级页面。
-所有配置都由后端返回告知,前端动态渲染组件并配置路由。(前后端交流较多,返回的项目文件名称需交流清除)
- 方案一较为简单,这里只讲述方案二纯动态配置。
const nav=[{
path:'/demo1',
name:'demo1',
component:'demo1',
meta:{
title:'一级路由demo1'
},
children:[
{
path:'/demo2',
name:'demo2',
component:'demo2',
meta:{
title:'二级路由demo2'
},
}
]
},{
path:'/demo3',
name:'demo3',
component:'demo3',
meta:{
title:'一级路由demo3'
},
children:[
{
path:'/demo4',
name:'demo4',
component:'demo4',
meta:{
title:'一级路由demo4'
},
}
]
},]
缓存常选用vuex的原因:vuex相较于本地存储具有响应式的作用,vuex不直接暴露出来,安全性高。
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
isnav:false, //标记是添加了动态路由
},
getters: {
},
mutations: {
setnav(state)
{
state.isnav = true
}
},
actions: {
},
modules: {
}
})
用element-ui简单渲染出来
这里是home
{{item.meta.title}}
{{sonitem.meta.title}}
addRoutes在新版本已废弃,改用addRoute了
- 这里可以分为两种情况去添加配置,一种是在登录后进入白名单页面后拿到数据,进行动态路由添加。另一种是在访问,白名单页面之外的路由时,拦截下来,并添加路由配置,然后重新跳转页面。两种差异不大。
/*
views 目录结构
├── views
│ ├── home
│ └── index.vue #主页
│ ├── demo1
│ └── index.vue #一级动态路由页面
│ └── demo2.vue #二级动态路由页面
│ ├── demo3
│ └── index.vue #一级动态路由页面
│ └── demo4.vue #二级动态路由页面
*/
<1>先从简单一级动态路由配置开始
import store from "@/store"
// 定义白名单路由
const routes = [
{
path: '/home',
name: 'home',
component: ()=>import("@/views/home/index")
},
{
path:'/',
redirect:"/home"
}
]
const arr=["/",'/home']
const router = new VueRouter({
routes
})
// 将第一次白名单外的请求拦截下来,添加好动态配置,再跳转一次
router.beforeEach(async (to,from,next)=>{
if(arr.indexOf(to.path)<0)
{
if(store.state.isnav){
next()
}else{
const {data:res} = await getuserData()
const nav=res.nav
nav.forEach(item=>{
router.addRoute({
path:item.path,
name:item.name,
meta:item.meta,
component:()=>import(`@/views/${item.component}`)
})
})
store.commit("setnav")
next({path:to.path}) //这是不是放行next(),是重新跳转,
}
}else{
// 白名单页面直接放行
next()
}
})
这样就实现只有一级路由的动态配置了
<2>多层级动态路由配置
addRoute(parent,nav) 第一个参数传入该路由所添加在的父路由的path。
所以自己封装一个可递归函数,实现多层级动态路由添加,递归出口:不存在children节点。
/*
views 目录结构
├── views
│ ├── home
│ └── index.vue #主页
│ ├── demo1
│ └── index.vue #一级动态路由页面
│ └── demo2.vue #二级动态路由页面
│ ├── demo3
│ └── index.vue #一级动态路由页面
│ └── demo4.vue #二级动态路由页面
*/
import store from "@/store"
// 定义白名单路由
const routes = [
{
path: '/home',
name: 'home',
component: ()=>import("@/views/home/index")
},
{
path:'/',
redirect:"/home"
}
]
const arr=["/",'/home']
const router = new VueRouter({
routes
})
// 定义一个可递归的动态添加路由函数
// parent传入上一级路由的路径
async function addRoute(parent,nav)
{
if(parent)
{
// 有父节点则挂载在父节点之下
nav.forEach(item => {
router.addRoute(parent,{
//子路由地址必须加上父路由地址,根据后端数据返回自行决定添加
path:parent+item.path,
name:item.name,
// 导入路由模块地址,根据自己文件层级关系修改
component:()=>import("@/views"+parent+"/"+item.component),
})
// 若存在子路由,递归添加路由
if(item.children)
{
addRoute(item.path,item.children)
}
})
}else{
// 无父节点,第一个参数为空,全局挂载
nav.forEach(item => {
router.addRoute({
path:item.path,
name:item.name,
component:()=>import(`@/views/${item.component}/index`),
})
// 若存在子路由
if(item.children)
{
addRoute(item.path,item.children)
}
})
}
}
// 将第一次白名单外的请求拦截下来,添加好动态配置,再跳转一次
router.beforeEach(async (to,from,next)=>{
if(arr.indexOf(to.path)<0)
{
if(store.state.isnav){
next()
}else{
const {data:res} = await getuserData()
const nav=res.nav
addRoute(null,nav) //一级动态路由,无父路由传入null
store.commit("setnav")
next({path:to.path})
}
}else{
next()
}
})
export default router
<3>最后就可以加上白名单、token验证等花里胡哨的东西了。
import store from "@/store"
//设置默认路由
const routes = [
{ path: '/', redirect: '/login' },
{ path: '/login', component: pageLogin},
{ path: '/404', component: page404},
{ path: '*', redirect: '/404' },
];
const router = new VueRouter(routes)
// 设置默认路由白名单
const login_page=['/404','/login','/']
// 定义一个可递归的动态添加路由函数
// parent传入上一级路由的路径
async function addRoute(parent,nav)
{
if(parent)
{
// 有父节点则挂载在父节点之下
nav.forEach(item => {
router.addRoute(parent,{
//子路由地址必须加上父路由地址,根据后端数据返回自行决定添加
path:parent+item.path,
name:item.name,
// 导入路由模块地址,根据自己文件层级关系修改
component:()=>import("@/views"+parent+"/"+item.component),
})
// 若存在子路由,递归添加路由
if(item.children)
{
addRoute(item.path,item.children)
}
})
}else{
// 无父节点,第一个参数为空,全局挂载
nav.forEach(item => {
router.addRoute({
path:item.path,
name:item.name,
component:()=>import(`@/views/${item.component}/index`),
})
// 若存在子路由
if(item.children)
{
addRoute(item.path,item.children)
}
})
}
}
// 通过路由拦截动态添加路由配置
router.beforeEach(async(to,from,next)=>{
// 判断页面是否为白名单页面并验证token
if(login_page.indexOf(to.path)<0)
{
let accessToken=sessionStorage.getItem('accessToken')
if(accessToken) //token是否存在
{
let islegal=await axios.post("http://xx",{accessToken:accessToken)})
if(islegal)
{
// token验证通过后开始动态路由配置
// 查看是否缓存了路由配置
if(store.state.isnav)
{
// 存在路由缓存则可以放行
next()
}else{
// 获取动态配置
let {data:res} = await axios.get("http:xxxx")
let nav=res.nav
})
store.commit("setnav")
// 添加路由配置
addRoute(null,nav) //一级动态路由,无父路由传入null
// 标记路由已缓存
store.commit("setnav")
//完成拦截请求并载入路由规则
next({path:to.path}); //这里并不是放行,而是重新跳转,同router.push()一样
}
}else{
alert('登录已过期!请重新登录')
this.$router.push({name:"Login"})
}
}else{
alert('非法访问!请先登录')
this.$router.push({name:"Login"})
}
}else{
// 白名单页面放行
next()
}
})
export default router