经过我两年半的思考,总算写出来这个vue3的动态路由方案
中分头背带裤 git地址看此处
拉代码的话需要自己把http改成https,不然拉不下来 之前写过vue2.x的动态路由,vue3所使用的vue-router有些许的api更新。所以暂作记录.
模块版本
"vue": "^3.2.37""vue-router": "^4.1.3""vite": "^3.0.0"
基础工作:typings编写
一般自己需要定义一下meta中的自定义字段,当然也可以使用原生的
Record<string,unknow>
interface RouteMeta {
title?: string;
inWhite?: boolean;
inMenu?: boolean;
fixed?: boolean;
icon?: string;
isLink?: boolean;
link?: string;
}
interface RouteRecord extends Omit<Omit<RouteRecordRaw, 'meta'>, 'children'> {
children?: RouteRecord[];
meta?: RouteMeta;
} //app需要的路由信息类型
interface AsyncRoute extends Omit<Omit<RouteRecord, 'children'>, 'component'> {
component: string;
children?: AsyncRoute[];
} // 异步路由的component是string类型
export type AppRouteRecord = RouteRecord; //导出类型
export type AppAsyncRecord = AsyncRoute; //导出类型
流程梳理
- 页面加载初始路由
const constantRoute: Array<AppRouteRecord> = [ { name: 'login', path: '/login', component: () => import('@/views/login/login'), meta: { name: 'login', inMenu: false, }, }, { name: '404', path: '/:pathMatch(.*)', component: ErrorPage404, } ]; - 检测用户登陆情况,如果用户未登录,就去登录,登录了就请求一下权限路由表
router.beforeEach(async (to, form, next) => { const sys = sysStore(); if (to.name !== 'login') { if (!sys.token) { next({ name: 'login', query: { redirect: to.fullPath, }, }); } else { if (!sys.asyncRoutes.length) { await handleRoutes(); const { path, query } = to; next({ path, query, replace: true });此处解构是为了修改页面重新加载路由的时候页面报错,warning } else { next(); } } } else { next(); } }); handleRoutes处理对应路由import * as asyncRoutes from '@/mock/routes.json';//mock的路由结构 import { ResponseData } from '@/utils/request'; import { AppAsyncRecord, RouteRecord, AsyncRoute } from '@/typings/evt'; import { cloneDeep, forEach, map } from 'lodash'; import { router } from '@/router'; import baseLayout from '@/layout/baseLayout'; import Home from '@/views/home/index'; import emptyLayout from '@/layout/emptyLayout'; import { RouteRecordRaw } from 'vue-router'; import { sysStore } from '@/store/modules/sys'; const baseComponents = { baseLayout, Home, emptyLayout, };//定义一下基础的布局和 type baseComponents = 'baseLayout' | 'Home' | 'emptyLayout'; /** * 模拟请求获取后端接口的路由表 * @return {Record<string,unknown>} asyncRoutes */ export const getRoutes = () => { return new Promise((resolve) => { setTimeout(() => { resolve(asyncRoutes); }, 500); }); }; /** * 处理异步路由 */ export const handleRoutes = () => { return new Promise((resolve) => { getRoutes().then((result) => { const { data } = result as ResponseData; const routes = formatRoutes(data as AppAsyncRecord[]); addRoutes(routes); const sys = sysStore(); sys.asyncRoutes = data as RouteRecord[];//将获取到的路由存到store,方便其他地方使用 resolve(routes); }); }); }; /** * @param {AppAsyncRecord[]} data 路由数据 * @return 返回符合component规范的数据 */ export const formatRoutes = (data: AppAsyncRecord[]) => { return map(data, (current) => { const route = { ...current }; const { component: currentComp } = current; Object.assign(route, { component: resolvePage(currentComp), }); if (current.children?.length) { const currentChild: AsyncRoute[] = cloneDeep( current.children ) as AsyncRoute[]; const children = formatRoutes(currentChild); Object.assign(route, { children }); } return route as unknown as RouteRecordRaw; }); }; /** * @param {AppRouteRecord[]} data 路由表 * @param {string|undefined} parentName 父级name */ export const addRoutes = ( data: RouteRecordRaw[], parentName?: string | undefined ) => { forEach(data, (v) => { router.addRoute(parentName ?? (v.name || ''), v as RouteRecordRaw); }); }; const pages = import.meta.glob('../views/**/*.tsx');//先获取到所有的组件列表 /** * @param {string} currentComp 路由名称 * 返回路由的component */ const resolvePage = (currentComp: string) => { if (Object.keys(baseComponents).includes(currentComp)) { return baseComponents[currentComp as baseComponents]; } if (!currentComp) return ''; return pages[currentComp];//返回对应的组件 };
总结
总体来说跟vue2版本的动态路由没啥区别,只是api有些更改。只要加载到对应的组件就可以了。还有一种实现方案就是动态import,在vite中我没有实现这个功能,有对应的例子可以共同探讨一下。