vite 动态 import 引入打包报错解决方案

一、介绍

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

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

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

二、问题

// 问题代码 () => import( /* @vite-ignore */ `${fileSrc}`)
routerList.push({
  path: `/${routerPath}`,
  name: `${routerName}`,
  component: () => import( /* @vite-ignore */ `${fileSrc}`)
})

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

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

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

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

三、需求

主要项目结构

├── components                    // 公共组件存放目录
└── views                         // 路由视图存放目录
    └── Home                      // Home 页面
        └── components            // Home 页面封装组件存放文件
            └── HomeHeader.vue    // Home 页面头部组件
        └── index.vue             // Home 主入口
        └── types                 // Home页面专属类型
    └── Login                     // Login 页面
        └── components            // Login 页面封装组件存放文件
            └── LoginHeader.vue   // Login 页面头部组件
        └── index.vue             // Login 主入口
        └── types                 // Login页面专属类型
    ....
    ....

文件内部

export default defineComponent({
  name: 'home',
  isRouter: true,
  isComponents: false,
  setup() {
    ...
  }
})

组件内部通过定义

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

四、 解决 (推荐方案二)

vite 动态导入有两种方式

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

以下方案有需要自行取舍

4.1 方案一

使用 import.meta.glob

缺点:

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

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

// global.ts
export const vueRouters = function (): Array<RouteRecordRaw> {
  let routerList: Array<RouteRecordRaw> = [];
  const modules = import.meta.glob('../views/**/*.vue')
  Object.keys(modules).forEach(key => {
    const nameMatch = key.match(/^\.\.\/views\/(.+)\.vue/)
    if(!nameMatch) return
    const indexMatch = nameMatch[1].match(/(.*)\/Index$/i)
    let name = indexMatch ? indexMatch[1] : nameMatch[1];
    // 首字母转小写 letterToLowerCase 首字母转大写 letterToUpperCase
    routerList.push({
      path: `/${letterToLowerCase(name)}`,
      name: `${letterToUpperCase(name)}`,
      component: modules[key]
    });
  })
  return routerList
};

使用 router.ts

import { vueRouters } from '../services/global'
const routes: Array<RouteRecordRaw> = [
  {
    path: '/',
    name: 'Login',
    component: () => import('@/views/Login/index.vue')
  },
  ...vueRouters()
]

4.2 方案二 “推荐”

使用 import.meta.globimport.meta.globEager

  • import.meta.glob: 因为 import.meta.glob 获取到的文件就是懒加载的,避免了使用 import 语句, 所以打包后不会报错不存在动态引入了
  • import.meta.globEager:不使用 then 也能获取到文件全局上下文进行有需要的判断
// global.ts
function getModules() {
  const components = import.meta.glob('../views/**/*.vue')
  return components
}
function getComponents() {
  const components = import.meta.globEager('../views/**/*.vue')
  return components
}
// 自动注册组件
export const asyncComponent = function (app: App<Element>): void {
  const modules = getModules();
  const components = getComponents();
  Object.keys(modules).forEach((key: string) => {
    const viewSrc = components[key];
    const file = viewSrc.default;
    if (!file.isComponents) return
    const AsyncComponent = defineAsyncComponent(modules[key])
    app.component(letterToUpperCase(file.name), AsyncComponent)
  });
  // console.log(app._component.components)
};

// 自动注册路由
export const vueRouters = function (): Array<RouteRecordRaw> {
  let routerList: Array<RouteRecordRaw> = [];
  const modules = getModules();
  const components = getComponents();
  Object.keys(modules).forEach(key => {
     const viewSrc = components[key];
     const file = viewSrc.default;
     if (!file.isRouter) return
     // 首字母转小写 letterToLowerCase 首字母转大写 letterToUpperCase
     routerList.push({
       path: `/${letterToLowerCase(file.name)}`,
       name: `${letterToUpperCase(file.name)}`,
       component: modules[key]
     });
  })
  return routerList
}  
 

使用 router.ts (路由注册)

import { vueRouters } from '../services/global'
const routes: Array<RouteRecordRaw> = [
  {
    path: '/',
    name: 'Login',
    component: () => import('@/views/Login/index.vue')
  },
  ...vueRouters()
]

使用 main.ts (组件注册)

import { asyncComponent } from './services/global';
export const app = createApp(App)
asyncComponent(app)

4.3 方案三

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

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

使用 router.ts

import { vueRouters } from '../services/global'
const routes: Array<RouteRecordRaw> = [
  {
    path: '/',
    name: 'Login',
    component: () => import('@/views/Login/index.vue')
  }
]
const router = createRouter({
  history: createWebHashHistory(),
  routes
})
vueRouters(router)
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,348评论 6 491
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,122评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,936评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,427评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,467评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,785评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,931评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,696评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,141评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,483评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,625评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,291评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,892评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,741评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,977评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,324评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,492评论 2 348

推荐阅读更多精彩内容