- Vite+vue3的约定式路由实践;
- 实现了类似umi的约定式路由,并实现了全局
layouts
的和404
的路由; - 本文大量借鉴了下面链接的文章,侵权删
- 作者:MinJie
- 链接:juejin.cn/post/706445...
- 来源:稀土掘金
路由规则
生成的方式,我们尽量与 umi.js
保持一致, 并实现(umijs)的约定式 layouts
和404
路由。但避免一个问题:避免将不需要的组件映射成路由。
pages/index.tsx
→/
pages/blog/index.tsx
→/blog
pages/about.tsx
→/about
在umi
中,我们可以在config.ts
中这样配置来避免将不需要的组件映射成路由
javascript
conventionRoutes: {
// 规定只有index文件会被识别成路由
exclude: [
/(?<!(index|\[index\]|404)(\.(js|jsx|ts|tsx)))$/,
/model\.(j|t)sx?$/,
/\.test\.(j|t)sx?$/,
/service\.(j|t)sx?$/,
/models\//,
/components\//,
/services\//,
],
},
在这里,我们暂时使用这个正则,也可以根据项目中是否使用了jsx
来增加.vue
文件后缀
ini
const modules = import.meta.glob("/src/pages/**/{index,404}.{ts,tsx}");
工程目录示例
├─layouts
│ │ index.tsx
├─pages
│ │
│ └─demo
│ │ index.tsx
│ │
│ └─demo-child
│ hello-world.tsx
│ index.tsx
话不多说,直接上代码
route/plugin.ts
import { set } from "lodash-es";
import type { RouteRecordRaw } from "vue-router";
/**
* 根据 pages 目录生成路径配置
*/
function generatePathConfig(): Record<string, any> {
// 扫描 src/pages 下的所有具有路由文件
const modules = import.meta.glob("/src/pages/**/{index,404}.{ts,tsx}");
const pathConfig = {};
Object.keys(modules).forEach((filePath) => {
const routePath = filePath
// 去除 src/pages 不相关的字符
.replace("/src/pages/", "")
// 去除文件名后缀
.replace(/.tsx?/, "")
// 转换动态路由 $[foo].tsx => :foo
.replace(/\$\[([\w-]+)]/, ":$1")
// 转换以 $ 开头的文件
.replace(/\$([\w-]+)/, "$1")
// 以目录分隔
.split("/");
// 使用 lodash.set 合并为一个对象
set(pathConfig, routePath, modules[filePath]);
});
return pathConfig;
}
/**
* 将文件路径配置映射为 vue-router 路由
*/
function mapPathConfigToRoute(cfg: Record<string, any>): RouteRecordRaw[] {
// route 的子节点为数组
return Object.entries(cfg).map(([routePath, child]): RouteRecordRaw => {
let currentRoute: RouteRecordRaw = {
path: routePath,
name: routePath,
};
// () => import() 语法判断 转换为组件
if (typeof child.index === "function" || typeof child === "function") {
currentRoute.component = child.index || child;
}
const { index, ...rest } = child;
// 还有子级为目录,查找下一层级
if (Object.keys(rest).length > 0) {
currentRoute.children = mapPathConfigToRoute(rest);
}
return currentRoute;
});
}
// 提取公共layouts
const getLayouts = (): (() => Promise<any>) => {
const layouts = import.meta.glob("/src/layouts/index.{ts,tsx}");
if (!layouts) return () => Promise.resolve(undefined);
return Object.entries(layouts)[0][1];
};
function generateRouteConfig(): RouteRecordRaw[] {
const { ...pathConfig } = generatePathConfig();
return [
{
path: "",
name: "",
component: getLayouts(),
children: mapPathConfigToRoute(pathConfig),
},
{ path: "/:pathMatch(.*)*", redirect: "/404" },
];
}
const routeConfig = generateRouteConfig();
export { routeConfig };
在router中使用
typescript
import { createRouter, createWebHistory } from "vue-router";
import { routeConfig } from "./plugin";
const router = createRouter({
history: createWebHistory(""),
routes: routeConfig,
});
router.beforeEach(async (to: any) => {
});
router.afterEach(() => {
});
export default router;
结语
暂无