Webpack 4 课程 - 第4部分 - 使用SplitChunksPlugin进行代码拆分(Code splitting)
本文翻译自wanago.io的Webpack 4 course – part four. Code splitting with SplitChunksPlugin 的文章。
自学用,翻译超垃圾
Webpack4给我们带来了众多变化。在这些变化之中,比如在快速打包方面,新版Webpack引入了SplitChunksPlugin属性,废弃了CommonsChunksPlugin。本篇文章,你将学习到如何拆分你的输出代码来改善应用程序的性能。
代码拆分(code splitting)思想
首先:webpack的代码拆分到底是什么?它允许你将代码拆分成多个文件。如果使用得当,这将大大提升应用程序的性能。其中的原因是,浏览器会缓存你的代码。当你每次更改代码的时候,包含了被更改代码的文件就会被访问你网站的所有浏览者重新下载。但是,你也有可能不会更改你代码里的依赖模块。如果你将依赖模块拆分到一个单独的文件里,那么浏览者将不会再次下载没有被更改的这些文件。
使用webpack生成一个或多个bundles,每个bundle包含了我们源代码最终的生成的版本。bundles由多个chunk组成。
入口(Entry)
Entry属性要填入一个文件地址,这个文件就是我们应用程序开始执行的地方,当然也是webpack进行打包的一个开始点。你可以只定义一个入口(比如针对Single-Page Application进行开发),也可以定义多个入口(比如针对Multiple-Page Application进行开发)。
当定义一个入口时,将会生成一个chunk。如果你使用字符串定义一个入口时,打包生成的文件名将被命名为main。如果你使用对象定义多个入口,打包生成的文件名将会被命名为对象里面的键值。如下的例子是等价的:
entry : './src/index.js'
entry : {
main : './src/index.js'
}
输出(output)
Output对象是关于webpack怎样打包以及打包代码到哪里的一个配置项。尽管entry可以配置多个,但是output只能有一个。这里的output就是我们命名chunk的重要地方。你可以为最终bundle输出指定一个具体的文件名,但是我们想要拆分代码的话,就最好不要这样做。你可以使用[name]
来生成一个模板,该模板可以生成我们输出文件的文件名:
output : {
filename : '[name].[chunkhash].bundle.js',
path : path.resolve(__dirname, 'dist')
}
值得一提的是这里的[chunkhash]
:它是基于你文件的内容生成的chunk专属hash值。当你的文件内容改变时,这个hash值也会跟着改变。由于浏览器会以不同的方式缓存文件。如果文件名改变,浏览器就会知道该文件需要重新加载。举个例子,一个chunkhash值类似于:0c553ebfd158e16da428
我们上面入口定义的main chunk就会被打包进一个命名为main.[chunkhash].bundle.js
的文件里头。
SplitChunksPlugin
有了SplitChunksPlugin,你可以移动你应用程序的特定代码部分到单独的文件中。如果一个模块被多个chunk引用,那么它可以很容易的在这些chunk中进行共享。这是webpack的默认行为。
utilities/users.js
export default [
{ firstName: "Adam", age: 28 },
{ firstName: "Jane", age: 24 },
{ firstName: "Ben", age: 31 },
{ firstName: "Lucy", age: 40 }
]
a.js
import _ from 'lodash';
import users from './users';
const adam = _.find(users, { firstName: 'Adam' });
b.js
import _ from 'lodash';
import users from './users';
const lucy = _.find(users, { firstName: 'Lucy' });
webpack.config.js
module.exports = {
entry: {
a: "./src/a.js",
b: "./src/b.js"
},
output: {
filename: "[name].[chunkhash].bundle.js",
path: __dirname + "/dist"
}
};
如果运行如上代码,你会发现webpack会生成两个文件:a.[chunkhash].bundle.js
和b.[chunkhash].bundle.js
,这两个文件都包含了lodash库模块:这样并不好!我以前说过,为共享的库创建单独的文件是webpack的默认行为,但是这涉及到async chunks,意味着我们需要异步导入这些文件。在介绍懒加载(lazy loading)的时候,我们将会介绍更多这样的话题。为了包含所有的类型的chunk,我们需要更改一下我们webpack的配置:
webpack.config.js
module.exports = {
entry: {
a: "./src/a.js",
b: "./src/b.js"
},
output: {
filename: "[name].[chunkhash].bundle.js",
path: __dirname + "/dist"
}
};
现在我们看到多了一个文件vendors~a~b.[chunkhash].bundle.js
,该文件包含了lodash库。这由于webpack的一些开箱即用的默认值:
splitChunks: {
chunks: "all",
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
}
配置里第一个vendors包含了从node_modules拆分的文件。default是用来拆分其他的共享模块文件。这就有一点冗余的问题。a.[chunkhash].bundle.js
和 b.[chunkhash].bundle.js
都包含了users.js
。这是因为,splitchunksplugin拆分chunk时,默认只拆分大于30kb的文件。我们可以容易更改这个大小:
webpack.config.js
module.exports = {
entry: {
a: "./src/a.js",
b: "./src/b.js"
},
output: {
filename: "[name].[chunkhash].bundle.js",
path: __dirname + "/dist"
},
optimization: {
splitChunks: {
chunks: "all",
minSize: 0
}
}
};
运行上面查看结果,发现新生成一个名为a~b.[chunkhash].bundle.js
,这是一个默认缓存组。由于我们的users.js文件远小于30kb,如果没有minSize属性的话,它将不会单独拆分到一个文件里。事实上,这是利大于弊的,因为拆分进单独文件中,并不会提升性能,反而还会强迫浏览器为小容量代码增加请求数量,使得性能降低。
我们还可以专门为utilities目录做更多:
webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
a: "./src/a.js",
b: "./src/b.js"
},
output: {
filename: "[name].[chunkhash].bundle.js",
path: __dirname + "/dist"
},
optimization: {
splitChunks: {
chunks: "all",
cacheGroups: {
utilities: {
test: /[\\/]src[\\/]utilities[\\/]/,
minSize: 0
}
}
}
}
};
现在我们的bundle包含了4个文件:a.[chunkhash].bundle.js
, b.[chunkhash].bundle.js
, vendors~a~b.[chunkhash].bundle.js
和 utilities~a~b.[chunkhash].bundle.js
。即使我们现在在splitchunks对象里设置全局的minSize:0
, default缓存组也不会被创建。这是因为所有被涉及到的文件被我们创建的utilities组给覆盖掉了。并且有一个默认为0的优先权(priority)属性,这个更高于default缓存组。如果你注意到了会发现,default缓存组已经设置了-20的优先权(priority)。
其实你也可以更改其他的默认参数,具体可以查看SplitChunksPlungin文档
总结
即使你只有一个入口点(比如针对大部分的单页面应用),把你的依赖拆分到单独的文件是个很好的选择。并且使用webpack4很容易做到,只要在你的splitChunks配置里,设置chunks为all就可能满足你的需求。如果想让我介绍其他方面,请告诉我。稍后我们将会学习到如何使用懒加载继续改善应用程序的性能,敬请期待!