Webpack 效能優化 (一)(使用別名做重定向)
前言
Webpack 是 OneAPM 前端技術棧中很重要的一部分,它非常好用,如果你還不瞭解它,建議你閱讀這篇 Webpack 入門指迷 ,在 OneAPM 我們用它完成靜態資源打包,ES6 程式碼的轉換 ,React 元件的組織等,在接下來的日子裡,我們將通過一系列文章和業界分享我們在使用 Webpack 過程中關於效能方面的經驗。
作為系列文章的第一篇,我們會重點介紹 Webpack 中的 resolve.alias
,也就是請求重定向。不過請注意 Webpack 裡的請求是對模組的依賴,也就是一個 require
語句,而不是一個 HTTP 請求。
必要的準備
- 需要你有一定的 Node.js 基礎
- 電腦上裝有最新版的 Webpack (npm install webpack -g)
- 瞭解 Webpack 配置檔案的格式
例子:本地時鐘
要實現的功能很簡單,就是在頁面上用中文顯示當前時間,需要用到 moment 這個庫,這個庫封裝了很多和日期相關的函式,而且自帶了國際化的支援。
新建一個 Node.js 專案
使用 npm init
初始化你的專案,然後通過npm install moment -D
加上 moment
的開發者依賴。
新建一個entry.js
作為入口檔案,當然你也可以用 app.js
這樣的名字,只是大部分的 Webpack 示例都是用的是 entry.js
。
var moment = require('moment');
document.write(moment().locale('zh-cn').format('LLLL'));
新建一個頁面index.html
, 引用 bundle.js
:
<body>
<h5>當前時間:</h5>
<script src="dist/bundle.js"></script>
</body>
此時的檔案目錄看起來是這樣的:
index.html
package.json
entry.js
node_modules/moment
到目前為止 bundle.js
這個檔案還不存在,不過彆著急,接下來的工作就交給 Webpack 來完成。
index.html ------------------------+
package.json |
+--> <Clock App>
entry.js --------+ |
+-->bundle.js+--+
node_modules/moment-+
如圖,Webpack 會把 entry.js
和 moment
模組一起打包成一個 bundle.js
檔案,和 index.html
一起構成了我們的 Clock App。怎麼樣,是不是已經聽到 Clock App 滴答作響了?
使用 webpack 打包程式碼
在命令列執行:
webpack --entry ./entry.js --output-path dist --output-file bundle.js
你會看到類似下面的輸出結果:
Hash: bf9007fb1e0cb30e3ef7
Version: webpack 1.10.0
Time: 650ms
Asset Size Chunks Chunk Names
bundle.js 378 kB 0 [emitted] null
[0] ./entry.js 125 bytes {0} [built]
+ 86 hidden modules
可以看到,耗時 650ms,這麼慢著實讓人意外,一定要想辦法提高“新一代神器”速度;另一方面,最後一行的 + 86 hidden modules 非常讓人懷疑:明明是一個簡單的 Clock App,怎麼會有這麼多的依賴。
如何快速定位 Webpack 速度慢的原因
再一次,在命令列輸入:
webpack --entry ./entry.js --output-path dist --output-file bundle.js \
--colors \
--profile \
--display-modules
不過這次新增加了三個引數,這三個引數的含義分別是:
--colors
輸出結果帶彩色,比如:會用紅色顯示耗時較長的步驟--profile
輸出效能資料,可以看到每一步的耗時--display-modules
預設情況下node_modules
下的模組會被隱藏,加上這個引數可以顯示這些被隱藏的模組 這次命令列的結果已經很有參考價值,可以幫助我們定位耗時比較長的步驟Hash: bf9007fb1e0cb30e3ef7 Version: webpack 1.10.0 Time: 650ms Asset Size Chunks Chunk Names bundle.js 378 kB 0 [emitted] null [0] ./entry.js 125 bytes {0} [built] factory:11ms building:8ms = 19ms [1] ../~/moment/moment.js 102 kB {0} [built] [0] 19ms -> factory:7ms building:141ms = 167ms [2] (webpack)/buildin/module.js 251 bytes {0} [built] [0] 19ms -> [1] 148ms -> factory:132ms building:159ms = 458ms [3] ../~/moment/locale ^\.\/.*$ 2.01 kB {0} [optional] [built] [0] 19ms -> [1] 148ms -> factory:6ms building:10ms dependencies:113ms = 296ms [4] ../~/moment/locale/af.js 2.57 kB {0} [optional] [built] [0] 19ms -> [1] 148ms -> [3] 16ms -> factory:52ms building:65ms dependencies:138ms = 438ms ..... 廣告分割線,Node.js 工程師簡歷請發 nodejs@oneapm.com ...... [85] ../~/moment/locale/zh-cn.js 4.31 kB {0} [optional] [built] [0] 22ms -> [1] 162ms -> [3] 18ms -> factory:125ms building:145ms dependencies:22ms = 494ms [86] ../~/moment/locale/zh-tw.js 3.07 kB {0} [optional] [built] [0] 22ms -> [1] 162ms -> [3] 18ms -> factory:126ms building:146ms dependencies:21ms = 495ms
從命令列的結果裡可以看到從 Request[4] 到 Request[86] 都是在解析 moment.js
附帶的大量本地化檔案。所以我們遇到的速度慢的問題其實是由 moment
引起的。
如果你想知道為什麼 Webpack 會載入這麼多的模組,可以參考這篇文章 Why Enormous Locales During Webpack MomentJS
我們再來看看 entry.js
程式碼的第一行,標準的 CommonJS
寫法:
var moment = require('moment');
也就是說,請求的是 moment
的原始碼。實際上,通過 NPM 安裝moment
的時候會同時安裝 moment
的原始碼和壓縮後的程式碼,試驗證明下面這種寫法也是可行的:
var moment = require('moment/min/moment-with-locales.min.js');
只不過這樣改,可讀性會有所下降,而且每一個用到moment
的地方都得這麼寫。另外,如果同樣的問題出現在第三方模組中,修改別人程式碼就不那麼方便了。下面來看看用 Webpack 怎麼解決這個問題。
在 Webpack 中使用別名
別名(resolve.alias
) 是 Webpack 的一個配置項,它的作用是把使用者的一個請求重定向到另一個路徑,例如通過修改 webpack.config.js
配置檔案,加入:
resolve: {
alias: {
moment: "moment/min/moment-with-locales.min.js"
}
}
這樣待打包的指令碼中的 require('moment')
; 其實就等價於 require('moment/min/moment-with-locales.min.js');
。通過別名的使用在本例中可以減少幾乎一半的時間。
Hash: cdea65709b783ee0741a
Version: webpack 1.10.0
Time: 320ms
Asset Size Chunks Chunk Names
bundle.js 148 kB 0 [emitted] main
[0] ./entry.js 125 bytes {0} [built]
factory:11ms building:9ms = 20ms
[1] ../~/moment/min/moment-with-locales.min.js 146 kB {0} [built] [1 warning]
[0] 20ms -> factory:8ms building:263ms = 291ms
[2] (webpack)/buildin/module.js 251 bytes {0} [built]
[0] 20ms -> [1] 271ms -> factory:3ms building:1ms = 295ms
WARNING in ../~/moment/min/moment-with-locales.min.js
Module not found: Error: Cannot resolve 'file' or 'directory' ./locale in */webpack_performance/node_modules/moment/min
@ ../~/moment/min/moment-with-locales.min.js 1:2731-2753
在 Webpack
中忽略對已知檔案的解析
module.noParse
是 webpack
的另一個很有用的配置項,如果你 確定一個模組中沒有其它新的依賴 就可以配置這項,webpack
將不再掃描這個檔案中的依賴。
module: {
noParse: [/moment-with-locales/]
}
這樣修改,再結合前面重新命名的例子,更新後的流程是:
webpack
檢查到entry.js
檔案對moment
的請求;- 請求被
alias
重定向,轉而請求moment/min/moment-with-locales.min.js;
noParse
規則中的/moment-with-locales/
一條生效,所以webpack
就直接把依賴打包進了bundle.js
。Hash: 907880ed7638b4ed70b9 Version: webpack 1.10.0 Time: 76ms Asset Size Chunks Chunk Names bundle.js 147 kB 0 [emitted] main [0] ./entry.js 125 bytes {0} [built] factory:13ms building:13ms = 26ms [1] ../~/moment/min/moment-with-locales.min.js 146 kB {0} [built] [0] 26ms -> factory:13ms building:5ms = 44ms
時間進一步被壓縮,只需要 76ms,比前一步還減少了 75%。
在 Webpack 中使用公用 CDN
Webpack 是如此的強大,用其打包的指令碼可以執行在多種環境下,Web 環境只是其預設的一種,也是最常用的一種。考慮到 Web 上有很多的公用 CDN 服務,那麼 怎麼將 Webpack 和公用的 CDN 結合使用呢?方法是使用 externals
宣告一個外部依賴。
externals: {
moment: true
}
當然了 HTML 程式碼裡需要加上一行
<script src="//apps.bdimg.com/libs/moment/2.8.3/moment-with-locales.min.js"></script>
這次打包,結果只用了 49 ms,幾乎達到了極限。
總結
本文結合本地時鐘的例子,展示了定位 Webpack 效能問題的步驟,以及所需要的兩個引數 :--display-modules
和 --profile
。然後,重點介紹了 resolve.alias
即利用別名做重定向的方法和場景,在此基礎上,配合module.noParse
忽略某些模組的解析可以進一步加快速度。最後介紹了用 externals
定義外部依賴方法來使用公用 CDN。
關於
本文相關的原始碼在: https://github.com/wyvernnot/webpack_performance/tree/master/moment-example;
本文系OneAPM工程師原創文章。OneAPM是中國基礎軟體領域的新興領軍企業,能幫助企業使用者和開發者輕鬆實現:緩慢的程式程式碼和SQL語句的實時抓取。想閱讀更多技術文章,請訪問OneAPM官方技術部落格。
相關文章
- 使用Webpack4優化Web效能Web優化
- webpack效能優化(下)Web優化
- webpack效能優化(上)Web優化
- React+Webpack效能優化ReactWeb優化
- webpack打包效能優化之路Web優化
- vue + webpack 前端效能優化VueWeb前端優化
- 使用Android Profile做效能分析及優化Android優化
- webpack構建和效能優化探索Web優化
- webpack4.x 效能優化Web優化
- 面試題:webpack之效能優化面試題Web優化
- 淺談webpack4.0 效能優化Web優化
- webpack效能優化不完全指北Web優化
- 【譯】Google – 使用 webpack 進行 web 效能優化(一):減小前端資源大小GoWeb優化前端
- 【譯】Google - 使用 webpack 進行 web 效能優化(一):減小前端資源大小GoWeb優化前端
- 三十分鐘掌握Webpack效能優化Web優化
- React Native如何做效能優化React Native優化
- 【譯】Google - 使用 webpack 進行 web 效能優化(二):利用好持久化快取GoWeb優化持久化快取
- 【譯】Web 效能優化: 使用 Webpack 分離資料的正確方法Web優化
- vue-router 重定向和別名 理解Vue
- spark效能優化(一)Spark優化
- Mysql效能優化一MySql優化
- Oracle效能優化-SQL優化(案例一)Oracle優化SQL
- webpack--效能優化之打包構建速度和程式碼除錯優化Web優化除錯
- 面試官: 說一下你做過哪些效能優化?面試優化
- 一些webpack配置優化手段Web優化
- 【譯】Google - 使用 webpack 進行 web 效能優化(三):監控和分析應用GoWeb優化
- webpack 打包優化Web優化
- webpack系列-優化Web優化
- Webpack打包優化Web優化
- 【面經】面試官:做過效能優化的工作嗎?你會從哪些方面入手做效能優化呢?面試優化
- 【前端效能優化】vue效能優化前端優化Vue
- 高效能MySQL (一):Schema與資料型別優化MySql資料型別優化
- 使用shouldComponentUpdate進行效能優化優化
- 效能優化小冊 - 分類構建:利用好 webpack hash優化Web
- webpack優化的一些基本方法Web優化
- 記一次 Webpack 專案優化Web優化
- Android效能優化筆記(一)——啟動優化Android優化筆記
- Webpack 中css 如何 import 使用 alias別名 相對路徑WebCSSImport
- webpack常用優化配置Web優化