vue3动态路由的实现
前置知识
- vue-router的路由守卫,导航守卫 | Vue Router (vuejs.org)
- addRoute为路由添加一个新记录接口:Router | Vue Router (vuejs.org)
使用vue-router的路由守卫,在进入路由前先判断一下,路由是否已导入,如果还没有导入的,可以动态的导入。需要为路由添加的信息有path,component
主要的问题是,组件的动态导入 之前使用webpack工具的时候可以使用request方法实现模块的动态导入
component: (resolve) => require([`@/view${item.curl}`], resolve)
现在vue3使用vite作为构建工具,和webpack搭不上边。 不过还好vite的官方文档也表示支持动态导入
下面摘自官方文档
Vite 也支持带变量的动态导入。
const module = await import(`./dir/${file}.js`)
注意变量仅代表一层深的文件名。如果
file
是foo/bar
,导入将会失败。对于更进阶的使用详情,你可以使用 glob 导入 功能。
Glob 导入¶ Vite 支持使用特殊的
import.meta.glob
函数从文件系统导入多个模块: 以上将会被转译为下面的样子:
// vite 生成的代码
const modules = {
'./dir/foo.js': () => import('./dir/foo.js'),
'./dir/bar.js': () => import('./dir/bar.js')
}
你可以遍历 modules
对象的 key 值来访问相应的模块:
for (const path in modules) {
modules[path]().then((mod) => {
console.log(path, mod)
})
}
匹配到的文件默认是懒加载的,通过动态导入实现,并会在构建时分离为独立的 chunk。如果你倾向于直接引入所有的模块(例如依赖于这些模块中的副作用首先被应用),你可以传入 { eager: true }
作为第二个参数:
const modules = import.meta.glob('./dir/*.js', { eager: true })
所以,我们要导入某个文件夹下面的所有文件就是
modules = import.meta.glob('../views/content/**')
, 需要访问对应的模块就是
modules[`../views/content/${path}.vue`]
当然如果你配置的component名称只是一个文件名的话,可以像上面提到的直接用import(字符串拼接),就没有必要使用import.meta.blog了
下面是动态路由的完整代码
import store from '@/store'
router.beforeEach((to, from, next) => {
const whiteList = ['/login'] // 白名单
let token = window.sessionStorage.getItem('refresh_token')
let menuList = JSON.parse(window.sessionStorage.getItem('menuList'))
let hasRoutes = store.state.hasRoutes
if (token) {
if (!hasRoutes) {
bindRoute(menuList) //这个是动态路由的绑定
store.commit('SET_ROUTES_STATE', true)
}
next()
} else {
if (whiteList.includes(to.path)) {
next()
} else {
next('/login')
}
}
})
// 动态绑定路由
const bindRoute = (menuList) => {
let newRoutes = router.options.routes
menuList.forEach((menu) => {
if (menu.children) {
menu.children.forEach((m) => {
let route = menuToRoute(m, menu.name)
if (route) {
newRoutes[0].children.push(route)
}
})
}
}) // 重新添加到路由
newRoutes.forEach((route) => {
router.addRoute(route)
})
}
// 获取views目录下的所有的文件 不要使用@别名
// const modules = import.meta.glob('../views/content/**')
const modules = import.meta.glob('../views/content/**')
// 解析组件
const resolveComponent = (path) => {
console.log(modules)
// 拿到views下面的所有文件之后,动态拼接`key`去获取value
const importPage = modules[`../views/content/${path}.vue`]
if (!importPage) {
throw new Error(`Unknown page ${path}. Is it located under Pages with a .vue extension?`)
}
return importPage
}
// 菜单对象转成路由对象
const menuToRoute = (menu, parentName) => {
if (!menu.component) {
return null
} else {
let route = {
name: menu.name,
path: menu.path,
meta: {
parentName: parentName,
},
}
//注意变量仅代表一层深的文件名。如果 file 是 foo/bar,导入将会失败 ,这是vite官方文档的原话,多级目录使用import.meta.glob
// route.component = () => import('@/views/content/' + menu.component + '.vue')
route.component = resolveComponent(menu.component)
return route
}
}
export default router
下面的是我的目录json-menuList,需要的可以结合代码参考
[
{
"id": 1,
"children": [
{
"id": 3,
"name": "用户管理",
"icon": "user",
"order_num": 1,
"path": "/sys/user",
"component": "sys/user/TheUser",
"menu_type": "C",
"remark": "用户管理菜单",
"parent": 1
},
{
"id": 4,
"name": "角色管理",
"icon": "peoples",
"order_num": 2,
"path": "/sys/role",
"component": "sys/role/TheRole",
"menu_type": "C",
"remark": "角色管理菜单",
"parent": 1
},
{
"id": 5,
"name": "菜单管理",
"icon": "tree-table",
"order_num": 3,
"path": "/sys/menu",
"component": "sys/menu/TheMenu",
"menu_type": "C",
"remark": "菜单管理菜单",
"parent": 1
}
],
"name": "系统管理",
"icon": "system",
"order_num": 1,
"path": "/sys",
"component": "",
"menu_type": "M",
"remark": "系统管理目录",
"parent": null
},
{
"id": 2,
"children": [
{
"id": 6,
"name": "部门管理",
"icon": "tree",
"order_num": 8,
"path": "/bsns/department",
"component": "bsns/TheDepartment",
"menu_type": "C",
"remark": "部门管理菜单",
"parent": 2
},
{
"id": 7,
"name": "岗位管理",
"icon": "post",
"order_num": 7,
"path": "/bsns/post",
"component": "bsns/ThePost",
"menu_type": "C",
"remark": "岗位管理菜单",
"parent": 2
},
{
"id": 9,
"name": "菜品管理",
"icon": "food",
"order_num": 2,
"path": "/bsns/food",
"component": "bsns/food/TheFood",
"menu_type": "C",
"remark": "",
"parent": 2
},
{
"id": 10,
"name": "店铺管理",
"icon": "store",
"order_num": 1,
"path": "/bsns/store",
"component": "bsns/store/TheStore",
"menu_type": "C",
"remark": "",
"parent": 2
},
{
"id": 11,
"name": "类别管理",
"icon": "category",
"order_num": 4,
"path": "/bsns/category",
"component": "bsns/category/TheCategory",
"menu_type": "C",
"remark": "",
"parent": 2
},
{
"id": 12,
"name": "订单查看",
"icon": "order",
"order_num": 3,
"path": "/bsns/order",
"component": "bsns/order/TheOrder",
"menu_type": "C",
"remark": "",
"parent": 2
}
],
"name": "业务管理",
"icon": "monitor",
"order_num": 2,
"path": "/bsns",
"component": "",
"menu_type": "M",
"remark": "业务管理目录",
"parent": null
}
]
参考文章 功能 | Vite 官方中文文档 (vitejs.cn)