在项目开发过程中,近期对项目进行了双页面配置,针对两个不同页面分别单独配置了路由。然而,在单独启动新页面(该页面模块数量较少)时,却发现启动耗时较长。经过深入排查,最终确定是以下这行代码导致了该问题:
js
return () => import(`@/pages/PRD/views/${realPath}`);
Webpack 在遇到import()语法时,会依据给定路径将所有符合条件的模块进行打包。由于上述导入代码的最后一个部分是变量,对于 Webpack 而言,这等同于@/pages/PRD/views/**/*,因此 Webpack 会将 views 目录下的所有模块及其相关依赖一同打包。
为深入探究 Webpack 对import的处理机制,我创建了一个项目,并针对不同情况进行打包测试,最终得出以下结论:
js
// `import`的参数不能是完全动态的语句,例如`import(util)`是不被允许的,至少需要包含部分模块路径信息,以便Webpack确定打包范围。
const component = () => {
const path = "xxx";
return import(path);
}
// 这种导入方式会将xxx目录下的所有组件和js文件都进行打包。
const componentB = (name) => {
const component = "component" + name;
return import(`./xxx/${component}`);
}
// 这种导入方式会将xxx目录下以component开头的文件进行打包。
// 如果xxx目录下存在/component*/这样的文件夹,那么文件夹内的所有组件都会被打包。
const componentC = (name) => {
const component =
return import(`./xxx/component${name}`);
}
// 较为合适的动态引入方式
const componentD = (name) => {
const component =
return import(`./xxx/component${name}.vue`);
}
Webpack 解析 import 注释指令
在 Webpack 中,import()函数的注释指令是一种特殊语法,用于控制 Webpack 对动态导入模块的处理方式,主要包含以下几种:
webpackChunkName
- 功能:该指令用于手动指定 Webpack 拆分代码时生成的代码块(chunk)名称。合理命名代码块,有助于在打包结果中清晰区分不同功能模块的代码块,对缓存管理、调试以及性能分析等工作都非常有利。
-
示例:
jsimport(/* webpackChunkName: "user - module - chunk" */ './userModule.js');
在上述代码中,Webpack 会将./userModule.js及其依赖模块打包到名为user - module - chunk的代码块中。从 Webpack 2.6.0 版本开始,还支持使用占位符,例如(index)会使代码块名称以递增数字进行命名,(request)则会使用实际解析的文件名。示例如下:
js// 假设导入多个模块,会生成类似userModule1、userModule2等命名的代码块 import(/* webpackChunkName: "userModule(index)" */ './userModule.js');
webpackMode
- 功能:该指令用于指定 Webpack 解析动态导入的模式。在不同模式下,Webpack 对动态导入模块的处理逻辑以及生成代码块的方式会有所不同。
- 模式类型及示例
-
lazy:这是默认模式。在这种模式下,Webpack 会为每个import()调用单独生成可延迟加载的代码块。也就是说,每次遇到import(),Webpack 都会创建一个新的独立代码块,在运行时按需加载。例如:
jsimport(/* webpackMode: "lazy" */ './module1.js'); import(/* webpackMode: "lazy" */ './module2.js');
上述代码会分别生成两个独立的代码块,用于加载module1.js和module2.js。
-
lazy - once:此模式下,Webpack 会生成单个可延迟加载的代码块,该代码块能够满足多个import()调用的需求。适用于一些动态导入语句,比如import(./locales/${language}.json),可能会根据不同的language值请求多个模块路径的情况。首次调用import()时获取代码块,后续调用则复用该代码块。示例如下:
js// 多个动态导入可能共用一个代码块 import(/* webpackMode: "lazy - once" */ `./locales/${language}.json`); import(/* webpackMode: "lazy - once" */ `./locales/${anotherLanguage}.json`);
-
eager:在该模式下,Webpack 不会生成额外的代码块,而是将被导入的模块直接引入当前代码块,并返回已解析状态的Promise。与静态导入不同的是,在import()调用完成前,模块不会执行。例如:
jsimport(/* webpackMode: "eager" */ './eagerModule.js').then((module) => { module.someFunction(); });