前言
其他公共资源主要有两个用户:
一、项目是多页应用,有多个入口,多页面之间有共同使用的模块、代码等。这时可以把多个页面的公共部分代码提取出来。
二、我们可以把整个C端项目提取出公共代码,这样用户在打开一个页面的时候,顺便加载缓存了公共文件,后续再打开其他页面的时候,可以直接命中公共文件的缓存,减少资源的下载。
如何提取公共代码
有两种方法都可以达到提取公共代码的目的,一是使用 SplitChunksPlugin 插件,另一种是使用 DllPlugin、DllReferencePlugin两个插件的组合完成。
在webpack3及之前我们使用 CommonsChunkPlugin 来抽取公共资源,webpack4后删除了 CommonsChunkPlugin,改用 SplitChunksPlugin。
SplitChunksPlugin
若不加以配置,webpack4 会自动使用 splitChunks 对资源块进行拆分,默认拆分规则如下:
- 块被多处引用或者来自 node_modules 目录
- 块大小大于 30kb(压缩前)
- 按需加载的块并行请求数不能大于 5 个
- 初始化加载的块并行请求数不能大于 3 个
也就是说 splitChunks 只会拆分被多次引用的块,或者该块来自于 node_modules 目录;并且该块在压缩前的大小应大于 30kb,否则不拆离。同时,最后两个条件对并行请求数做出了限制,这就意味着为了满足后两个条件,可能会产生体积较大的块。
自定义配置
splitChunksPlugin 插件提供了配置参数,使得开发者能够对包进行自定义配置和拆分块。
module.exports = {
//...
optimization: {
splitChunks: {
chunks: 'async', // all async initial 选择对哪些块进行优化
minSize: 30000, // 被拆分的最小大小(压缩前)
minChunks: 1, // 被共享的最小次数
maxAsyncRequests: 5, // 最大按需求并行请次数
maxInitialRequests: 3, // 最大初始化并行请求数
automaticNameDelimiter: '~', // 自动命名分隔符
name: true, // 自动为块命名
cacheGroups: {
vendor: { // key 为entry中定义的 入口名称
chunks: "initial", // 必须三选一: "initial" | "all" | "async"(默认就是异步)
test: /react|lodash/, // 正则规则验证,如果符合就提取 chunk
name: "vendor", // 要缓存的 分隔出来的 chunk 名称
minSize: 0,
minChunks: 1,
enforce: true,
maxAsyncRequests: 1, // 最大异步请求数, 默认1
maxInitialRequests : 1, // 最大初始化请求书,默认1
reuseExistingChunk: true // 可设置是否重用该chunk(查看源码没有发现默认值)
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
}
}
}
DllPlugin
相对于 splitChunksPlugin,DllPlugin 的配置要相对复杂些。
DLLPlugin 和 DLLReferencePlugin 用某种方法实现了拆分 bundles,同时还大大提升了构建的速度。
DllPlugin 需要设置打包的配置文件,并先于项目打包将第三方组件打包;这个插件会创建一个只有dll的bundle,并且生成manifest.json的文件,这样来让 DllReferencePlugin 映射到相关的依赖上去。
DllReferencePlugin 是在 webpack 主配置文件中设置的, 这个插件把只有 dll 的 bundle(们)(dll-only-bundle(s)) 引用到需要的预编译的依赖。
dll应该只在生成环境使用,否则调试会有些问题。
配置webpack.dll.config.js
先配置webpack.dll.config.js 来打包生成公用JS
const path = require('path')
const webpack = require('webpack')
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
module.exports = {
mode: 'production',
devtool: false,
entry: {
react: [
'react',
'react-dom',
'react-router-dom',
'prop-types',
'react-fastclick',
'classnames',
],
common: [
'axios',
]
},
output: {
path: path.join(__dirname, '../dist'),
filename: 'lib/[name]_[hash:4].dll.js',
library: '[name]_[hash:4]'
},
performance: {
hints: false,
maxAssetSize: 300000, //单文件超过300k,命令行告警
maxEntrypointSize: 300000, //首次加载文件总和超过300k,命令行告警
},
optimization: {
minimizer: [
new UglifyJsPlugin({
parallel: true // 开启多线程并行
})
]
},
plugins: [
new webpack.DllPlugin({
context: __dirname,
path: path.join(__dirname, '../dist/lib', '[name]-manifest.json'),
name: '[name]_[hash:4]'
})
]
}
在package.json 中增加命令
"scripts": {
"dll": "webpack --config build/webpack.dll.config.js",
}
当我们运行npm run dll命令后,就会把配置的公共代码或第三方包,先打包出来,并生成manifest.json文件。
运行npm run dll 会生成如下代码
dist
├── lib
│ ├── common-manifest.json
│ ├── common_cf4c.dll.js
│ ├── react-manifest.json
│ └── react_cf4c.dll.js
配置 webpack.pro.config.js
dll 文件生成完后,就可以配置webpack.pro.config.js文件, 除了需要使用 DllReferencePlugin 插件来映射dll库外,我们还需要用到 html-webpack-include-assets-plugin 插件把公共JS库插入到HTML中。
webpack.pro.config.js的配置:
const HtmlWebpackIncludeAssetsPlugin = require('html-webpack-include-assets-plugin')
// ...
plugins: [
new webpack.DllReferencePlugin({
context: __dirname,
manifest: require('../dist/lib/react-manifest.json')
}),
new webpack.DllReferencePlugin({
context: __dirname,
manifest: require('../dist/lib/common-manifest.json')
}),
new HtmlWebpackIncludeAssetsPlugin({
assets: [{ path: 'lib', glob: '*.dll.js', globPath: 'dist/lib/' }],
append: false
})
]
package.json的配置
"build": "cross-env NODE_ENV=production node ./build/build.js",
在运行 npm run build 就可以打包线上包,因为提前把dll库打包好,后续再打包线上包的时候就节省了很多时间。