初級分析:使用 webpack 內置的 stats
stats: 構建的統計信息
package.json 中使用 stats
"script": {
"build:stats": "webpack --env production --json > stats.json"
},
Node.js 中使用
const webpack = require("webpack");
const config = require("./webpack.config.js")("production")
webpack(config, (err, stats) => {
if(err) {
return console.error(err);
}
if(stats.hasErrors()) {
return console.error(stats.toString("error-only"));
}
console.log(stats);
})

速度分析:使用 speed-measure-webpack-plugin
代碼示例
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
const smp = new SpeedMeasurePlugin();
const webpackConfig = smp.wrap({
plugins: [
new MyPlugin(),
new MyOtherPlugin()
]
})''
可以看到每個 loader 和插件執行耗時

速度分析插件作用
分析整個打包總耗時
每個插件和loader的耗時情況
webpack-bundle-analyzer 分析體積
代碼示例
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
plugins: [
new BundleAnalyzerPlugin()
]
}
構建完成后會在 8888 端口展示大小。
可以分析哪些問題?
依賴的第三方模塊文件大小
業務里面的組件代碼大小
使用高版本的 webpack 和 Node.js

使用 webpack4:優化原因
- V8 帶來的優化(for of 替代 forEach、Map 和 Set 替代 Object、includes 替代 indexOf)
- 默認使用更快的 md4 hash 算法
- webpacks AST 可以直接從 loader 傳遞給 AST,減少解析時間
- 使用字符串方法替代正則表達式

多進程/多實例構建:資源並行解析可選方案

多進程/多實例:使用 HappyPack 解析資源
原理:每次 webapck 解析一個模塊,HappyPack 會將它及它的依賴分配給 worker 線程中
代碼示例


多進程/多實例:使用 thread-loader 解析資源
原理:每次 webpack 解析一個模塊,thread- loader 會將它及它的依賴分配給 worker 線程中

多進程/多實例:並行壓縮
方法一:使用 parallel-uglify-plugin 插件

方法二:uglifyjs-webpack-plugin 開啟 parallel 參數

方法三:terser-webpack-plugin 開啟 parallel 參數

分包:設置 Externals
思路:將 react、react-dom 基礎包通過 cdn 引入,不打入 bundle 中
方法:使用 html-webpack-externals- plugin

進一步分包:預編譯資源模塊
思路:將 react、react-dom、redux、react-redux 基礎包和業務基礎包打包成一個文件
方法:使用 DLLPlugin 進行分包,DllReferencePlugin 對 manifest.json 引用
使用 DLLPlugin 進行分包

使用 DllReferencePlugin 引用 manifest.json
在 webpack.config.js 引入

緩存
目的:提升二次構建速度
緩存思路:
- babel-loader 開啟緩存
- terser-webpack-plugin 開啟緩存
- 使用 cache-loader 或者 hard-source-webpack-plugin
縮小構建目標
目的:盡可能的少構建模塊
比如 babel-loader 不解析 node_modules

減少文件搜索范圍
優化 resolve.modules 配置(減少模塊搜索層級)
優化 resolve.mainFields 配置
優化 resolve.extensions 配置
合理使用 alias

圖片壓縮
要求:基於 Node 庫的 imagemin 或者 tinypng API
使用:配置 image-webpack-loader

Imagemin的優點分析
-
有很多定制選項
-
可以引入更多第三方優化插件,例如pngquant
-
可以處理多種圖片格式
Imagemin的壓縮原理
pngquant: 是一款PNG壓縮器,通過將圖像轉換為具有alpha通道(通常比24/32位PNG 文件小60-80%)的更高效的8位PNG格式,可顯著減小文件大小。
pngcrush:其主要目的是通過嘗試不同的壓縮級別和PNG過濾方法來降低PNG IDAT數據 流的大小。
optipng:其設計靈感來自於pngcrush。optipng可將圖像文件重新壓縮為更小尺寸,而不 會丟失任何信息。
tinypng:也是將24位png文件轉化為更小有索引的8位圖片,同時所有非必要的metadata 也會被剝離掉
tree shaking(搖樹優化)復習
概念:1 個模塊可能有多個方法,只要其中的某個方法使用到了,則整個文件都會被打到 bundle 里面去,tree shaking 就是只把用到的方法打入 bundle ,沒用到的方法會在 uglify 階段被擦除掉。
使用:webpack 默認支持,在 .babelrc 里設置 modules: false 即可 · production mode的情況下默認開啟
要求:必須是 ES6 的語法,CJS 的方式不支持
無用的 CSS 如何刪除掉?
PurifyCSS: 遍歷代碼,識別已經用到的 CSS class
uncss: HTML 需要通過 jsdom 加載,所有的樣式通過PostCSS解析,通過 document.querySelector 來識別在 html 文件里面不存在的選擇器
在 webpack 中如何使用 PurifyCSS?
- 使用 purgecss-webpack-plugin
- 和 mini-css-extract-plugin 配合使用

構建體積優化:動態 Polyfill

babel-polyfill
打包后體積:88.49k,占比 29.6%
Promise 的瀏覽器支持情況
Can I user ____?

構建體積優化 :動態 Polyfill
| 方案 | 優點 | 缺點 | 是否采用 |
|---|---|---|---|
| babel-polyfill | react官方推薦 | 1. 包體積200K+,難以單獨抽離Map、Set 2. 項目里如果 react 單獨引用的 cdn,如果需要用它需要單獨構建一份放在 react 前面加載 |
否 |
| babel-plugin-transform-runtime | 體積小 | 不能在 polyfill 原型上方法。不適用於業務復雜的項目開發環境 | 否 |
| 自己的 Map、Set 的 polyfill | 定制化高、體積小 | 1.重復造輪子 2.自己填坑 3.體積小,但是所有都要加載 |
否 |
| polyfill-service | 只給用戶需要的 polyfill,社區維護 | 部分國內奇葩瀏覽器可能無法識別(但是可以降級返回所需的全部 polyfill) | 是 |
Polyfill Service原理
識別 User Agent,下發不同的 Polyfill

構建體積優化:如何使用動態 Polyfill service
polyfill.io 官方提供的服務
<script src="https://cdn.polyfill.io/v2/polyfill.min.js"></script>
基於官方自建的 polyfill 服務
自己去找了
體積優化策略總結
-
Scope Hoisting
-
Tree-shaking
-
公共資源分離
-
圖片壓縮
-
動態 Polyfill
