基于 Webpack 的應用包體尺寸優化

最近我在構建一個基于 React 的單頁應用,當我用 Google TestMySite 來檢測自己的站點時,它的反饋是加載時間過長,因此我開始考慮如何優化初次下載的包體大小。優化應用包體的第一步就是檢視當前的包體組成,判斷其中哪些依賴時必須的,我們在 Webpack 的回顯中可以看到當前的包體大小:
$ webpack -p --progress
Hash: dbce3735c9520e2dc682
Version: webpack 1.14.0
Time: 54264ms
Asset Size Chunks Chunk Names
dist/index.js 3.29 MB 0 [emitted] main
dist/index.js.map 13.7 MB 0 [emitted] main
[0] multi main 40 bytes {0} [built]
+ 1374 hidden modules
分析包體依賴
這里我們使用 webpack-bundle-analyzer 來分析 Webpack 生成的包體組成并且以可視化的方式反饋給開發者。我們可以使用npm來安裝該插件:
$ npm install --save-dev webpack-bundle-analyzer
然后我們需要修改webpack.config.js來引入該插件:
var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
// ...
plugins: [new BundleAnalyzerPlugin()]
// ...
然后我們照常使用 Webpack 編譯之后,可視化的結果會被展示在 http:// localhost:8888/ ,你大概可以看到如下的交互界面:
這個交互插件應該能幫你分析哪些依賴占據了主要的包體大小,這也提醒我們在引入某個功能的時候,應該只引入需要的模塊,以 Lodash 為例:
//1. Import the entire lodash library and add it to the bundle
import lodash from 'lodash'
lodash.groupBy(rows, 'id')
//2. Import only the required function from lodash
import groupBy from 'lodash/groupBy'
groupBy(rows, 'id')
設置合適的 Node 環境變量
設置合適的環境變量能夠幫助 Webpack 更好地去壓縮處理依賴中的代碼,我們需要在生產環境中將NODE_ENV設置為production:
plugins: [...
new webpack.DefinePlugin({
'process.env.NODE_ENV': '"production"'
}),
..]
使用最小化的 SourceMap
當我們在生產環境下組合壓縮 JavaScript 文件時,Webpack 會為我們生成某個 SourceMap 文件來映射源文件的內容。在開發環境中我們經常會將devtool設置為eval,這樣會將大量的代碼信息打包到輸出包體中從而提升編譯速度。而開發環境中我們可以將該項設置為eval-source-map或者cheap-module-source-map,我們比較切換前后的包體大小可以發現縮小了將近 1MB 的內容:
$ webpack -p --progress
Hash: 68a52fddbcc2898a5899
Version: webpack 1.14.0
Time: 29757ms
Asset Size Chunks Chunk Names
dist/index.js 1.71 MB 0 [emitted] main
dist/index.js.map 464 bytes 0 [emitted] main
[0] multi main 40 bytes {0} [built]
+ 1365 hidden modules
其他常用插件
這里我列舉幾個常用的能夠用于減少包體大小的插件,我們可以根據項目需求選擇性的使用:
-
compression-webpack-plugin :該插件能夠將資源文件壓縮為.gz文件,并且根據客戶端的需求按需加載。
-
dedupeplugin :抽取出輸出包體中的相同或者近似的文件或者代碼,可能對于 Entry Chunk 有所負擔,不過能有效地減少包體大小。
-
uglifyjsplugin :壓縮輸出塊的大小,可以參考官方文檔。
-
ignoreplugin :用于忽略引入模塊中并不需要的內容,譬如當我們引入moment.js時,我們并不需要引入該庫中所有的區域設置,因此可以利用該插件忽略不必要的代碼。
...
var CompressionPlugin = require("compression-webpack-plugin");
...
let config = {
entry: path.join(__dirname, '../app/index'),
cache: false,
devtool: 'cheap-module-source-map',
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': '"production"'
}),
new webpack.optimize.DedupePlugin(),
new webpack.optimize.UglifyJsPlugin({
mangle: true,
compress: {
warnings: false, // Suppress uglification warnings
pure_getters: true,
unsafe: true,
unsafe_comps: true,
screw_ie8: true
},
output: {
comments: false,
},
exclude: [/\.min\.js$/gi] // skip pre-minified libs
}),
new webpack.IgnorePlugin(/^\.\/locale$/, [/moment$/]),
new webpack.NoErrorsPlugin(),
new CompressionPlugin({
asset: "[path].gz[query]",
algorithm: "gzip",
test: /\.js$|\.css$|\.html$/,
threshold: 10240,
minRatio: 0
})
...
],
引入該插件后包體的體積會受到進一步的壓縮:
$ webpack -p --progress
Hash: 68a52fddbcc2898a5899
Version: webpack 1.14.0
Time: 29757ms
Asset Size Chunks Chunk Names
dist/index.js 1.54 MB 0 [emitted] main
dist/index.js.gz 390 KB 0 [emitted] main
dist/index.js.map 464 bytes 0 [emitted] main
[0] multi main 40 bytes {0} [built]
+ 1365 hidden modules
來自:https://zhuanlan.zhihu.com/p/24928279