Vue Router动态路由思路
一、路由存储方式
以下内容仅作为参考,如有错误内容还请指出
- 数据库存储
在数据库中新建路由表,发起用户登录请求由后端判断当前用户权限(管理员、普通用户)决定返回的data数据,其中数据中包含用户基本信息以及对应路由。 - 前端存储
正常填写路由数据,但需要在路由下标注该路由权限,发起用户登录后根据返回结果中的用户权限过滤路由信息。
二、数据处理
响应数据中data.router(路由树)应声明对应的store并做持久化存储,防止刷新后关闭网页后丢失菜单信息(根据个人需要,若token没有失效用户重新进入网页不需要登录,则应持久化处理后的菜单信息,方便回显)
// router格式
{
path:'', // router.push()对应路径
name:'', // 名称跳转(唯一标识)
// 不要在meta外添加自定义属性,因为在addRoute时自定义属性不会被识别
meta:{ // 元信息,可以添加自定义属性
title:'标题',
fullPath:'', // 跳转全路径,根据情况看后端处理还是前端处理
}
component:undefined,
children:[]
}
1. push跳转
在路由push跳转时,若路径以/开头则会被认为是顶级路由,router会从首层路由向下寻找,若不以/开头则替换当前路由的兄弟路由,如
/**
* login
* layout
* first
* second
* third
*/
假设当前处于/layout/first下,执行router.push(‘second’)会跳转至/layout/second下,但若执行router.push(‘/second’),router会寻找/second路由。因此在处理router时应存储跳转当前路由的全路径也就是fullPath会更为方便
2.路由添加方式
刚开始写demo时遇到了很多问题,这里做一下分享以及相关错误解释
- 创建router时将路由分为dynamicRouter与staticRouter两个对象,展开放入createRouter的routes下,在后续拿到处理好的路由时,遍历push到dynamicRouter的children数组上,由于push时修改的初始对象,理应能够将每一项都放在动态路由的子路由上,但实际上没有任何效果。
大概是因为导入的router对象在main.js中执行app.use(router)时children属性是空数组,而在登录后即使向该数组添加值,也不会被路由识别 - 在通过addRoute添加路由时,若被添加的路由path以/开头,即使addRoute使用了name参数该路由依旧会被当做顶级路由处理,而非作为name的子路由,因此children的path不要以/开头
3.component引入
// 在vite中提供了import.meta.glob(path)的方式引入component组件,path不能使用模板字符串
const modules = import.meta.glob('/src/views/**/*.vue')
import.meta.glob是vite提供的批量导入文件的方式,其中**代表匹配包含/在内的任意字符串.*代表不含/的任意字符串,因此上述函数会将view下所有的vue文件全部引入到modules,其返回值为map形式,可以直接使用modules[]路径导入文件
三、代码
1.后端返回数据
[
{
path: 'user',
name: 'user',
meta: {
title: '用户',
},
children: [
{
// 认为从父级到子级path的拼接最终组件的访问push路径
path: 'userInfo',
name: 'userInfo',
meta: {
title: '用户信息',
},
},
{
path: 'userAvatar',
name: 'userAvatar',
meta: {
title: '用户头像',
},
},
],
},
{
path: 'count',
name: 'count',
meta: {
title: '账号',
},
children: [
{
// 认为从父级到子级path的拼接最终组件的访问push路径
path: 'password',
name: 'password',
meta: {
title: '密码',
},
},
],
},
],
2.数据处理
// 该router对象必须是在main.js中use的router对象
import router from './index.js'
// 处理时不对数据做校验,如有借鉴请自行添加其余数据处理预防出错
const generateDynamicRouter = (data) => {
const result = generateRouter('/',data)
for(const route of result) {
router.addRoute('layout',route)
}
}
/**
* 递归函数,传入访问公共前缀与router数组,期待返回值为router数组
*/
const generateRouter = (path,router) => {
let result = []
for(const child of router){
// 拿到child后,需要判断其是否为叶子节点,如果有children且长度大于0为非叶子节点
let leaf = !(child.children && child.children.length > 0)
result.push({
path:child.path, // path不能带/,否则会被当做顶级路由处理,自行处理
name:child.name, // 唯一标识
meta:{
title:child.meta.title, // 标题,可以在el-menu中遍历作为标题
/**
* 只有叶子节点才能有path,且动态路由后续会放在顶级路由layout下的children属性上,因此会加上/layout
* 后续拼接的内容为外部传入的父级path + 当前路由的path
* 初始path为/,递归时会使用传入path + item.path + '/',保证递归过程中可以直接拼接item.path
*/
fullPath:leaf ? '/layout' + path + item.path : ''
}
// 自带/,因此views后不加/
component:leaf ? modules[`/src/views${path+child.path}/index.vue`] : undefined,
children: leaf ? [] : generateRouter(path + child.path + '/',child.children)
})
}
// 遍历结束后会将该路由数据返回,当到达叶子节点后就会直接push,非叶子节点递归处理后将其返回值赋值给children后push
return rusult
}
3.后续处理
动态路由的目的本质上是为了实现根据权限控制看到的内容不同,因此看到的首页也会不同,因此在添加完路由后需要找到第一个叶子节点作为首页
// 叶子节点是非常好找的,在刚处理好的动态路由中meta信息存储每个节点的全路径,且非叶子节点的children非空
const getFirstMenu = (routers) => {
// 数组的第一个路由的chilren有值,则继续传入,为null则返回该路由的meta.fullPath
return routers[0].chilren ? getFirstMenu(routers[0].children) :routers[0].meta.fullPath
}
分享就到这,如果后续有问题欢迎交流
2365

被折叠的 条评论
为什么被折叠?



