Webpack CommonsChunkPlugin 理解

 

最近读了一下webpack的文档,读到CommonsChunkPlugin这个插件,深深折服与webpack的强大,同时也产生了一些自己的疑问。

 

首先,CommonsChunkPlugin这个插件是用来提取公共代码的,通过将公共模块提取出来,只在页面加载的时候引入一次,提升应用的加载效率。

顺便提一下,chunk其实就是代码块的意思,可能是一个或多个模块,一般就是一个js文件。

 

CommonsChunkPlugin有中文翻译的文档,但是感觉并不是很通顺,英文文档看完也有一些疑惑,比如minChunks到底是做什么用的,怎么用?chunks是什么?首先贴一下文档。

 1 {
 2   name: string, // or
 3   names: string[],
 4   // The chunk name of the commons chunk. An existing chunk can be selected by passing a name of an existing chunk.
 5   // If an array of strings is passed this is equal to invoking the plugin multiple times for each chunk name.
 6   // If omitted and `options.async` or `options.children` is set all chunks are used, otherwise `options.filename`
 7   // is used as chunk name.
 8   // When using `options.async` to create common chunks from other async chunks you must specify an entry-point
 9   // chunk name here instead of omitting the `option.name`.
10 
11   filename: string,
12   // The filename template for the commons chunk. Can contain the same placeholders as `output.filename`.
13   // If omitted the original filename is not modified (usually `output.filename` or `output.chunkFilename`).
14   // This option is not permitted if you're using `options.async` as well, see below for more details.
15 
16   minChunks: number|Infinity|function(module, count) -> boolean,
17   // The minimum number of chunks which need to contain a module before it's moved into the commons chunk.
18   // The number must be greater than or equal 2 and lower than or equal to the number of chunks.
19   // Passing `Infinity` just creates the commons chunk, but moves no modules into it.
20   // By providing a `function` you can add custom logic. (Defaults to the number of chunks)
21 
22   chunks: string[],
23   // Select the source chunks by chunk names. The chunk must be a child of the commons chunk.
24   // If omitted all entry chunks are selected.
25 
26   children: boolean,
27   // If `true` all children of the commons chunk are selected
28 
29   async: boolean|string,
30   // If `true` a new async commons chunk is created as child of `options.name` and sibling of `options.chunks`.
31   // It is loaded in parallel with `options.chunks`.
32   // Instead of using `option.filename`, it is possible to change the name of the output file by providing
33   // the desired string here instead of `true`.
34 
35   minSize: number,
36   // Minimum size of all common module before a commons chunk is created.
37 }
  • namenames:chunk的名称,如果这个chunk已经在entry中定义,该chunk会被直接提取;如果没有定义,则生成一个空的chunk来提取其他所有chunk的公共代码。
  • filename:可以指定提取出的公共代码的文件名称,可以使用output配置项中文件名的占位符。未定义时使用name作为文件名。
  • chunks:可以指定要提取公共模块的源chunks,指定的chunk必须是公共chunk的子模块,如果没有指定则使用所有entry中定义的入口chunk。
  • minChunks:在一个模块被提取到公共chunk之前,它必须被最少minChunks个chunk所包含。(通俗的说就是一个模块至少要被minChunks个模块所引用,才能被提取到公共模块。)

该数字必须不小于2或者不大于chunks的个数。默认值等于chunks的个数。

如果指定了Infinity,则创建一个公共chunk,但是不包含任何模块,内部是一些webpack生成的runtime代码和chunk自身包含的模块(如果chunk存在的话)。

用户也可以定制自己的逻辑去生成代码。

 

我们看一个简单的例子。

 1 module.exports = {
 2     entry: {
 3         app: './src/index.js',
 4         vender: [
 5             'lodash',
 6             'otherlib'
 7         ]
 8     },
 9     plugins: [
10         new webpack.optimize.CommonsChunkPlugin({
11             name: 'vender'
12         })
13     ],
14     output: {
15         filename: '[name].[chunkhash].js',     // 使用Hash来命名文件,实现文件缓存的功能。当文件内容发生变化,文件名会随之改变。
16         path: path.resolve(__dirname, 'dist')
17     }
18 };

上面的代码中定义了两个入口,app和vender(公共库),plugins中使用CommonsChunkPlugin提取vender。

 

vender是我们提取出来的公共chunk,通常不会被修改,所以理应在每次编译后文件名保持一致。然而,我们尝试修改入口文件index.js会发现,vender的文件名会发生变化。

原因呢上面提到过,由于每次编译会导致vender的module.id发生变化,内部的runtime代码随之发生改变。

 

解决方案有以下几种:

  1. 使用NamedModulesPlugin插件,用文件路径而非默认的数字ID来作为模块标识。

  2. 使用HashedModuleIdsPlugin插件,用相对路径的Hash值来作为模块标识。推荐在生产环境中使用。

  3. 将runtime部分的代码提取到一个单独的文件中,代码如下。

 1 module.exports = {
 2     entry: {
 3         app: './src/index.js',
 4         vender: [
 5             'lodash'
 6         ]
 7     },
 8     plugins: [
 9         new webpack.optimize.CommonsChunkPlugin({
10             name: 'vender',
11             minChunks: Infinity
12         }),
13         new webpack.optimize.CommonsChunkPlugin({
14             name: 'manifest',
15             chunks: ['vender']
16         })
17     ],
18     output: {
19         filename: '[name].[chunkhash].js',
20         path: path.resolve(__dirname, 'dist')
21     }
22 };

代码中再次使用了CommonsChunkPlugin,从vender中提取出了名为manifest的运行时代码。

 

 

未完待续,欢迎指正。

posted @ 2017-10-12 11:29  dong93  阅读(6401)  评论(0编辑  收藏  举报