本文首发于 dawei.lv
本文基于 webpack 4.8.1
吐槽
webpack 彪版本号的速度真是飞快,4.0 发布没多久上去看的时候才 4.1.*,现在已经刷到 4.8.1 了,给人一种“我版本号很高了,可以安心升级了”的感觉,然而坑依然很多...尤其是 API 文档,到处可见 3.0 的陈旧信息。Code Splitting 章节点进去依然在讲 CommonsChunkPlugin ,CommonsChunkPlugin 点进去提示去看 SplitChunksPlugin,看文档的时候经常会迷失自我,心累...好了,吐槽完毕,下面是正文。需要直接复制粘贴的同学直接拉到最后~
4.0 与 3.0 的区别
mode
webpack4.0 新增了 mode
的概念, mode
可以为 development
、production
和 none
。
development
帮我们设置了 process.env.NODE_ENV=development
,并添加了 NamedModulesPlugin
插件。process.env.NODE_ENV=development
可以用来显示一些在开发模式下才显示的 debug 信息,请注意这个 NODE_ENV
不能在 webpack.config.js
中使用,只能在你的源文件中使用。想要在 webpack.config.js
中也生效,需要在 package.json 的 script 脚本前添加 NODE_ENV=development
,如 NODE_ENV=development webpack --config webpack.dev.js
。NamedModulesPlugin
是在开启 HMR 的时候使用的插件。
// webpack.development.config.js
module.exports = {
+ mode: 'development'
- plugins: [
- new webpack.NamedModulesPlugin(),
- new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("development") }),
- ]
}
复制代码
production
帮我们设置了 process.env.NODE_ENV=production
,并添加了 UglifyJsPlugin
、ModuleConcatenationPlugin
、NoEmitOnErrorsPlugin
等插件,在设置了 sideEffects=false
之后可以实现未引用代码删除的功能。
// webpack.production.config.js
module.exports = {
+ mode: 'production',
- plugins: [
- new UglifyJsPlugin(/* ... */),
- new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("production") }),
- new webpack.optimize.ModuleConcatenationPlugin(),
- new webpack.NoEmitOnErrorsPlugin()
- ]
}
复制代码
optimization
另一个区别在于引入了 optimization
的概念,optimization.minimizer
和 optimization.splitChunks
是需要我们关注的两个配置。
optimization.minimizer
用于指定 webpack 使用哪个代码压缩插件,默认为 new webpack.optimize.UglifyJsPlugin
,推荐更换为 UglifyJSPlugin
。
+const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
module.exports = {
//...
optimization: {
minimizer: [
- new webpack.optimize.UglifyJsPlugin({})
+ new UglifyJSPlugin({})
]
}
}
复制代码
optimization.splitChunks
替代了 3.0 的 CommonsChunkPlugin,实现公共代码抽取。具体 API 参见SplitChunksPlugin。划几个重点,webpack 生成已经给大部分用户提供了默认的设置,mode=production
就已经带了这个优化,BUT!! 默认开启的代码分割只对异步加载的代码有效,也就是如果你是多个入口的配置,那么你的 react、react-dom、react-router 等公共库以及你的 common 代码都会被重复打包进多个入口里。emmmmm,这叫什么开箱即用嘛,还是我们自己动手吧。
首先,optimization.splitChunks.chunks
设置为 all
,使得 async
异步加载的代码和 initial
初始化的代码都会被抽取。optimization.splitChunks.cacheGroups
添加 commons
和 vendors
(如下)。
module.exports = {
optimization: {
splitChunks: {
chunks: "all",
cacheGroups: {
commons: {
name: "commons",
test: /src[\/]/,
chunks: "initial",
priority: 2,
minChunks: 2
},
vendors: {
name: "vendors",
test: /node_modules/,
chunks: "initial",
priority: 10,
minChunks: 2
}
}
}
}
};
复制代码
这里的 name
指定你要抽取出来的 js 的文件名,test
字段用来筛选你要匹配的代码,minChunks:2
表示代码被引用 2 次及以上就会被抽取出来,commons
实现抽取你的 src
文件夹下的公共代码,vendors
则用于抽取 node_modules 下的公共库。下面我们需要把我们抽取出来的 commons.js
和 vendors.js
添加到 HtmlWebpackPlugin ,以实现打包出来的 html 文件引用 commons.js
和 vendors.js
。
new HtmlWebpackPlugin({
//...
- chunks: 'index',
+ chunks: ['vendors', 'commons', 'index'],
})
复制代码
到笔者发布文章为止,HtmlWebpackPlugin 还不支持添加动态名称的 cacheGroups,无法将未明确指定 name 的 vendors~chunk-a~chunk-b.js 之类的 js 打包进代码中,不过可以看到相关的代码已经快要出来了。之后就可以实现更精细的代码分割打包了。
plugins
使用 mini-css-extract-plugin 替代 extract-text-webpack-plugin
抽取 css 到单独文件中,使用 optimize-css-assets-webpack-plugin 对 css 进行压缩处理。
optimize-css-assets-webpack-plugin
在使用的时候强烈推荐设置 isSafe = true
,避免 z-index 被修改的问题。
new OptimizeCSSAssetsPlugin({
cssProcessorOptions: {
isSafe: true
}
});
复制代码
最后
webpack4-demo 是笔者整理的 webpack4.0 demo,详细的 webpack 配置可以在这里找到。
- 支持开发/生产模式
- 支持开发模式下 HMR
- 支持代码分割、代码混淆压缩
- 支持未引用代码删除
- 支持 less、autoprefixer
- 支持单/多入口
- 支持查看打包各个模块占用大小