Vite动态路由和异步组件

前端开发
2022年04月29日
1633

在给 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;