2020-05-02 更新
近期发现 dll 已经落后了,随着 webpack 4 的优化,dll 带来的提升已经不太明显。所以据说 vue-cli 和 create-react-app 曾经采用过的 dll 优化,现在也已经不再使用。现在有一个更好的方式,就是在开发模式下采用 hard-source-webpack-plugin
。不用额外的 dll 配置,不用生成多余文件,也不用考虑注入问题。开发模式下二次构建的速度会有所提升,production 模式则根据实际业务和文件大小来调整配置了。
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin')
const IS_DEV = process.env.NODE_ENV === 'development'
module.exports = {
configureWebpack() {
const devPlugins = [
/**
* 缓存加速二次构建速度
*/
new HardSourceWebpackPlugin(),
new HardSourceWebpackPlugin.ExcludeModulePlugin([
{
test: /mini-css-extract-plugin[\\/]dist[\\/]loader/
}
])
]
if(IS_DEV) {
return {
plugins: devPlugins
}
}
}
}
DLLPlugin 和 DLLReferencePlugin 用某种方法实现了拆分 bundles,同时还大大提升了构建的速度。一般用来将常用的库,例如 vue,vuex 抽离出来,后续构建过程跳过这些库。既能加快打包速度,又能充分利用缓存。
首先安装如下三个包,缺一不可。
yarn add webpack-cli add-asset-html-webpack-plugin clean-webpack-plugin -D
项目根目录创建 webpack.dll.config.js,写入如下代码:
const path = require('path')
const webpack = require('webpack')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
// dll文件存放的目录
const dllPath = 'public/vendor'
module.exports = {
entry: {
// 需要提取的库文件
vendor: ['vue', 'vue-router', 'vuex', 'axios']
},
output: {
path: path.join(__dirname, dllPath),
filename: '[name].dll.js',
// vendor.dll.js中暴露出的全局变量名
// 保持与 webpack.DllPlugin 中名称一致
library: '[name]_[hash]'
},
plugins: [
// 清除之前的dll文件
new CleanWebpackPlugin(),
// 设置环境变量
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('production')
}
}),
// manifest.json 描述动态链接库包含了哪些内容
new webpack.DllPlugin({
path: path.join(__dirname, dllPath, '[name]-manifest.json'),
// 保持与 output.library 中名称一致
name: '[name]_[hash]',
context: process.cwd()
})
]
}
package.json 里的 scripts 添加如下代码:
"dll": "webpack -p --progress --config ./webpack.dll.config.js",
运行 npm run dll
可以看到在 public 目录下生成了一个 vendor 目录,里面有两个文件:
vendor.dll.js 动态链接库文件包含了大量模块的代码,通过 vendor_f1555695fd34524aee6f 变量把自己暴露在了全局中,也就是可以通过 window.vendor_f1555695fd34524aee6f 可以访问到它里面包含的模块
var vendor_f1555695fd34524aee6f=...
vendor-manifest.json 清楚地描述了与其对应的 dll.js 文件中包含了哪些模块,以及每个模块的路径和 ID
{"name":"vendor_f1555695fd34524aee6f","content": {...}}
这里生成了 vendor.dll.js,可以直接手动在 index.html 引入,但是也可以通过 webpack 插件自动注入 index.html。代码如下:
vue.config.js
const webpack = require('webpack')
const CompressionPlugin = require('compression-webpack-plugin')
const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin')
const path = require('path')
const resolve = url => path.resolve(__dirname, url)
const IS_PROD = process.env.NODE_ENV === 'production'
module.exports = {
configureWebpack(config) {
const plugins = [
...
]
if (IS_PROD) {
// 有几个 dll.js,这里就响应 new 几个 webpack.DllReferencePlugin
plugins = plugins.concat([
new webpack.DllReferencePlugin({
context: process.cwd(),
manifest: require('./public/vendor/vendor-manifest.json')
}),
// 将 dll 注入到 生成的 html 模板中
new AddAssetHtmlPlugin({
// dll文件位置
filepath: resolve('./public/vendor/*.js'),
// dll 引用路径
publicPath: './vendor',
// dll最终输出的目录
outputPath: './vendor'
})
])
}
return {
plugins
}
},
}
总结: