DllPlugin
一个 creat-react-app 创建的项目中,像 react
, react-dom
等这样的基础库应该是不会经常变动的,所以如果将它们和业务代码分开来打包的话,即使业务代码有变动也不会影响到这些基础代码,这样就可以有效的利用浏览器的缓存。说到这里,可能有人会想到 webpack 的 CommonsChunkPlugin
也可以实现这样的功能。不过两者不同的是 DllPlugin
是事前将基础代码打包,如果基础代码没有变化的话,以后就不用打包,直接使用,这个有点像 c++ 的动态链接库的概念,这也是它为什么要叫 DllPlugin
的原因了。而 CommonsChunkPlugin
每次打包的时候都会重新打包这些基础代码。显然,按照上面的说法,DllPlugin
还可以提高打包的速度。
下面说一下使用 DllPlugin
的步骤。
编写 webpack.config.dll.js
我们在 config 目录下新建一个 webpack.config.dll.js
用来对基础代码进行打包配置:
1 | const path = require('path') |
这里有几点说明一下:
- 打包出来的文件名会像这样
vendor.659ad6e1.js
,这里我们把vendor
的输出到public/static/js
下,目的是为了执行npm run build
时将public
下的文件全拷贝到build
。 output
的属性library
必须和DllPlugin
的name
一致。DllPlugin
的path
指定了vendor
的描述文件vendor-manifest.json
的输出路径,该文件用于通知 webpack 哪些库不需要打包。HtmlWebpackPlugin
的作用是为了将生成的vendor
插入到 html 文件之中,我们在public/template
下面新建了一个index.html
用作HtmlWebpackPlugin
的template
,最后输出一个新的index.html
到public
下面。DefinePlugin
和UglifyJsPlugin
是为了优化打包后的vendor
的体积。
然后运行 NODE_ENV=production webpack --config ./config/webpack.config.dll.js
即可得到我们所要的文件:
1 | ├── index.html |
修改 webpack.config.prod.js
我们在 webpack.config.prod.js
的 plugins
中增加如下插件来告诉 webpack 打包时将 vendor
中的库排除掉。
1 | new webpack.DllReferencePlugin({ |
最后,我们来对比下使用该插件的前后的对比:
before1
✨ Done in 23.03s.
1 | -rw-r--r-- 1 youxingzhi staff 252510 8 13 18:28 0.8cca93f2.chunk.js |
总大小约为 1.83M
after
1 | ✨ Done in 18.23s. |
1 | -rw-r--r-- 1 youxingzhi staff 250133 8 13 18:47 0.1223c155.chunk.js |
总大小约为 1.85M
虽然优化后的总大小增加了约 20K 的大小(暂时不知道为啥),但是通过把这些公共且稳定的基础代码抽离出来以后,原来的各个文件都有了不同程度的瘦身,这么做还是有些价值的。
HappyPack
happypack 是 webpack 的一个插件,目的是通过多进程模型,来加速代码构建,关于该插件的原理可以参考这里。下面我们来看看我们的项目是怎么使用的:
在 webpack.config.base.js
中针对 eslint-loader
使用 happypack 插件:
1 | { |
在 webpack.config.prod.js
中针对 babel-loader
使用 happypack 插件:
1 | { |
然后再构建一次,发现速度又有了提升:
1 | ✨ Done in 16.21s. |
使用 BundleAnalyzerPlugin 分析打包后结果
我们在 package.json
中增加一条命令 build:analyze: ANALYZE=true node scripts/build.js
,然后在配置文件中添加这条语句:
1 | if (process.env.ANALYZE) { |
运行 yarn run build
后,得到如下结果:
这里有两个地方可以进一步优化:
- 我们异步加载的路由文件中打包了很多重复的组件,如图中红框所示的
Error
和TopupItem
等,这些组件都可以提取到一个公共的文件中。 - 有很多图片内联到了 js 代码中,可以提取出来
针对第二个问题,我们将 url-loader
的 limit
参数调小一些即可。而针对第一个问题,则需要 CommonsChunkPlugin
登场了。
CommonsChunkPlugin
这里一直没有搞成功,直到我看到了这篇文章,才知道可以用 CommonsChunkPlugin
的 async
这个属性:
1 | // async chunk common |
再次执行打包命令,得到结果:
这样就把异步路由里面的公共组件都提取到了 common-main.****
中了。
总结
这次优化我们首先使用 DllPlugin
把一些稳定的基础库提取到了 vendor.****.js
中,如果基础库没有升级的话,这个文件可以长期存在于用户的缓存中。然后我们使用 HappyPlugin
并行处理一部分打包流程,提高了打包的速度。最后我们通过 CommonsChunkPlugin
将异步路由中的公共组件提取出来,虽然增加了一些初始加载的代码,但是减少了异步路由中很多重复组件的代码,另外将图片抽离出来,使得最后打包出来的总体积减少到了 0.88M,效果还是不错的。