有人能解释一下Webpack的CommonsChunkPlugin吗?

84

我大致了解到,CommonsChunkPlugin插件会查看所有入口点,检查它们之间是否存在共同的包/依赖项,并将它们分离成自己的捆绑包。

因此,假设我有以下配置:

...
enrty : {
    entry1 : 'entry1.js', //which has 'jquery' as a dependency
    entry2 : 'entry2.js', //which has 'jquery as a dependency
    vendors : [
        'jquery',
        'some_jquery_plugin' //which has 'jquery' as a dependency
    ]
},
output: {
    path: PATHS.build,
    filename: '[name].bundle.js'
}
...

如果我没有使用CommonsChunkPlugin进行捆绑

我最终会有3个新的捆绑文件:

  • entry1.bundle.js 包含 entry1.js 的完整代码和 jquery,并包含自己的运行时
  • entry2.bundle.js 包含 entry2.js 的完整代码和 jquery,并包含自己的运行时
  • vendors.bundle.js 包含 jquerysome_jquery_plugin 的完整代码,并包含自己的运行时

显然,这很糟糕,因为我可能会在页面中加载三次jquery,我们不希望出现这种情况。

如果我使用CommonsChunkPlugin进行捆绑

根据我传递给CommonsChunkPlugin的参数,将发生以下情况之一:

  • CASE 1 : 如果我传递 { name : 'commons' },我最终将得到以下捆绑文件:

    • entry1.bundle.js 包含 entry1.js 的完整代码和 jquery,不包含运行时
    • entry2.bundle.js 包含 entry2.js 的完整代码和 jquery,不包含运行时
    • vendors.bundle.js 包含 some_jquery_plugin 的完整代码,并且不包含运行时
    • commons.bundle.js 包含 jquery 的完整代码并包含运行时

    这样我们最终得到了一些更小的捆绑文件,而运行时包含在commons捆绑文件中。这还可以,但不理想。

  • CASE 2 : 如果我传递 { name : 'vendors' },我最终将得到以下捆绑文件:

    • entry1.bundle.js 包含 entry1.js 的完整代码和 jquery,不包含运行时
    • entry2.bundle.js 包含 entry2.js 的完整代码和 jquery,不包含运行时
    • vendors.bundle.js 包含 jquerysome_jquery_plugin 的完整代码,并包含运行时。

    这样,我们最终得到了一些更小的捆绑文件,但是运行时现在包含在vendors捆绑文件中。这比上一个情况稍微糟糕一些,因为现在运行时位于vendors捆绑文件中。

  • CASE 3 : 如果我传递 { names : ['vendors', 'manifest'] },我最终将得到以下捆绑文件:

    • entry1.bundle.js 包含 entry1.js 的完整代码和 jquery,不包含运行时
    • entry2.bundle.js 包含 entry2.js 的完整代码和 jquery,不包含运行时
    • <

对于情况1,我认为你应该指定minChunks属性。 - Marko
10
我从你的问题中学到了很多,非常感谢! - Rafael Eyng
1
非常感谢您的提问,并在这个插件上澄清我的疑惑❤ - Glenn Mohammad
也许有人知道,在Webpack 4中这些示例会是什么样子? - StalkAlex
1个回答

107
这是CommonsChunkPlugin的工作原理。
共享多个入口块的模块被分配到一个公共块中。 一个复杂配置的好例子可以在Webpack repository中找到。 CommonsChunkPlugin在Webpack的优化阶段运行,这意味着它在内存中操作,就在块被封装并写入磁盘之前。
当定义了几个公共块时,它们按顺序处理。在你的情况下有3个,就像运行插件两次一样。但请注意,CommonsChunkPlugin可以有更复杂的配置(minSize,minChunks等),这会影响模块移动的方式。 CASE 1: There are 3 "entry" chunks (entry1, entry2 and vendors). The configuration sets the "commons" chunk as a common chunk. The plugin processes the "commons" common chunk (since the chunk does not exist, it is created): It collects the modules that are used more than once in the other chunks: entry1, entry2 and vendors use jquery so the module is removed from these chunks and is added to the "commons" chunk. The "commons" chunk is flagged as an "entry" chunk while the entry1, entry2 and vendors chunks are unflagged as "entry". Finally, since the "commons" chunk is an "entry" chunk it contains the runtime and the jquery module.
  1. 有3个条目块(entry1、entry2和vendors)。
  2. 配置将vendors块设置为公共块。
  3. 插件处理vendors公共块:
    1. 它收集在其他块中使用超过一次的模块:entry1和entry2使用jquery,因此从这些块中删除该模块(请注意,它不会添加到vendors块中,因为vendors块已经包含它)。
    2. vendors块被标记为条目块,而entry1和entry2块被取消标记为条目块。
  4. 最后,由于vendors块是一个条目块,它包含运行时和jquery/jquery_plugin模块。
  1. 有3个 entry 块(entry1entry2vendors)。
  2. 配置将 vendors 块和 manifest 块设置为公共块。
  3. 插件会创建 manifest 块,因为它不存在。
  4. 插件会处理 vendors 公共块:
    1. 它收集在其他块中使用多次的模块: entry1entry2 使用 jquery,因此该模块从这些块中删除(请注意,它不会添加到 vendors 块中,因为 vendors 块已经包含它)。
    2. vendors 块被标记为一个 entry 块,而 entry1entry2 块则取消了 entry 标记。
  5. 插件会处理 manifest 公共块(由于该块不存在,因此会创建它):
    1. 它收集在其他块中使用多次的模块:由于没有使用多次的模块,因此不会移动任何模块。
    2. manifest 块被标记为 entry 块,而 entry1entry2vendors 则取消了 entry 标记。
  6. 最后,由于 manifest 块是一个 entry 块,因此它包含运行时。

2
我还有一个问题,如果你愿意回答的话。假设在上面的例子中,entry1.jsentry2.js除了jquery文件之外,还有另一个共同的文件,我们称之为ownLib.js。在CASE 2和CASE 3中,ownLib.js会被放入vendors.bundle.js中,对吗?如何使除供应商文件以外的公共文件分离成它们自己的块,而不是vendors块的一部分?抱歉打扰你,但我仍在学习如何使用webpack。 - Dimitris Karagiannis
7
没问题:是的,ownLib.js 应该放在第一个公共块中。如果你想将通用依赖项收集到另一个块中,你需要传递像这样的内容: { names : ['common', 'vendors', 'manifest'] } - Laurent Etiemble
6
好问题,好答案,好讨论。看起来我终于明白了。 - oluckyman
@LaurentEtiemble: 新的webpack.optimize.CommonsChunkPlugin({ names: ['common', 'vendor', 'manifest'], chunks: ['main', 'Home'], }),这是我的配置,但是webpack运行时出现在所有公共块中。你能解释一下为什么吗? - Bhargavi Gunda
3
我过去的一天都在阅读CommonsChunkPlugin文档,这是我第一次阅读到,在执行后,处理过的代码块“被取消标记为入口”。这基本上解释了我遇到的所有问题--如果我可以投多个赞,我会这样做的。 - Coderer
显示剩余8条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接