本文基于 webpack4
DLLPlugin
和DLLReferencePlugin
用某种方法实现了拆分 bundles,同时还大大提升了构建的速度。可以阅读 DllPlugin 获取更多相关内容。
与 splitChunks 的区别
说到拆分,大家会不会想到 splitChunks
?
splitChunks
作用是将第三方的组件拆分出来,打包成一个或几个包,用于长期缓存。这个行为可以在webpack中设置并自动完成。
DllPlugin
也能将第三方组件拆分出来,打包成一个或几个包,用于长期缓存且能加速打包过程。
那么它们的差异在于:
DllPlugin
需要设置打包的配置文件,并先于项目打包将第三方组件打包;DllPlugin
需要手动插入到对应的页面(可以使用add-asset-html-webpack-plugin
在打包项目的时候自动插入 );Dllplugin
内含有的组件在webpack
打包项目的时候,不经过打包过程。所以能加快打包速度。(这个其实可以使用webpack
中的external
来排除打包某些组件,然后通过链接将对应的组件链入页面,达到相同效果);- 如果库可以按需加载,
Dllplugin
将不能按需加载,它方式是全量的引入的,而splitChunks
可以按需加载地打包。
其实看起来,splitChunks
就是 DllPlugin
的自动版本。
DllPlugin的使用
// webpack.dll.config.js
const webpack = require('webpack'),
path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const resolvePath = path.join(__dirname, "../lib");
module.exports = {
mode: "production",
resolve: {
extensions: [".js", ".jsx"]
},
entry: {
react: ['react', 'react-dom', 'react-router-dom', 'redux', 'react-redux'],
},
output: {
path: resolvePath,
filename: "[name].[chunkhash:8].js",
library: "lib_[name]"
},
plugins: [
new CleanWebpackPlugin(),
new webpack.DllPlugin({
context: process.cwd(),
path: path.resolve(resolvePath, "[name]-manifest.json"),
name: 'lib_[name]'
})
]
};
// webpack.config.js
module.export = {
...
plugins: [
...
new webpack.DllReferencePlugin({
context: process.cwd(),
manifest: require(path.join(__dirname, '../lib/react-manifest.json'))
}),
new AddAssetHtmlPlugin({
filePath: path.resolve(__dirname, '../lib/react.edij4567.js')
}),
...
]
...
}
先运行 webpack.dll.config.js
(在 package.json
中设置命令 )
// package.json
...
"scripts": {
...
"build:dll": "webpack --config config/webpack.dll.config.js",
}
将对应的插件打包,然后再运行项目打包命令,将对应的dll打包进去。其中,应该自动检测 lib
文件夹中的文件,在 webpack.config.js
插入对应的 webpack.DllReferencePlugin
和 new AddAssetHtmlPlugin
。
效果对比
建议配合 webpack-bundle-analyzer 食用,更添美味!
由于打包机器不一样,结果可能会有所差异,而且webpack会利用缓存来加速打包,所以可以看到,第一次打包在没有缓存的情况下,时间会比较长。利用多次打包的表现来观察其中差异,应该能得到一个相对准确的结论。
只对react全家桶(react, react-dom,react-router-dom,redux,reacr-redux)进行操作,dll包大小为56.28KB。
此为交叉运行结果(秒):
dll: 25.85 10.43 11.02 10.53 11.48 10.38 12.44 11.86
正常打包:25.86 11.64 11.97 11.01 10.72 10.61 12.66 12.32
看的出来,优化的确是有效果,但是效果不明显。但有可能是因为dll的包小,导致效果不明显,加入antd
组件,再测试。
dll包(react, react-dom,react-router-dom,redux,reacr-redux,antd)大小为2.54MB。
结果(秒):
dll: 8.52 5.97 6.02 5.96 6.52 6.07 6.24 6.07
由此可以得出个结论:
dll包的大小,或者是dll包里的库文件需要webpack去处理的越多越大,dll方案的性能提升效果越是明显。
以上说的dll包并不指一个包,根据包拆分策略,一般限制244KB内,那么以上的2.54MB的包可能会拆分为好几个。所以上面说的dll包指的是抽离出来的所有库的总包。
但由于实际开发中,一般不会全量引入 antd
等较大的库,一般采取的方案是按需加载,这样引入的包变小,需要webpack 处理的文件也少了。
上文中说到的 与 splitChunks 的区别 的第三点,使用 externals
和引入对应组件的方案,观察一下效果:
先只排除react全家桶(react, react-dom,react-router-dom,redux,reacr-redux)。
结果(秒):
externals: 9.36 8.56 8.80 8.86 9.29 8.96 8.86 8.84
排除(react, react-dom,react-router-dom,redux,reacr-redux,antd)。
结果(秒):
externals: 4.78 4.81 4.71 4.57 4.59 4.74 4.63 4.74
这里解释(猜测)一下,为何第一次打包没有以上打包那样长耗时,因为 externals
会完全排除相关依赖,即不处理(相当于SVIP直接走了贵宾通道);而 DllPlugin
需要进行处理(即需要经历webpack的处理流程,只是不去解析对应依赖,但是遇到依赖会去dll文件那里引入)(相当于VIP不需要额外处理只需要跟着走,但是跟非VIP的一样还是走完了整个过程,非VIP则需要进行全套处理)。
总结一下
- 用
DllPlugin
进行项目打包(只打包一次项目代码然后上线的,而不是开发时更改文件的打包构建时的情况)还是有效果的,但是微乎其微; - 当
DllPlugin
包含原webpack需要处理的文件越多越大时,性能效率效果越明显; externals
效果更佳Dllplugin
和externals
均是全量引入,由于不经过webpack,按需加载的福利自然也无法享受到- 此处说一个使用
DllPlugin
有可能会出现的问题,当抽离相关的组件到dll文件后,假如某个组件是依赖于dll内的某个组件下的某库,又可能将会将dll的那个组件重新引入打包到对应位置。即某个组件import abc from 'xxx/abc'
那么xxx
组件将再次打包(xxx
已打包在dll文件内)到项目里。
BTW,vue-cli (RFC: beta.10, Upgrading to webpack 4 + vue-loader 15 #1205) 和 create-react-app 都选择了不使用 dll 了。从上面的打包结果,你也可以知道原因:dll 能提升的效果太小了,或者说,webpack4 太牛了,打包性能大大超越以前。
时间