https://juejin.cn/post/6951557699079569422/

一、介绍

之前一直是使用 webpack 构建项目, 但是那种随着项目越来越大运行、打包、热更新缓慢带来的无奈。。。

新项目开发果断使用了vite2.0, 和 webpack 对比起来, 其他的优点不是很直观,但在运行、打包、热更新方面简直倍数差距, 只能说我觉得 vite2.0 很舒服。

当然 vite 作为热门构建共建优点还是很多的,详情可以去官网看看 vite 官网地址

二、问题

  1. // 问题代码 () => import( /* @vite-ignore */ `${fileSrc}`)
  2. routerList.push({
  3. path: `/${routerPath}`,
  4. name: `${routerName}`,
  5. component: () => import( /* @vite-ignore */ `${fileSrc}`)
  6. })
  7. 复制代码

之前使用 webpack 构建项目一直使用动态导入 require.context API 自动化注册组件及路由;

转移到 vite 之后,开发习惯当然不能变;随即使用的是 import.meta.globEager 完成动态导入;

本地开发过程中很舒服没问题,打包后部署到服务器报错找不到动态导入的文件;裂开~~~

经过这几天陆陆续续的尝试最终解决,总结了以下几种方案

三、需求

主要项目结构

  1. ├── components // 公共组件存放目录
  2. └── views // 路由视图存放目录
  3. └── Home // Home 页面
  4. └── components // Home 页面封装组件存放文件
  5. └── HomeHeader.vue // Home 页面头部组件
  6. └── index.vue // Home 主入口
  7. └── types // Home页面专属类型
  8. └── Login // Login 页面
  9. └── components // Login 页面封装组件存放文件
  10. └── LoginHeader.vue // Login 页面头部组件
  11. └── index.vue // Login 主入口
  12. └── types // Login页面专属类型
  13. ....
  14. ....
  15. 复制代码

文件内部

  1. export default defineComponent({
  2. name: 'home',
  3. isRouter: true,
  4. isComponents: false,
  5. setup() {
  6. ...
  7. }
  8. })
  9. 复制代码

组件内部通过定义

  • name: 路由组件名称
  • isRouter: 是否自动为路由;
  • isComponents: 是否自动注册为公共组件

四、 解决 (推荐方案二)

vite 动态导入有两种方式

  • import.meta.glob: 通过动态导入默认懒加载,通过遍历加 then 方法可拿到对应的模块文件详情信息
  • import.meta.globEager: 直接引入所有的模块, 即静态 import; 我的就是使用该方案打包部署报错

以下方案有需要自行取舍

4.1 方案一

使用 import.meta.glob

缺点:

  • 不使用 then 方法拿不到模块信息,无法进行判断是否需要自动注册组件或路由;
  • 使用了 then 方法成异步的了, 路由渲染的时候文件还没获取成功注册不到

但是你可以用单独文件夹来区分,我认为限制性太大不够优雅;

  1. // global.ts
  2. export const vueRouters = function (): Array<RouteRecordRaw> {
  3. let routerList: Array<RouteRecordRaw> = [];
  4. const modules = import.meta.glob('../views/**/*.vue')
  5. Object.keys(modules).forEach(key => {
  6. const nameMatch = key.match(/^\.\.\/views\/(.+)\.vue/)
  7. if(!nameMatch) return
  8. const indexMatch = nameMatch[1].match(/(.*)\/Index$/i)
  9. let name = indexMatch ? indexMatch[1] : nameMatch[1];
  10. // 首字母转小写 letterToLowerCase 首字母转大写 letterToUpperCase
  11. routerList.push({
  12. path: `/${letterToLowerCase(name)}`,
  13. name: `${letterToUpperCase(name)}`,
  14. component: modules[key]
  15. });
  16. })
  17. return routerList
  18. };
  19. 复制代码

使用 router.ts

  1. import { vueRouters } from '../services/global'
  2. const routes: Array<RouteRecordRaw> = [
  3. {
  4. path: '/',
  5. name: 'Login',
  6. component: () => import('@/views/Login/index.vue')
  7. },
  8. ...vueRouters()
  9. ]
  10. 复制代码

4.2 方案二 “推荐”

使用 import.meta.globimport.meta.globEager

  • import.meta.glob: 因为 import.meta.glob 获取到的文件就是懒加载的,避免了使用 import 语句, 所以打包后不会报错不存在动态引入了
  • import.meta.globEager:不使用 then 也能获取到文件全局上下文进行有需要的判断
  1. // global.ts
  2. function getModules() {
  3. const components = import.meta.glob('../views/**/*.vue')
  4. return components
  5. }
  6. function getComponents() {
  7. const components = import.meta.globEager('../views/**/*.vue')
  8. return components
  9. }
  10. // 自动注册组件
  11. export const asyncComponent = function (app: App<Element>): void {
  12. const modules = getModules();
  13. const components = getComponents();
  14. Object.keys(modules).forEach((key: string) => {
  15. const viewSrc = components[key];
  16. const file = viewSrc.default;
  17. if (!file.isComponents) return
  18. const AsyncComponent = defineAsyncComponent(modules[key])
  19. app.component(letterToUpperCase(file.name), AsyncComponent)
  20. });
  21. // console.log(app._component.components)
  22. };
  23. // 自动注册路由
  24. export const vueRouters = function (): Array<RouteRecordRaw> {
  25. let routerList: Array<RouteRecordRaw> = [];
  26. const modules = getModules();
  27. const components = getComponents();
  28. Object.keys(modules).forEach(key => {
  29. const viewSrc = components[key];
  30. const file = viewSrc.default;
  31. if (!file.isRouter) return
  32. // 首字母转小写 letterToLowerCase 首字母转大写 letterToUpperCase
  33. routerList.push({
  34. path: `/${letterToLowerCase(file.name)}`,
  35. name: `${letterToUpperCase(file.name)}`,
  36. component: modules[key]
  37. });
  38. })
  39. return routerList
  40. }
  41. 复制代码

使用 router.ts (路由注册)

  1. import { vueRouters } from '../services/global'
  2. const routes: Array<RouteRecordRaw> = [
  3. {
  4. path: '/',
  5. name: 'Login',
  6. component: () => import('@/views/Login/index.vue')
  7. },
  8. ...vueRouters()
  9. ]
  10. 复制代码

使用 main.ts (组件注册)

  1. import { asyncComponent } from './services/global';
  2. export const app = createApp(App)
  3. asyncComponent(app)
  4. 复制代码

4.3 方案三

使用 import.meta.globthen 方法, 加上路由内置 addRoute() 方法注册
缺点:

  • 由于文件懒加载获取, 页面加载有明显的的卡顿
  • 不适用于注册组件
  • addRoute() 方法适用于根据后台接口返回的路由权限注册鉴权路由
  1. // global.ts
  2. export const vueRouters = function (router: Router): void {
  3. let routerList: Array<RouteRecordRaw> = [];
  4. const modules = import.meta.glob('../views/**/*.vue')
  5. for (const path in modules) {
  6. modules[path]().then((mod) => {
  7. const file = mod.default;
  8. if (!file.isRouter) return
  9. // 首字母转小写 letterToLowerCase 首字母转大写 letterToUpperCase
  10. router.addRoute({
  11. path: `/${letterToLowerCase(file.name)}`,
  12. name: `${letterToUpperCase(file.name)}`,
  13. component: file
  14. })
  15. })
  16. }
  17. };
  18. 复制代码

使用 router.ts

  1. import { vueRouters } from '../services/global'
  2. const routes: Array<RouteRecordRaw> = [
  3. {
  4. path: '/',
  5. name: 'Login',
  6. component: () => import('@/views/Login/index.vue')
  7. }
  8. ]
  9. const router = createRouter({
  10. history: createWebHashHistory(),
  11. routes
  12. })
  13. vueRouters(router)