过去,webpack 在打包时的一个权衡是,包中的每个模块都将被包装在单独的函数闭包中。这些包装函数使 JavaScript 在浏览器中执行得更慢。相比之下,Closure Compiler 和 RollupJS 等工具会将所有模块的范围“提升”或连接到一个闭包中,并允许你的代码在浏览器中具有更快的执行时间。
此插件将在 webpack 中启用相同的连接行为。默认情况下,此插件已在 生产 mode
中启用,否则禁用。如果你需要覆盖生产 mode
优化,请将 optimization.concatenateModules
选项 设置为 false
。要在其他模式中启用连接行为,你可以手动添加 ModuleConcatenationPlugin
或使用 optimization.concatenateModules
选项
new webpack.optimize.ModuleConcatenationPlugin();
此连接行为称为“范围提升”。
范围提升是 ECMAScript 模块语法特别实现的一项功能。因此,webpack 可能会根据你使用的模块类型以及 其他条件 回退到常规打包。
正如文章中所解释的,webpack 尝试实现部分作用域提升。它会将模块合并到单个作用域中,但并非在所有情况下都能做到。如果 webpack 无法合并模块,则有两种选择:Prevent 和 Root。Prevent 表示模块必须在其自己的作用域中。Root 表示将创建一个新的模块组。以下条件决定了结果
条件 | 结果 |
---|---|
非 ES6 模块 | Prevent |
由非 Import 导入 | Root |
从其他块导入 | Root |
由多个其他模块组导入 | Root |
使用 import() 导入 | Root |
受 ProvidePlugin 影响或使用 module | Prevent |
接受 HMR | Root |
使用 eval() | Prevent |
在多个块中 | Prevent |
从 "cjs-module" 导出 * | Prevent |
以下伪 JavaScript 解释了该算法
modules.forEach((module) => {
const group = new ModuleGroup({
root: module,
});
module.dependencies.forEach((dependency) => {
tryToAdd(group, dependency);
});
if (group.modules.length > 1) {
orderedModules = topologicalSort(group.modules);
concatenatedModule = new ConcatenatedModule(orderedModules);
chunk.add(concatenatedModule);
orderedModules.forEach((groupModule) => {
chunk.remove(groupModule);
});
}
});
function tryToAdd(group, module) {
if (group.has(module)) {
return true;
}
if (!hasPreconditions(module)) {
return false;
}
const nextGroup = group;
const result = module.dependents.reduce((check, dependent) => {
return check && tryToAdd(nextGroup, dependent);
}, true);
if (!result) {
return false;
}
module.dependencies.forEach((dependency) => {
tryToAdd(group, dependency);
});
group.merge(nextGroup);
return true;
}
使用 webpack CLI 时,--stats-optimization-bailout
标志将显示退出原因。使用 webpack 配置时,将以下内容添加到 stats
对象
module.exports = {
//...
stats: {
// Display bailout reasons
optimizationBailout: true,
},
};