Webpack 4 教程 - 4. 使用SplitChunksPlugin插件进行代码分割
Webpack 4给我们带来了一些改变。包括更快的打包速度,引入了SplitChunksPlugin插件来取代(之前版本里的)CommonsChunksPlugin插件。在这篇文章中,你将学习如何分割你的输出代码,从而提升我们应用的性能。
代码分割的理念
首先搞明白: webpack里的代码分割是个什么鬼? 它允许你将一个文件分割成多个文件。如果使用的好,它能大幅提升你的应用的性能。其原因是基于浏览器会缓存你的代码这一事实。每当你对某一文件做点改变,访问你站点的人们就要重新下载它。然而依赖却很少变动。如果你将(这些依赖)分离成单独的文件,访问者就无需多次重复下载它们了。
使用webpack生成一个或多个包含你源代码最终版本的“打包好的文件”(bundles),(概念上我们当作)它们由(一个一个的)chunks组成。
入口(Entry)
入口定义了我们的应用代码开始执行的那个文件,webpack从这个文件开始打包。你能定义一个入口点(常见于单页应用 - Single-Page Application), 或者多个入口点(常见于多页应用 - Multiple-Page Application)。
定义一个入口点就生成一个chunk。如果你只是用字符串的方式定义了一个入口点,其就被命名为main。如果你用对象的方式定义多个入口点,其就被命名为入口对象中的键值。下面两个例子是等价的:
entry: './src/index.js'
entry: { main: './src/index.js' }
输出(Output)
输出对象配置webpack如何输出我们的打包(bundles)和资源(assets),以及将它们放到哪里。因为可能多于一个入口点,而只(能)指定一个输出配置。事实上我们就用chunks来给其一一命名。你能给打包输出的文件定义一个确定的名字,但既然我们想要分割我们的代码,就不能这么干。你得使用[name]来创建输出文件名的模板:
output: { filename: '[name].[chunkhash].bundle.js', path: path.resolve(__dirname, 'dist') }
这里要注意的重要事情是 [chunkhash]: 它基于你文件的内容给每个chunk生成了一个特有的hash。它只有在你的文件内容本身变化的时候才变化。事实上,(如果内容没有变化)浏览器会缓存它。如果文件名改变了(译注:这里是指hash变化了,而hash是文件名的一部分,即意味着文件的内容变化了),浏览器就知道要重新下载了。chunkhash看起来长得就象这样子: 0c553ebfd158e16da428
如此这般,我们的main chunk就会被打包成名为 main.[chunkhash].bundle.js的文件。
SplitChunksPlugin插件
正是有了SplitChunksPlugin插件,你能在你的应用中移出一部分到单独的文件中。如果一个模块被多个chunks使用,(分割出它之后)就能很容易的在这些chunks之间共享。这正是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的默认行为会给共享库创建分离的文件,但这涉及到异步chunks,即我们异步导入文件。我们在讨论懒加载的时候再来更多的覆盖这一主题。为了针对所有类型的chunks,我们需要稍微改改我们的webpack配置:
// 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" } }, };
现在我们看到创建了一个附加的名叫vendors~a~b.[chunkhash].bundle.js的文件,其包含了Lodash库。事实上这全靠了配置中本身默认固有一个cacheGroups的配置项:
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插件默认只分割超过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 } } };
默认的cache group配置使得会生成一个名为a~b.[chunkhash].bundle.js的文件。因为我们的users.js文件大大小于30kb,如果没有修改minSize属性的话,它就不会分割成一个单独的文件。在真实情形下,这是合理的,因为(如分割)并不能带来性能确实的提升,反而使得浏览器多了一次对utilities.js的请求,而这个utilities.js又是如此之小(不划算)。
我们能更进一小步,只针对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 } } } } };
现在我们打包出4个文件:a.[chunkhash].bundle.js, b.[chunkhash].bundle.js, vendors~a~b.[chunkhash].bundle.js 和 utilities~a~b.[chunkhash].bundle.js。即使我们现在设置一个全局的minSize: 0(在splitChunks对象中),默认的cache group也不会创建。这是因为被我们创建的utilities group覆盖了。utilities group默认的优先级值是0, 高于default cache group的。你可能已经注意到,默认cache group的优先级设置成了-20。
还有其它一些默认参数的设置,你能查阅SplitChunksPlugin的文档。
小结
即使你只有一个入口点(见于大多数的单页应用),分离依赖到单独的文件中也是一个好主意。使用SplitChunksPlugin来达到这一目标实际上很简单,因为这是Webpack 4的默认行为,很有可能你只需设置chunks: “all”就足够了。如果你想要我说说其它有关的东西,告诉我吧。很快我们将学习如何应用懒加载来更好的提升性能, 敬请期待!