webpack、vite的自动导包的”爱恨情仇”

前言

最近在开发Vue3+Vite的统一基座工程,遇到了模块自动导入的不同构建工具的引入方式,特此记录。

在项目中,为了避免频繁导入。大家都会在对应,例如项目使用Webpack 打包工具的。在其项目store文件下其中的index.js 文件中使用webpack提供的apirequire.context的方法实现自动导入。

ES 模块规范

在实现自动导入模块前,我们先了解下ES 模块规范

所谓的ES模块规范,即 JavaScript 的标准模块系统,它允许您使用 importexport 关键字来导入和导出模块。这是现代 JavaScript 中推荐使用的模块化方式。

ES模块规范中,提供了import.meta.glob功能。它允许在项目运行中动态匹配特定模式的模块。例如以下代码就可以动态匹配到module模块下的所有js文件。

1
import.meta.glob('./module/*.js')

话不多说,上干货

例如,你的项目sotre文件目录为

1
2
3
4
5
6
7
8
- store
- module
- module1.js
- module2.js
- ...
- moduleN.js
- main.js

其中module下的moduleX文件你的main.js为你的sotre入口文件,

webpack中的自动导入的实现方式

main.js入口文件中你可以使用webpack提供require.context的方法实现module文件目录下的模块自动导入。

1
2
3
4
5
6
7
const files = require.context('./model', false, /\.js$/)
const modules = {}
files.keys().forEach((key) => {
modules[key.replace(/(\.\/|\.js)/g, '')] = files(key).default
})

export default modules

其中files.keys()require.context方法返回的一个函数,它会返回一个包含所有匹配模块路径的数组。

require.context是Webpack提供的一个方法,它允许你在构建时动态地导入模块。该方法接收三个参数:

  1. directory: 表示要搜索的目录路径。
  2. useSubdirectories: 表示是否搜索子目录。
  3. regExp: 表示匹配文件的正则表达式。

在上面代码中,files就是通过require.context动态导入了./model目录下所有的.js文件,并使用正则表达式/\.js$/来匹配文件。这样,files.keys()返回一个包含所有匹配模块路径的数组。例如控制台打印files.keys()可获得以下数组

1
2
3
4
5
6
[
"./module1.js",
"./module2.js",
"./。。。.js",
"./moduleN.js"
]

接着,我们可以使用.forEach()遍历这个数组,对每个匹配的模块进行处理,提取模块名,并将模块添加到modules对象中。这样,你就得到了一个以模块名为键、模块对象为值的modules对象,它包含了所有从./model目录中动态导入的模块。

vite自动导入的实现方式

main.js入口文件中你可以使用ES模块规范提供的import.meta.glob方法来获取特定模块的匹配模式,来实现自动导入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// main.js
const modules = {};

async function importAllModules() {
const files = import.meta.glob('./module/*.js');
for (const path in files) {
const key = path.replace(/\.\/module\/|\.js/g, '');
const module = await files[path]();
modules[key] = module.default;
}
}

importAllModules().then(() => {
console.log(modules); // 所有模块已导入并组合成对象
});

// 现在可以导出组合后的 modules 对象供其他地方使用
export default modules;

在这种情况下,使用异步操作是因为模块导入是一个异步操作。import.meta.glob()方法会返回一个对象,该对象的键是匹配到的文件路径,值是一个函数,调用该函数将异步导入对应的模块。因此,我们需要使用异步操作来等待模块导入完成,然后再将其添加到modules对象中。

如果我们不使用异步操作,而是直接将模块导入的结果添加到module对象中,那么由于模块导入是异步的,modules对象可能在模块导入完成之前被导出,导致modules对象不完整或为空。使用异步操作可以确保在所有模块导入完成后再导出modules对象,保证其包含所有模块导出的内容。

总结

为什么都是打包工具,会有不同的自动导入方式呢?

在使用 Webpack 的情况下,与使用 Vite 或其他原生支持 ES 模块的项目相比,自动导入模块的实现会有一些区别。主要区别在于 Webpack 不支持 import.meta.glob,因为它是 ES 模块的一个特殊功能,而 Webpack 是一个打包工具,不完全符合 ES 模块的规范。

那这里就浅浅对比下俩种打包工具的差异吧

webpack

  • 成熟度高。Webpack是一个成熟且应用相当广泛的打包构建工具,具有强大的生态系统和社区支持。
  • 打包速度较慢。Webpack打包速度在大型项目打包速度比较慢,从入口文件开始,基于代码中的import、export、require构建依赖树,将所有的模块打包到一个或者几个少数文件中。因此,项目规模庞大的话,启动和热更新更慢。每次代码变更构建都需要生成新的Bundle文件。
  • 配置复杂。官网配置很多,需要处理不同的Loader和Plugin来管理不同的资源文件。
  • 插件系统丰富。Webpack具有强大的插件系统,允许开发者根据需求扩展定义。
  • Tree Shaking。Webpack通过使用UglifyJS等工具进行Tree Shaking,消除未使用的代码
  • 热模块替换(HMR)。Webpack支持热模块替换,但在某些情况下需要手动配置。

Vite

  • 新兴技术。Vite是一个相较新的构建工具,旨在提供更快的开发体验和构建速度。
  • 打包速度极快。Vite在开发环境下具有极快的启动和热更新速度,因为它采用了原生ES模块的方式,并且将依赖项保持为独立的文件,而不是打包到一个大文件中。
  • 配置简单。Vite的配置比Webpack简单,尤其是对于常见的项目结构,大部分任务都无需额外配置。
  • 热模块替换(HMR)。Vite对热模块替换的支持非常好,在开发过程中几乎不需要手动配置即可实现HMR。
  • Tree Shaking。Vite使用Rollup进行Tree Shaking,这使得未使用的代码更容易被消除。
  • 插件系统还不够完善。Vite的插件系统仍在发展中,目前没有Webpack那么丰富的插件支持。

总之,Webpack更加适合与大型、复杂项目的构建工作,拥有成熟和完善的生态系统和社区。Vite更适合用于一些热更新快速的程序。