Vite动态路由和异步组件
在给 vite 的项目打包部署之后,发现有些页面/组件资源获取不到,报错如下:
Error: Unknow variable dynamic import: xxxx.vue
解决方案:https://vitejs.cn/guide/features.html#glob-import
使用 import.meta.globEager
解决动态路由引入,import.meta.glob
解决异步组件的引入
动态引入路由
diff
import _ from 'lodash';
import { IMenuItem } from '@/types/menu';
import { RouteRecordRaw } from 'vue-router';
export function generateDynamicRoutes (menus: IMenuItem[]): RouteRecordRaw[] {
return [
{
path: '/',
name: 'home',
component: () => import('@/views/Home.vue'),
meta: {
id: -1,
parentId: 0,
hidden: false,
title: '首页',
icon: 'HomeOutlined',
keepAlive: true
}
},
...generator(menus.filter(menu => menu.type === 1)),
{
path: '/:pathMatch(.*)*',
name: 'NotFound',
redirect: '/expection/404',
meta: {
hidden: true
}
}
]
}
const generator = (menus: IMenuItem[]): RouteRecordRaw[] => {
+ const pages = import.meta.globEager('../views/**/*.vue');
+
+ const resolveComponent = (path: string) => {
+ const importPage = pages[path ? `../views/${path}${/\.vue$/.test(path) ? '' : '.vue'}` : '../views/Home.vue'];
+
+ if (!importPage) {
+ throw new Error(`Unknown page ${path}. Is it located under Pages with a .vue extension?`);
+ }
+
+ return importPage.default;
+ }
return menus.map(menu => {
const { id, parentId, name, router, path, icon, isShow, isCache } = menu;
const currentRoute: RouteRecordRaw = {
path: router ? `/${router}` : '/', // 路由 path
name: name, // 路由名称(唯一)
- component: () => path ? import(`../views/${path}${/\.vue$/.test(path) ? '' : '.vue'}`) : import('../views/Home.vue'),
+ component: resolveComponent(path),
meta: {
id,
parentId,
title: name, // 页面标题
icon,
keepAlive: isCache,
hidden: !isShow // 是否在侧边栏中隐藏
}
}
// 是否有子菜单,并递归处理
if (menu.children && !_.isEmpty(menu.children)) {
// 递归
currentRoute.children = generator(menu.children);
}
return currentRoute;
})
}
异步组件
diff
import { defineAsyncComponent, defineComponent } from 'vue';
import GlobalLoadingComponent from '@/components/common/GlobalLoadingComponent.vue';
import { Modal } from 'ant-design-vue';
+ const resolveComponent = (path: string) => {
+ const components = import.meta.glob('../../**/*.vue');
+
+ return components[`../..${path}`]
+ }
/**
* 定义异步组件
* @param { string } filePath - 以`/src`目录为基准的绝对路径
*/
const useDefineAsyncComponent = (filePath: string) => {
if (!/^\//.test(filePath)) {
throw new Error('请使用以`/src`目录为基准的绝对路径');
}
return defineAsyncComponent({
// 工厂函数
- loader: () => import(`../..${filePath}`),
+ loader: resolveComponent(filePath),
// 加载异步组件时要使用的组件
loadingComponent: GlobalLoadingComponent,
// 加载失败时要使用的组件
errorComponent: defineComponent({
setup () {
Modal.error({
title: '提示',
content: '组件加载失败,请刷新页面重试!'
});
}
}),
// 在显示 loadingComponent 之前的延迟 | 默认值:200(单位 ms)
delay: 200,
// 如果提供了 timeout ,并且加载组件的时间超过了设定值,将显示错误组件
// 默认值:Infinity(即永不超时,单位 ms)
timeout: 10000,
// 定义组件是否可挂起 | 默认值:true
suspensible: false,
/**
* @param {*} error 错误信息对象
* @param {*} retry 一个函数,用于指示当 promise 加载器 reject 时,加载器是否应该重试
* @param {*} fail 一个函数,指示加载程序结束退出
* @param {*} attempts 允许的最大重试次数
*/
onError (error, retry, fail, attempts) {
if (error.message.match(/fetch/) && attempts <= 3) {
// 请求发生错误时重试,最多可尝试 3 次
retry()
} else {
// 注意,retry/fail 就像 promise 的 resolve/reject 一样:
// 必须调用其中一个才能继续错误处理。
fail()
}
}
});
}
export default useDefineAsyncComponent;