Open Adamwu1992 opened 6 years ago
options
配置参数参考文档:
名词解释:
chunk
: 插件在处理代码时操作的单位,任何一段代码可以视为一个chunk
;entry chunk
: 在options.entry
里指定的文件里的代码被称作entry chunk
,如果是一个数据,多个文件的代码被合并成一个entry chunk
;children chunk
: 从入口文件开始,webpack
会根据split point
(比如异步引入等)分割代码,被分割出来的代码块称为children chunk
;source chunk
: CommonsChunkPlugin
将要作用的代码块,通常由chunks
或者children
指定;commons chunk
: CommonsChunkPlugin
从source chunk
里根据规则提取出来的公共代码块,需要注意的是,一个chunk并不等于最后打包出来的一个文件,通常多个chunk会被合并成一个文件减少下载请求;children
& async
& deepChildren
我一开始被这三个参数困扰了好久,尤其是文档中描述的children of the commons chunk
和 descendants of the commons chunk
到底有何差异,一直没能找到例子体现。
先上结论:
children
:name
属性需要设置为一个已经存在的entry chunk
,source chunk
中提取出来的commons chunk
都会被合并到指定的entry chunk
中。async
: name
属性需要设置为一个已经存在的entry chunk
,从source chunk
中的children chunk
里提取出的commons chunk
被放进一个独立的异步模块。deepChildren
: name
属性需要设置为一个新的名字,从source chunk
中的entry chunk
里提取出来的commons chunk
被放进新生成的这个模块里。首先,下面是源码的结构:
+ utils
- babel-polypill.js
- sillyname.js
+ src
- main.js
- cat.js
- dog.js
模块的依赖关系是:
main.js (main entry)
+ babel-polyfill
+ src/cat (dynamic)
+ sillyname
+ src/dog (dynamic)
+ sillyname
babel-polyfill
被入口文件直接引用,而cat
和dog
是以异步加载的方式引用。首先使用以下的配置打包:
entry: {
main: ['./apps/src/main.js']
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: ({ resource }) => (
resource !== undefined &&
resource.indexOf('utils') !== -1
)
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest',
minChunks: Infinity
}),
new BundleAnalyzerPlugin({
analyzerMode: 'static'
})
]
使用minChunks
函数从entry chunk
里提取公共模块,由于只有一个main
是入口模块,cat
和dog
会被webpack
的code split
分离成单独的模块,所以只有main
会被提取,所以打包出来的依赖关系应该如下:
vendor
+ babel-polyfill
main
+ src
1
+ src/cat
+ sillyname
0
+ src/dog
+ sillyname
manifest
修改配置,使用children
属性看看有什么变化:
...
new webpack.optimize.CommonsChunkPlugin({
name: 'main',
children: true,
minChunks: ({ resource }) => (
resource !== undefined &&
resource.indexOf('utils') !== -1
)
}),
If
true
all children of the commons chunk are selected. ----webpackthe word "commons chunk" should probably be replaced by "entry chunk". ----stack overflow
这个配置下,webpack
会从entry chunk
的children
中提取,并且将生成的commons chunk
合并到自身,通常使用chidlren
属性是,name
都会被指定为一个已经存在的entry chunk
,我尝试做了一些不通常的操作,我改变name
的值为一个新的模块名,但是插件忽略了这个名字,仍然将提取出来的common chunk
合并到对应的entry chunk
中,此处的children
可以认为是通过code split
分割出来的额模块,所以sillyname
也会被提取,打包出来的依赖关系如下:
main
+ src
+ babel-polyfill
+ sillyname
1
+ src/cat
0
+ src/dog
manifest
如果将上面的children
换成deepChildren
,会有什么变化呢?打包依赖关系如下:
main
+ src
+ babel-polyfill
1
+ src/cat
+ sillyname
0
+ src/dog
+ sillyname
manifest
这简直是没有任何提取嘛,一定是打开方式不对。 改变name,让插件生成一个新的commons chunk:
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
deepChildren: true,
minChunks: ({ resource }) => (
resource !== undefined &&
resource.indexOf('utils') !== -1
)
}),
生成的依赖如下:
main
+ src
vendor
+ babel-polyfill
1
+ src/cat
+ sillyname
0
+ src/dog
+ sillyname
manifest
sillyname
不会被提取,所以deepChildren
指定的chunk
里不包括entry chunk
分割出来的children chunk
,此时得到第一个结论,children
和deepChildren
的区别是,前者的source chunk为entry chunk
和children chunk
,后者为entry chunk
,而且设置children情况下,那么需要为一个已经存在的entry chunk,设置deepChildren时则需要生成一个新的commons chunk。
回到上一个场景,设置children为true时,插件会把entry chunk和children chunk中提取出的commons chunk都合并到name(此时name指定为一个已经存在的entry chunk)指定的entry chunk中,会导致一个模块的体积过大,且有不必要的下载浪费。 我们设置async,生成的依赖关系如下:
main
+ src
+ babel-polyfill
0
+ sillyname
2
+ src/cat
1
+ src/dog
manifest
从结果来看,插件提取了children chunk,并且生成了一个新的异步模块,而entry chunk中的提取出的commons则被合并到entry chunk中,就像指定了children: true时的打包方式一样。我经过测试,如果删除children配置结果并不会改变。
测试了这么多,发现要么是children chunk里的公共代码不会被提取到新文件,要么是entry chunk里的代码不会被提取到新文件,达不到我们对前端极致的性能追求啊。尝试组合一下配置:
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: ({ resource }) => (
resource !== undefined &&
resource.indexOf('utils') !== -1
)
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'main',
children: true,
async: true,
minChunks: ({ resource }) => (
resource !== undefined &&
resource.indexOf('utils') !== -1
)
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest',
minChunks: Infinity
}),
new BundleAnalyzerPlugin({
analyzerMode: 'static'
打包后的依赖关系:
vendor
+ babel-polyfill
main
+ src
0
+ sillyname
2
+ src/cat
1
+ src/dog
manifest
第一个插件将entry chunk里的代码提取到vender里,第二个插件将children chunk里的代码提取成一个异步模块,总算把所有的代码都分开了
单页面应用打包
entry
入口分割以上的配置会让webpack从两个入口打包,最终会生成两个文件,
index.js
里会包含index.js
的代码以及所有依赖模块的代码,chunk.js
里会包含Array.js
、String.js
的代码以及它们依赖的代码,同时以上两个模块里还会包含webpack的运行时代码。这样的配置没有什么卵用,因为如果
index.js
和Array.js
都引用了一个A.js
的模块,那么这个模块会分别被打进两个模块里,并不会达到提取公共模块的目的,此时需要CommonChunkPlugin插件了。这种配置下打出来的
chunk.js
和第一种配置时一样,但是会把index.js
和chunk.js
里的共用代码都从index.js
里抽离出来,webpack运行时代码也会抽离出来,index.js
的体积会减小很多。这样做的目的是给
chunk.js
设置一个较长时间的缓存提升性能,因为chunk.js
按照设想都是第三方库,每次打包hash
都不会发生变化。但是现实是,改动了index.js
里的内容,chun k.js
也会发生改变。原因是webpack的运行时代码包含么个模块的引用id,模块发生变化会导致运行时代码变化,而我们的运行时代码是包含在chunk.js
里的。增加一个插件,
chunks
参数指定从chunk.js
模块里提取代码,然后生成一个manifest.js
模块,原本在chunk.js
里的运行时代码会被提取出来,这样每次改动业务代码(index.js
)的时候,只有index.js
和manifest.js
代码hash会发生变动,达到了优化的目的。我测试了用这种配置也可以达到目的,我猜测大概的是因为
CommonChunkPlugin
会把webpack的运行时代码打包到最后一个模块里,区别就是:配置1在生成manifest.js
模块时只会去提取chunk.js
模块,配置2下,插件会运行两遍,只不过第二遍没有公共代码可以提取里,所以manifest.js
里只包含运行时代码。chunks
缺省时插件会提取所有的entry chunk
,所以生成manifest.js
时指定模块效率更佳。