本文首發於 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
- 支援單/多入口
- 支援檢視打包各個模組佔用大小