携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第5天,点击查看活动详情
vue3 + nestjs 自给自足,实现动态路由
vue3.x + vue-router@4 + nestjs
效果图
思路
-
与后端商量基本数据格式编写接口;
-
前端在本地只配置不参与权限控制的路由,比如 layout、login、not-found,其他权限路由登录之后请求后端查询接口,返回实例格式如下:
{ code: 200, message: "success", data: { // 当前角色路由信息 "menus": [{ name: 'demo01', path: '/demo01', redirect: '/demo01/demo05', component: 'app', children: [/* ... */], ...title、icon、meta }], // 当前角色 "role": 'xxx' } } -
前端在 router.beforeEach 路由守卫使用 addRoute 添加路由,基本代码如下:
router.beforeEach(async (to, from, next) => { const demo = useDemo() if (!demo.init) { const routeList = await getRoutes() const r = fetchRoutes(routeList) await addRouter(r) await routerInit(r, demo) if (to.path) next({ path: to.path }) } next() }) -
把路由列表存储到 store(pinia 或者 vuex),layout 页面从 store 获取路由列表进行动态渲染侧边导航栏。
接口开发
使用 nest@8.0.x + typeorm@^0.2.45 开发接口,我也是刚接触这种树形实体,所以不懂的就多看看文档吧。
树形实体Route
import {
Entity,
Tree,
Column,
PrimaryGeneratedColumn,
TreeChildren,
TreeParent,
} from 'typeorm';
@Entity()
@Tree('closure-table')
export class Route {
// 可以用 @PrimaryGeneratedColumn('uuid') 自动生成 uuid
@PrimaryGeneratedColumn()
id: number;
@Column({ length: 50 })
component: string;
@Column({ length: 50, default: '' })
redirect?: string;
@Column({ length: 50 })
url: string;
@Column({ length: 50 })
name: string;
@TreeChildren()
children: Route[];
@TreeParent()
parent: Route;
}
controller
@Get()
@SkipJwtAuth()
findAll() {
return this.routeService.findAll();
}
service
findTrees 返回树的所有数据,可以看成是 findAll()
async findAll() {
// const res = await this.routeRepository.findTrees();
return await this.routeRepository.findTrees();
}
mock数据
树形实体的保存,需要先存在一个模型,然后子模型通过 .parent 的方式设置父子关系,再 save 到表中,采用递归是比较合适的做法。
await connection.synchronize(); // 表不存在建表
await connection.createQueryBuilder().delete().from(Route).execute(); // 删除所有记录
const mockRoute = [
{
name: 'demo01',
path: '/demo01',
redirect: '/demo01/demo05',
component: 'App',
children: [
{
name: 'demo10',
path: '/demo01/demo05',
component: 'demo-test',
children: null,
},
{
name: 'demo06',
path: '/demo01/demo02',
component: 'App',
children: [
{
name: 'demo07',
path: '/demo01/demo02/demo03',
component: 'demo03',
},
],
},
],
},
{
name: 'demo02',
path: '/demo02',
redirect: '/demo02/demo04',
component: 'App',
children: [
{
name: 'demo08',
path: '/demo02/demo04',
component: 'demo-04',
children: null,
},
],
},
{
name: 'demo03',
path: '/demo03',
component: 'demo-03',
children: null,
},
{
name: 'demo04',
path: '/demo04',
component: 'demo-04',
children: null,
},
{
name: 'demo-test',
path: '/demo-test',
component: 'demo-test',
children: null,
},
];
// 添加树形数据
function mock(list, parent = undefined) {
list.map(async (u) => {
const r1 = new Route();
r1.url = u.path;
r1.name = u.name;
r1.component = u.component;
if (u.redirect) r1.redirect = u.redirect;
if (parent) {
r1.parent = parent;
}
const p1 = await routeRepository.save(r1);
if (u.children) {
mock(u.children, p1);
}
});
}
await mock(mockRoute);
最后接口返回的数据如下:
code: 200
data: [{id: 16, component: "App", redirect: "/demo01/demo05", url: "/demo01", name: "demo01",…},…]
0: {id: 16, component: "App", redirect: "/demo01/demo05", url: "/demo01", name: "demo01",…}
1: {id: 17, component: "App", redirect: "/demo02/demo04", url: "/demo02", name: "demo02",…}
2: {id: 18, component: "demo-test", redirect: "", url: "/demo-test", name: "demo-test", children: []}
3: {id: 19, component: "demo-03", redirect: "", url: "/demo03", name: "demo03", children: []}
4: {id: 20, component: "demo-04", redirect: "", url: "/demo04", name: "demo04", children: []}]
这里说明一下字段的用处把
-
url 对应跳转的 path;
-
component 用于引入页面,比如我的页面都存放在 src/pages 目录下,那我路由配置组件的路径如下
// demo-03 => src/pages/demo-03/demo-03.vue component: () => import(`@/pages/${component}/${component}.vue`) -
redirect 应该是可选的,到时候配置路由应该判断它是否是 "",如果是的话就不赋值
let obj: RouteRecordRaw = { path: r.url, name: r.name, component: r.component === 'App' ? () => import(`@/App.vue`) : () => import(`@/pages/demo/${e.component}.vue`) } if (r.redirect !== '') { ;(obj.redirect as any) = r.redirect }
本篇主要讲了动态路由思路,后端接口不重要,但是还是把代码放一下,可以把重点放在思路那里,下一篇主要讲解并编写前端的逻辑。
后续
- vue3 递归组件使用方式和 tsx 方式编写侧边导航栏
- 学习基本递归函数的编写
- vue-router@4 addRoute api使用、
- token 鉴权登录、用户角色权限
- 路由、角色管理
- ...