ModuleConcatenationPlugin

在过去,webpack 打包时的一个权衡是,打包中的每个模块都会被包裹在单独的函数闭包中。这些包裹的函数使得 JavaScript 在浏览器中执行速度变慢。相比之下,像 Closure Compiler 和 RollupJS 这样的工具会将所有模块的作用域“提升”或合并到一个闭包中,从而使得代码在浏览器中执行速度更快。

此插件将在 webpack 中启用相同的合并行为。默认情况下,此插件在 生产模式 下已启用,在其他情况下则禁用。将 optimization.concatenateModules 选项 设置为 false 即可覆盖在生产模式下的默认优化。手动添加 ModuleConcatenationPlugin 或使用 optimization.concatenateModules 选项可以在其他模式下启用合并行为:

new webpack.optimize.ModuleConcatenationPlugin();

这种合并行为被称为“作用域提升”。

作用域提升是 ECMAScript 模块语法特别支持的一个功能。因此,webpack 可能会根据使用的模块类型和 其他条件 回退到普通的打包方式。

优化 bailout

正如文章所解释的,webpack 尝试实现部分作用域提升。它会将模块合并到一个单一的作用域中,但并非在所有情况下都能做到。如果 webpack 无法合并一个模块,则有两种替代方案:阻止与创建新组。阻止意味着该模块必须在自己的作用域中;创建新组意味着会创建一个新的模块组。以下条件决定了不同的输出:

条件输出
没有 ES6 模块阻止
从非导入导入创建新组
从其他 chunk 导入创建新组
被其他多个模块分组导入创建新组
import() 语法导入创建新组
ProvidePlugin 影响或使用 module阻止
接受 HMR创建新组
使用 eval()阻止
在多个 chunk 中阻止
export * from "cjs-module"阻止

模块分组算法

下面的伪 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;
}

调试优化 bailout

如果使用的是 webpack CLI,使用 --stats-optimization-bailout 标志将显示出现 bailout 的原因;如果使用的是 webpack 配置,则需要向 stats 对象添加以下内容:

module.exports = {
  //……
  stats: {
    // 此配置将显示出现 bailout 的原因
    optimizationBailout: true,
  },
};

1 位译者

3 位贡献者

skipjackTheLarkInnbyzyk