極致編譯速度,一文搞定webpack5升級

大家好,我是若川。我持續組織了近一年的源碼共讀活動,感興趣的可以?點此掃碼加我微信?lxchuan12?參與,每周大家一起學習200行左右的源碼,共同進步。同時極力推薦訂閱我寫的《學習源碼整體架構系列》?包含20余篇源碼文章。歷史面試系列。另外:目前建有江西|湖南|湖北籍前端群,可加我微信進群。


在嘗試升級 webpack5 之前,建議大家盡量先把官方文檔[1]通讀一遍,可以少走很多彎路,本文是在結合具體業務場景后,對官方文檔的歸納和補充。

背景

music-musician-web-node 是音樂人場景的核心應用,覆蓋的頁面多,代碼量大,在使用 webpack4 的情況下,本地開發的編譯效率一直不是很好,以筆者使用的 MacBook Pro M1 為例,音樂人 + 版權總共 45 個頁面,啟動一次全量構建需要花費 140s 左右 (已經加了諸如 happypack 之類的并行打包策略)2dd6c0aa991126dec843470f4f4e65bd.png

增量構建一般是 4-5s,但是一旦修改引用次數比較多的比如公用的 ts 定義,則增量編譯時間一度能達到 80s 以上1f29bc254d9e81a945af02110c6fb0b8.png

因此,隨著業務需求的增多,為了提升需求交付效率,解決開發體驗的問題,就變得非常急迫。在解決開發體驗方面,升級 webpack 是最快捷且直接能帶來巨大收益的手段。

webpack5 帶來的提升

webpack4 到 webpack5 畢竟有很多的 break change,為了讓大家能有升級的熱情,那么還是必須先展示升級帶來的提升。我們就以創作者中心的數據來說明。在上面的背景里面我們已經講了原先開發時 webpack 啟動和增量構建的時間分別是140s和平均4-5s,這里我們直接列出升級后的成果

啟動時間降低

webpack 啟動時間縮短到55s左右,減少了近60%e0cdd2d2b21fdfa9a56010002d767ec9.png

增量時間降低

增量構建時間非常夸張,直接到了1s左右,即使是原先可能會導致構建時間達到 80s 以上的 ts 定義修改,也被降低到3s左右,平均減少80% 以上d003d6805ccc971c75b96e120683b3f8.png

生產包構建時間

之前在本地大約是 240s 左右,升級后為 200s。061b45979fedd2c18c5ea0f68693946c.png注意:一定要升級 terser-webpack-plugin 到最新版,否則生產包構建時間相較于 webpack4 可能還會降低。

包體積變化

因為創作者中心的項目頁面非常多,不太好比較 webpack4 和 webpack5 打包結果體積的變化,但是從官方優化策略(這個優化策略還導致了很多 break change,這個在下文會細講)和很多實踐結果看,對包體積都是有優化的。

升級前置準備

從這里開始,我們就正式著手 webpack 的升級。在正式升級前,需要先做一些前置工作,這些工作可以幫我們規避之后升級過程中一些奇怪的問題。

  • 將 webpack 升級到 v4 的最新版本:如果你當前使用的版本就是 v4,升級應該是無痛的,但如果 webpack 是 v3 甚至更早的版本,請參考官方文檔[2]先升級到 v4

  • 將 webpack-cli 升級到最新版

  • 將使用的 loader 和 plugin 都升級到兼容 v4 的最新版本:這里要留意每個 plugin 和 Loader 對于 webpack 版本的要求,因為有些 loader/plugin 的最新版是只兼容 webpack5 的,我們這里還暫時不需要升級到這一步

因為每個項目所使用的 loader 和 plugin 有一些差異,因此這里實在不方便列出所有對應的版本,只能以筆者自身的項目為例,把常見的 plugin 和 loader webpack4 對應的最新版本列出來,其他的請根據自己項目的情況,到 npm 或者 github 查找 readme 或者翻閱 release 記錄,幾乎所有符合標準的工具庫都會給出

  • terser-webpack-plugin: webpack4 請升級到 4.x, webpack5 升級到最新

  • babel-loader: 無論 v4 或者 v5, 升級到 7.x 或者 8.x 都可以

  • extract-text-webpack-plugin: v4 請升級到最新版,v5 之后用 mini-css-extract-plugin 進行替換

  • optimize-css-assets-webpack-plugin: v4 升級到最新版,v5 用 css-minimizer-webpack-plugin 替換

  • ts-loader: v4 升級到 8.x,v5 升級最新版

  • less-loader: 7.x 為兼容 webapck4 的最后版本

  • sass-loader: 10.x 為兼容 webpack4 的最后版本

  • css-loader: 5.x 為兼容 webpack4 的最后版本

  • postcss-loader: 4.x 為兼容 webpack4 的最后版本

  • ...

修復 warning 和 errors

做完上述的前置準備以后,不出意外,運行編譯流程,會有一些報警甚至是錯誤,請修復這些問題。

更改 v4 版本過時的寫法

v4 版本就在告警的過時寫法在 v5 版本一般都會直接拋棄,所以我們需要對照官方給出的指引,進行更改,否則升級 webpack5 后 webpack 是無法啟動的

  • optimization.hashedModuleIds: true → optimization.moduleIds: 'hashed'

  • optimization.namedChunks: true → optimization.chunkIds: 'named'

  • optimization.namedModules: true → optimization.moduleIds: 'named'

  • NamedModulesPlugin → optimization.moduleIds: 'named'

  • NamedChunksPlugin → optimization.chunkIds: 'named'

  • HashedModuleIdsPlugin → optimization.moduleIds: 'hashed'

  • optimization.noEmitOnErrors: false → optimization.emitOnErrors: true

  • optimization.occurrenceOrder: true → optimization: { chunkIds: 'total-size', moduleIds: 'size' }

  • optimization.splitChunks.cacheGroups.vendors → optimization.splitChunks.cacheGroups.defaultVendors

  • optimization.splitChunks.cacheGroups.test(module, chunks) → optimization.splitChunks.cacheGroups.test(module, { chunkGraph, moduleGraph })

  • Compilation.entries → Compilation.entryDependencies

  • serve → serve is removed in favor of DevServer

  • Rule.query (deprecated since v3) → Rule.options/UseEntry.options

測試 webpack5 的兼容性

這一步非常重要,webpack5 相比于 webpack4 一個非常顯著的差異,就是 webpack5 為了優化 bundle size,不再默認支持 node polyfill,所以一旦一些 node 與 browser 環境公用的包內部使用了 node 的全局變量,則會引發非常嚴重的 runtime 階段的報錯,為了盡量提前發現問題,我們需要在去掉 node 全局變量的情況下先測試下代碼的運行情況 注意:這里的寫法僅用于 webpack4 測試兼容性階段,在升級 webpack5 之后記得去掉

module.exports?=?{//?...node:?{Buffer:?false,process:?false,},
};

在補充完上述代碼后,先運行自己代碼看看,是否會遇到頁面報錯,注意,這些報錯是 runtime 階段的,并不是編譯階段就會出現的,所以得實際運行頁面才能看出來。如果你的代碼直接或者間接依賴了 node 的 process 和 Buffer 變量,那么你肯定會遇到下面的報錯

ReferenceError:?Buffer?is?not?defined

或者

ReferenceError:?process?is?not?defined

在實際的業務場景中,我們一個工程往往有非常多的頁面,且每個頁面狀態繁多,無法簡單的看出是否有 runtime 報錯,所以我建議讀者直接就當做代碼中確實就是有 node 全局變量的引用,然后在升級 webpack5 的時候手動進行 polyfill,這樣可以將問題概率降到最低,具體怎么做 polyfill,接下來就會講到。

開始 webpack5 升級

在上述前置準備完成后,我們可以開始進行正式的升級了。

安裝最新 webpack 版本

nenpm install -D webpack@latest

更改配置

  • 移除optimization.moduleIdsoptimization.chunkIds的配置,webpack5 優化了 chunkId 和 moduleId 的默認生成策略,直接用默認的會更高效

  • 使用[hash]占位符的地方都可以換成[contenthash],后者會更高效

  • IgnorePlugin在入參為 regexp 的時候,寫法改成new IgnorePlugin({ resourceRegExp: /regExp/ })

  • 對于 webpack4 中如node.fs: 'empty'的寫法,在 webpack5 中,node 屬性只支持三個字段,global,__dirname,__filename,參考[3],所以除這三個屬性外的 node 屬性,需要放到resolve.fallback中,因此node.fs: 'empty'需要改為resolve.fallback.fs: false

  • url-loader,raw-loader,file-loader建議直接用Assets Module[4]替換,雖然不換也暫時不影響,但是在后面的版本,這三個 loader 可能被移除

針對optimization.splitChunks,在 v5 版本盡量參考下列配置

  • splitChunks 推薦使用默認配置,或者直接optimization.splitChunks: { chunks: 'all' }

  • 如果原先有這種寫法optimization.splitChunks.cacheGroups: { default: false, vendors: false },需要改成optimization.splitChunks.cacheGroups: { default: false, defaultVendors: false }

需要更正或者省略的寫法

/* webpackChunkName: '...' */

之前的代碼邏輯中,我們會使用這種注釋寫法給 code split 的 chunk 命名,但是在 v5 版本,可以不用這么做,當 mode 為 development 的時候,webpack 會自動以文件名來命名 chunk

引用 Json 的變化

原先的這種寫法

import { version } from './package.json';
console.log(version);

需要改成

import pkg from './package.json';
console.log(pkg.version);

處理升級過程中的錯誤

一般來說,在升級過程中,會遇到三類問題

schema 錯誤

webpack 的配置語法錯誤,這種錯誤很好解決,在報錯信息中 webpack 會詳細標明錯誤原因和改進方法,按照提示進行修復即可。

編譯時報錯

這類問題也不難解決,最常見的就是因為 webpack5 升級后不再做 node 的 polyfill,會報module not found,這里列出官方給出的 webpack4 默認的 polyfill,大家根據業務場景的報錯,自行下載對應的 npm 模塊,然后添加配置即可(不需要全部加上,根據報錯內容按需添加)

module.exports?=?{//...resolve:?{fallback:?{assert:?require.resolve('assert'),buffer:?require.resolve('buffer'),console:?require.resolve('console-browserify'),constants:?require.resolve('constants-browserify'),crypto:?require.resolve('crypto-browserify'),domain:?require.resolve('domain-browser'),events:?require.resolve('events'),http:?require.resolve('stream-http'),https:?require.resolve('https-browserify'),os:?require.resolve('os-browserify/browser'),path:?require.resolve('path-browserify'),punycode:?require.resolve('punycode'),process:?require.resolve('process/browser'),querystring:?require.resolve('querystring-es3'),stream:?require.resolve('stream-browserify'),string_decoder:?require.resolve('string_decoder'),sys:?require.resolve('util'),timers:?require.resolve('timers-browserify'),tty:?require.resolve('tty-browserify'),url:?require.resolve('url'),util:?require.resolve('util'),vm:?require.resolve('vm-browserify'),zlib:?require.resolve('browserify-zlib'),},},
};

還有一種可能的情況是之前能引用的 npm 包,在升級以后引用不到了,需要變更寫法,這種比較少見,但是創作者中心這里遇到過。應用依賴的二方包依賴了 uuid 這個庫,之前的寫法是

import?uuidv4?from?'uuid/dist/v4';//?...

然而升級之后,這種寫法在編譯時就報錯了,需要改成

import?{?v4?as?uuidv4?}?from?'uuid';

這種錯誤就很難預料,但好在是編譯時的報錯,還是有跡可循的,遇到后再 google 其實問題也不大。

runtime 階段報錯

以創作者中心升級的經驗來看,會有兩種引發 runtime 階段報錯的原因

node 全局變量 polyfill

最常見的也是 node polyfill 的丟失,上面也提到過,node 與 browser 公用的一些包中會使用一些 node 的全局變量 (process,Buffer),這些變量是直接使用的,并沒有 require,因此在編譯階段不會暴露。為了解決這類問題,需要這么配置

{
plugins:?[new?webpack.ProvidePlugin({Buffer:?['buffer',?'Buffer'],process:?'process/browser',}),],
}

使用 ProvidePlugin 將變量注入到全局,這里注意要下載對應的 buffer 和 process/browser 的包,這里我推薦大家都將 buffer 和 process 的 polyfill 加上,以防萬一

import 后變量丟失

這個也是升級過程中實際遇到過的。部分 npm 包導出的寫法并不是特別規范,在 webpack4 的版本會自動被兼容掉,但是 webpack5 因為默認支持 tree shaking,對于 npm 包的導出寫法會更嚴格,因此存在一種情況,即

import?watermark?from?'watermark-dom'

這種寫法在升級后,引用到的變量是 undefined,同時也沒有警告和編譯錯誤 需要改成

import?{?watermark?}?from?'watermark-dom';

這種 runtime 階段的報錯處理起來就非常棘手,關鍵在于難以預料,無法防范,上面只是列舉了創作者中心升級過程中的 runtime 報錯,不同的業務場景可能還會遇到其他的報錯,所以千萬不要有僥幸心理,唯一穩妥的辦法是將升級的影響范圍告知 qa,讓 qa 做一次完整回歸。

部分 loader 的優化

去掉 cache 相關的 loader 和 plugin

webpack5 內部對于 cache 的優化和利用已經非常好了,不需要再使用 webpack4 階段用來優化緩存的 loader 和 plugin 來提升性能,只會引發未知的問題,建議全部去掉。

用 thread-loader 替換 happypack

happypack 的作者本人已經不再維護該模塊,而 thread-loader 是官方推出的多線程編譯優化方案,性能據說要好不少,推薦在升級完成后進行替換。

使用 css-minimizer-webpack-plugin 替換 optimize-css-assets-webpack-plugin

該 loader 用于 css 代碼壓縮,官方給的建議是升級后進行替換7f271cd0b14731f63ef5099371b2d563.png

用 @babel/preset-typescript 替換 ts-loader

推薦嘗試,不僅僅在配置上簡化很多,一套 babel 配置吃遍所有 js,同時在性能方面,個人體驗后感覺確實會快不少。不過 @babel/preset-typescript 因為沒有再走 tsc,所以編譯時不會再像 ts-loader 那樣將編譯錯誤暴露出來,這里需要單獨裝一個插件 fork-ts-checker-webpack-plugin 來進行編譯過程中的語法校驗

后記

webpack5 升級最難解決的問題還是 runtime 階段的異常報錯,這些報錯都是跟具體的業務場景掛鉤,無法枚舉。本文的目的也是以當前業務場景的升級經驗,將一些問題處理思路提供出來供大家參考。以上就是本次創作者中心升級 webpack 的總結,希望對大家有所幫助。

本文發布自網易云音樂技術團隊,文章未經授權禁止任何形式的轉載。我們常年招收各類技術崗位,如果你準備換工作,又恰好喜歡云音樂,那就加入我們 grp.music-fe (at) corp.netease.com!

參考資料

[1]

官方文檔: https://webpack.js.org/migrate/5/

[2]

官方文檔: https://webpack.js.org/migrate/4/

[3]

參考: https://webpack.js.org/configuration/node/

[4]

Assets Module: https://webpack.js.org/guides/asset-modules/

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/274446.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/274446.shtml
英文地址,請注明出處:http://en.pswp.cn/news/274446.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Java學習筆記(7)——輸入輸出

1、File 不同的操作系統對于檔案系統路徑的設定各有差別,例如在Windows中,一個路徑的表示法可能是: "c:\\Windows\\Fonts\\" 而在Linux下的路徑設定可能是: "/home/justin/" Windows的路徑指定是使用UNC&…

全庫模式 用戶模式 表模式_暗模式,亮模式和用戶的故事

全庫模式 用戶模式 表模式I have been working on designing a UI for an app that has individuals over the age of 60 as its main audience. At some point, I found my design more appealing in dark mode. As a UX designer, I know that my opinions and preferences d…

Rollup 與 Webpack 的 Tree-shaking

大家好,我是若川。我持續組織了近一年的源碼共讀活動,感興趣的可以 點此掃碼加我微信 lxchuan12 參與,每周大家一起學習200行左右的源碼,共同進步。同時極力推薦訂閱我寫的《學習源碼整體架構系列》 包含20余篇源碼文章。歷史面試…

聚類與分類的主要區別在于:_經驗在于細節:分析流服務的用戶體驗

聚類與分類的主要區別在于:看不見的差異 (The Invisible Difference) When app markets mature the overlap in features and designs grows closer as they catch up and copy each other. The more similar the apps are to one another, the more important the …

asp.net 動態創建TextBox控件 如何加載狀態信息

接著上文Asp.net TextBox的TextChanged事件你真的清楚嗎? 這里我們來說說狀態數據時如何加載的。雖然在Control中有調用狀態轉存的方法,但是這里有一個判斷條件 if (_controlState > ControlState.ViewStateLoaded) 一般的get請求這里的條件是不滿足…

從零實現一個迷你 Webpack

大家好,我是若川。我持續組織了近一年的源碼共讀活動,感興趣的可以 點此掃碼加我微信 lxchuan12 參與,每周大家一起學習200行左右的源碼,共同進步。同時極力推薦訂閱我寫的《學習源碼整體架構系列》 包含20余篇源碼文章。歷史面試…

ios 刷新遮罩遮罩_在Adobe XD中進行遮罩的3種方法

ios 刷新遮罩遮罩Are you new to Adobe XD? Or maybe you’re just stuck on how to create a simple mask? Here are 3 quick tips for how to mask your photos and designs in Adobe XD.您是Adobe XD的新手嗎? 或者,也許您只是停留在如何創建簡單的…

C#除法運算

C#中進行算術運算容易牽扯到類型的自動轉換,這種自動轉換稱之為隱式轉換,當然還可以人為的強制轉換 隱式轉換要求:不丟失精度,而且轉換前后都為數值 強制轉換:容易丟失可能會丟失精度 1 namespace 除法運算2 {3 cl…

Vite 4.0 正式發布!

源碼共讀我新出了:第40期 | vite 是如何解析用戶配置的 .env 的鏈接:https://www.yuque.com/ruochuan12/notice/p40也可以點擊文末閱讀原文查看,歡迎學習記筆記~12 月 9 日,Vite 4.0 正式發布。下面就來看看 Vite 4.0 有哪些更新吧…

代碼復審

我們CodingCook復審的是WWW的代碼,他們的項目是時間管理助手(TimeLine)。只是跟根據自己的經驗來看,不一定準 先說一下整體的感覺。WWW的代碼用了應該是比較符合面向對象的思想,借口,封裝隨處可見&#xff…

圖像標注技巧_保護互聯網上圖像的一個簡單技巧

圖像標注技巧補習 (TUTORIAL) Have you ever worried about sharing your images on the Internet? Anytime you upload something to the web you risk the chance of your work being used (without permission) by another.您是否曾經擔心過要在Internet上共享圖像&#xf…

【VueConf 2022】尤雨溪:Vue的進化歷程

大家好,我是若川。我持續組織了近一年的源碼共讀活動,感興趣的可以 點此掃碼加我微信 lxchuan12 參與,每周大家一起學習200行左右的源碼,共同進步。同時極力推薦訂閱我寫的《學習源碼整體架構系列》 包含20余篇源碼文章。歷史面試…

WCF netTcpBinding寄宿到IIS7

config配置文件不多說 <?xml version"1.0" encoding"utf-8" ?> <configuration><system.serviceModel><behaviors><serviceBehaviors><behavior name"myBehavior"><serviceMetadata/></behavior…

ar軟件測試工具_如何為用戶測試制作快速的AR原型

ar軟件測試工具We had a project recently with an element of AR-based interaction, which it turned out was impossible to create as a prototype in either Invision or Framer (our current stack). This had a massive impact on our ability to test with users in a …

未來ui設計的發展趨勢_2025年的未來UI趨勢?

未來ui設計的發展趨勢Humans are restless.人類是不安的。 Once we find something that works, we get used to it and we crave the next big thing. The next innovation. When will the future finally arrive? And when it does, how long will it take us to get used …

內存泄露檢測 vld

VLD是一款開源檢測內存泄露軟件的簡稱&#xff1a;Visual Leak Detector 網站&#xff1a;http://vld.codeplex.com/ 使用&#xff1a; 1. 安裝vld 或者 下載相關 .h&#xff0c;lib&#xff0c;dll 文件 2. 方法很簡單&#xff0c;只要在包含入口函數的.cpp文件中包含vld.h就可…

Monorepo 在網易的工程改造實踐

大家好&#xff0c;我是若川。我持續組織了近一年的源碼共讀活動&#xff0c;感興趣的可以 點此掃碼加我微信 lxchuan12 參與&#xff0c;每周大家一起學習200行左右的源碼&#xff0c;共同進步。同時極力推薦訂閱我寫的《學習源碼整體架構系列》 包含20余篇源碼文章。歷史面試…

這一年,Vue.js 生態開源之旅帶給我很大收獲~

大家好&#xff0c;我是若川。我持續組織了近一年的源碼共讀活動&#xff0c;感興趣的可以 點此掃碼加我微信 lxchuan12 參與&#xff0c;每周大家一起學習200行左右的源碼&#xff0c;共同進步。同時極力推薦訂閱我寫的《學習源碼整體架構系列》 包含20余篇源碼文章。歷史面試…

CSSyphus:煩躁不安的煩惱設計指南。

I’m trapped at home with my website. Or maybe it’s trapped at home with me. While some are using the weird lump of time provided by lockdown to indulge in baking, dancing, painting, singing, I’m using it to play around with code.我 被自己的網站困在家里。…

重構與臭豆腐4

重構要繼續&#xff0c;臭豆腐要做。   這個重構中各種提取類&#xff0c;方法&#xff0c;字段&#xff0c;可以方便的理解&#xff0c;如果使用了設置模式就更加邏輯清晰了。切東西也要講究刀法的。 重構可以方便的使用設計模式。設計模式為重構提供了目標。 比如多個if 可…