Webpack5 實(shí)踐 - 構(gòu)建效率倍速提升!
本文轉(zhuǎn)載自微信公眾號(hào)「五月君」,作者五月君。轉(zhuǎn)載本文請(qǐng)聯(lián)系五月君公眾號(hào)。
對(duì)于前端構(gòu)建工具 Webpack、babel、eslint 等的每一次升級(jí),就像剛剛經(jīng)歷一場(chǎng)地震似得,最不想面對(duì)的就是處理各種 API 的不兼容性,有時(shí)還會(huì)出現(xiàn)一些奇奇怪怪的問(wèn)題,為什么還要升呢?并不是為了給自己找事,還是要講究投入產(chǎn)出比的,也就是最終的收益是要大于產(chǎn)出比的。
前段在團(tuán)隊(duì)內(nèi)部對(duì) Webpack v5 帶來(lái)的一些新特性做一些 Research,相較于一些項(xiàng)目的構(gòu)建工具版本(Webpack v3)做了一個(gè)對(duì)比,在構(gòu)建效率這塊是有質(zhì)的飛躍的,同樣相對(duì)于 Webpack v4 也是有很大提升的。本文是本次升級(jí)過(guò)程中的實(shí)踐(踩坑)記錄,分享一些值得關(guān)注的功能、一些重大的改變、遇到的一些 NPM 組建兼容性問(wèn)題,希望能給予讀者朋友一些參考和幫助。
先上一張腦圖,涵蓋本文主題!
構(gòu)建效果對(duì)比
基于一些項(xiàng)目做了一些測(cè)試,首次構(gòu)建相較于之前提速將近 2 倍多,二次構(gòu)建差不多 2s 左右,效果更顯著,修改文件后的增量構(gòu)建,差不多也在幾秒鐘可完成,整體構(gòu)建效率提升還是很明顯的,除此之外打包后的文件大小也比之前小了一些,但之間的差距不是特別的大,重點(diǎn)還是構(gòu)建效率大幅提升。
構(gòu)建效率上之所以有這么大的性能提升,這與它的基于文件系統(tǒng)的持久化緩存是有很大幫助的,下文會(huì)講解。內(nèi)部的項(xiàng)目數(shù)據(jù)就不便再這里展示了,文末提供了一些來(lái)自社區(qū)的實(shí)踐,也可以看到一些數(shù)據(jù)對(duì)比。
下面,基于之前 Research 時(shí)寫的一些 Demo 可以對(duì)比下使用了持久化緩存在初次構(gòu)建、二次無(wú)文件改動(dòng)構(gòu)建、改動(dòng)文件后增量構(gòu)建三種情況下的效果對(duì)比,也可以顯著的看到一些效果。
代碼壓縮(生產(chǎn)環(huán)境)
JavaScript 代碼壓縮
Webpack5 在生產(chǎn)環(huán)境下默認(rèn)使用自帶的 TerserPlugin 插件(無(wú)需安裝)來(lái)做代碼壓縮,這個(gè)插件也被認(rèn)為是在代碼壓縮方面性能是較好的。無(wú)需再借助 UglifyjsPlugin、ParallelUglifyPlugin 這些插件了。
如果你使用的是 webpack4 版本需要手動(dòng)安裝 yarn add terser-webpack-plugin -D 并將插件添加到生產(chǎn)環(huán)境的配置文件中。
以下是使用示例,在 Webpack v5 的生產(chǎn)環(huán)境默認(rèn)開啟。
- const TerserPlugin = require("terser-webpack-plugin");
- module.exports = {
- optimization: {
- minimize: true,
- minimizer: [new TerserPlugin()],
- },
- };
支持做一些自定義的配置:文件過(guò)濾、并發(fā)運(yùn)行等,詳細(xì)參見 Webpack 文檔 TerserWebpackPlugin[1]。
- test:匹配需要壓縮的文件。
- include:匹配包含的目錄。
- exclude:匹配不需要包含的目錄。
- parallel:多進(jìn)程并發(fā)運(yùn)行,默認(rèn) os.cpus().length - 1。
- module.exports = {
- optimization: {
- minimize: true,
- minimizer: [
- new TerserPlugin({
- test: /\.js(\?.*)?$/i,
- include: /\/includes/,
- exclude: /\/excludes/,
- parallel: true
- // more ...
- }),
- ],
- },
- };
CSS 文件分離
CSS 壓縮之前先做的一項(xiàng)工作是 CSS 和 JS 文件分離,如果是從 Webpack v3 升級(jí)到 v5 會(huì)遇到一些問(wèn)題,之前使用的是 extract-text-webpack-plugin 在 webpack v5 會(huì)收到廢棄提醒,建議使用 **MiniCssExtractPlugin** 這個(gè)插件,本插件基于 webpack v4 的新特性(模塊類型)構(gòu)建。
與 extract-text-webpack-plugin 相比,擁有這些特性:異步加載、沒(méi)有重復(fù)的編譯(性能提升)、更容易使用、特別針對(duì) CSS 開發(fā)。
下面是一個(gè)配置,這里還有些優(yōu)化,生產(chǎn)模式使用 mini-css-extract-plugin 插件分離 JS/CSS 文件實(shí)現(xiàn)并行加載,而開發(fā)環(huán)境選擇 style-loader 它可以使用多個(gè)標(biāo)簽將 CSS 插入到 DOM 中,并且反應(yīng)會(huì)更快。
- const MiniCssExtractPlugin = require('mini-css-extract-plugin');
- module.exports = {
- module: {
- rules: [
- {
- test: /\.css$/i,
- use: [
- devMode ? 'style-loader' : MiniCssExtractPlugin.loader,
- 'css-loader'
- ],
- },
- ],
- },
- plugins: [new MiniCssExtractPlugin()],
- };
關(guān)于 CSS 分割插件的更詳細(xì)配置 Webpack 文檔 mini-css-extract-plugin[2]。
CSS 打包后加載圖片 404?
生產(chǎn)環(huán)境我們使用 mini-css-extract-plugin 插件分離 CSS 文件,如果你在 CSS 里引用了圖片,可能會(huì)遇到為什么打包后 CSS 里引用的圖片加載時(shí) 404 了?
在 Webpack 的 output 選項(xiàng)中有一個(gè) publicPath 配置,它指定了應(yīng)用程序中所有資源的基礎(chǔ)路徑。
- module.exports = {
- output: {
- publicPath: 'auto'
- }
- }
Webpack loader 的 options 選項(xiàng)中也有一個(gè) publicPath 配置,為 CSS 內(nèi)的圖片、文件等外部資源指定一個(gè)自定義的公共路徑,默認(rèn)值為 output.publicPath。如果出現(xiàn)打包后 CSS 內(nèi)圖片 404 的可以檢查下這里的配置是否有問(wèn)題。
- const MiniCssExtractPlugin = require('mini-css-extract-plugin');
- module.exports = {
- module: {
- rules: [
- {
- test: /\.css$/i,
- use: [
- devMode ? 'style-loader' : {
- loader: MiniCssExtractPlugin.loader,
- options: {
- publicPath: '../'
- }
- },
- ],
- },
- ],
- },
- plugins: [new MiniCssExtractPlugin()],
- };
CSS 代碼壓縮
CSS 壓縮之前會(huì)使用 optimize-css-assets-webpack-plugin 這個(gè)插件,在 webpack v5 之后推薦使用 css-minimizer-webpack-plugin 這個(gè)插件。
- const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
- module.exports = {
- optimization: {
- minimizer: [
- new CssMinimizerPlugin(),
- ],
- },
- };
性能提升核心緩存優(yōu)化
之前通過(guò) cache-loader、babel-loader?cacheDirectory 在配置 cacheDirectory:true 實(shí)現(xiàn)將編譯結(jié)果寫入磁盤或者通過(guò) hard-source-webpack-plugin 插件。
Webpack5 自帶緩存能力,會(huì)緩存生成的 webpack module 和 chunk,對(duì)于二次構(gòu)建有了很大的性能提升。通過(guò) cache 屬性配置,分為內(nèi)存和文件兩種緩存方式,默認(rèn)在生產(chǎn)環(huán)境是禁用的,需自行開啟。
基于內(nèi)存緩存
當(dāng)在開發(fā)環(huán)境默認(rèn)設(shè)置為 memory,基于內(nèi)存的緩存,除了下面的方式配置外,也可通過(guò) cache: true 配置。
- module.exports = {
- cache: {
- type: 'memory'
- },
- };
基于 FileSystem 的持久化緩存
基于內(nèi)存的緩存,只有在服務(wù)運(yùn)行中,才有效,每次的單獨(dú)構(gòu)建是利用不了緩存的,webpack5 對(duì)于緩存另一個(gè)比較好的功能是提供了基于文件系統(tǒng)的持久化緩存。
基于文件系統(tǒng)的持久化緩存無(wú)論在單獨(dú)構(gòu)建或連續(xù)構(gòu)建(可以指熱更新操作)中都可應(yīng)用,首先它會(huì)查看內(nèi)存緩存,如果未命中,則降級(jí)到文件系統(tǒng)緩存。
應(yīng)用很簡(jiǎn)單,設(shè)置 type:filesystem。默認(rèn)情況下它位于 node_modules/.cache/webpack/ 目錄,我們還可以通過(guò) name 屬性修改它的名稱,例如,我們通過(guò)不同的環(huán)境 NODE_ENV 來(lái)區(qū)別不同環(huán)境的緩存。
當(dāng) type 設(shè)置為 filesystem 后,有很多屬性是可以配置的,參見 Webpack 文檔 cache[3]。
- module.exports = {
- cache: {
- type: 'filesystem',
- buildDependencies: {
- config: [__filename],
- },
- name: `${ process.env.NODE_ENV || 'development'}-cache`
- }
- }
緩存失效
基于內(nèi)存的緩存每一次重新運(yùn)行都是一次新的構(gòu)建。需要注意的是持久化緩存,當(dāng)你修改了文件或傳遞了一些參數(shù),發(fā)現(xiàn)最終展現(xiàn)的效果沒(méi)有被更改,通常這與持久化緩存的緩存策略相關(guān)。
出于性能考慮,緩存會(huì)跳過(guò) node_modules 認(rèn)為這會(huì)極大降低 webpack 執(zhí)行速度,建議是不要手動(dòng)編輯 node_modules。通常也不會(huì)這么干直接去修改 node_modules。
有些操作也會(huì)使緩存失效,例如:當(dāng) NPM 升級(jí) loader、plugin、更改配置等。
Webpack 提供了 buildDependencies、name、version 三種方式可以使構(gòu)建緩存失效。
方法一:cache.buildDependencies
buildDependencies 指定構(gòu)建過(guò)程中受影響的代碼依賴,默認(rèn)為 webpack/lib,當(dāng) node_modules 中的 webpack 或其依賴項(xiàng)發(fā)生任何變化,當(dāng)前的緩存即失效。
還有一個(gè)是指定的配置文件 config: [__filename] 或配置文件的依賴項(xiàng)發(fā)生變化,也會(huì)失效。
- module.exports = {
- cache: {
- type: 'filesystem',
- buildDependencies: {
- defaultWebpack: ["webpack/lib/"],
- config: [__filename],
- },
- name: `${ process.env.NODE_ENV || 'development'}-cache`
- }
- }
方法二:cache.version
如果是把構(gòu)建工具封裝為一個(gè)單獨(dú)的工具包,類似于 react-scripts 這種的,理論上每次升級(jí)工具包,就需要重新編譯的,之前在一次本地測(cè)試時(shí)發(fā)現(xiàn)工具包升級(jí)后緩存沒(méi)有失效,如果出現(xiàn)這種情況的可以在 cache 里加上 version 配置指向 package.json 里的 version。
- module.exports = {
- cache: {
- type: 'filesystem',
- version: `${packageJson.version}`
- }
- }
有時(shí)配置文件或者代碼沒(méi)有修改,但是會(huì)依賴于命令行傳遞值想使緩存失效,同樣也可在 version 上加上這些命令行傳遞的值做為版本控制。
- module.exports = {
- cache: {
- type: 'filesystem',
- version: `${process.env.CLI_VALUE}`
- }
- }
當(dāng) version 依賴于多個(gè)值時(shí),可以將多個(gè)值做個(gè) md5 生成一串唯一的字符串做為版本也可。
方法三:cache.name
name 屬性比較好的是可以保存多個(gè)緩存目錄,例如通過(guò) process.env.NODE_ENV 區(qū)分不同的環(huán)境。
- module.exports = {
- cache: {
- type: 'filesystem',
- name: `${ process.env.NODE_ENV || 'development'}-cache`
- }
- }
持久化緩存這塊也有很多的東西可以講,詳情參見 [譯] webpack 5 之持久化緩存[4]。
長(zhǎng)期緩存優(yōu)化
Webpack 5 新增了長(zhǎng)期緩存算法,以確定性的方式為模塊和分塊分配短的(3 或 5 位)數(shù)字 ID,這是包大小和長(zhǎng)期緩存之間的一種權(quán)衡,生產(chǎn)環(huán)境默認(rèn)開啟以下配置。在減小文件打包大小同時(shí)也利于瀏覽器長(zhǎng)期緩存(不會(huì)因?yàn)閯h除或新增一個(gè)模塊而導(dǎo)致大量緩存文件失效)。
- // production default
- module.exports = {
- optimization: {
- moduleIds: 'deterministic',
- chunkIds: 'deterministic'
- mangleExports: 'deterministic'
- }
- }
Webpack v5 VS v4 模塊 ID
Webpack v4 及之前的 moduleId 默認(rèn)是自增的,例如 0.xxx.js、1.xxx.css、2.xxx.js 如果更改模塊數(shù)量(即使內(nèi)容沒(méi)有變化),也會(huì)導(dǎo)致模塊文件重新發(fā)生改變,不利于長(zhǎng)期緩存。
不同的版本也提供了不同的解決方案,webpack v4 之前使用 HashedModuleIdsPlugin 插件覆蓋默認(rèn)的模塊 ID 規(guī)則,在 webpack v4 中可以配置 optimization.moduleIds = 'hashed' 解決。這幾種方案都是使用模塊路徑生成的 hash 做為 moduleId。
Webpack v5 生產(chǎn)環(huán)境默認(rèn) optimization.moduleIds='deterministic' 無(wú)需更改。
Webpack v5 VS v4 Chunk ID
webpack v4 及之前的 chunkId 默認(rèn)也是遞增的,如果在 entry 配置中新增或刪除一個(gè)元素,chunkId 也會(huì)隨著遞增或遞減。
webpack v4 之前使用 NamedChunksPlugin 插件覆蓋默認(rèn)的 chunkId 規(guī)則,在 webpack v4 中可以配置 optimization.chunkIds = 'named' 解決。
Webpack v5 生產(chǎn)環(huán)境默認(rèn) optimization.chunkIds='deterministic' 無(wú)需更改。
真正的內(nèi)容哈希
另外,當(dāng)使用 [contenthash] 時(shí),webpack5 將使用真正的文件內(nèi)容做為哈希值,這個(gè)類似于協(xié)商緩存 Etag,不一樣的是還有一些優(yōu)化,如果你只是刪除了代碼中的一些注釋或重新命名變量,而這種情況代碼邏輯是沒(méi)有修改的,這些變化在壓縮后是不可見的,不會(huì)導(dǎo)致 [contenthash] 也發(fā)生變化。
如果是從 webpack v3 升級(jí)到 v5 的,HashedModuleIdsPlugin、NamedChunksPlugin 這些插件是可以去掉的,webpack v5 環(huán)境默認(rèn)開啟新的算法,無(wú)需再配置。
參考文檔 Webpack release 日志記錄 — 重大變更:長(zhǎng)期緩存[5]。
原生支持資源模塊
Webpack v5 內(nèi)置了資源模塊(assert),用來(lái)處理資源文件(圖片、字體等),在之前是通過(guò)配置額外的 loader,例如 raw-loader、file-loader、url-loader 實(shí)現(xiàn)的。
Webpack v4 資源文件處理
下面是一段 webpack v4 及之前版本的資源文件處理配置,當(dāng)匹配的文件大小如果小于 limit 限制,將其處理成 data URI 內(nèi)聯(lián)到 bundle 中,否則生成文件(使用 file-loader)輸出到目錄中,url-loader 內(nèi)置了 file-loader 對(duì)文件的處理。
- {
- test: /\.(jpe?g|png|gif|svg)$/,
- use: [
- {
- loader: 'url-loader',
- options: {
- limit: 1024 * 10
- }
- }
- ]
- },
Webpack v5 新的資源文件處理
Webpack v5 不再需要安裝 url-loader 處理資源文件,內(nèi)置了資源模塊類型,通過(guò) type 定義,用來(lái)替換之前需要額外配置 loader 的方式。
- asset/resource:將文件打包輸出并導(dǎo)出 URL,類似于 file-loader。
- asset/inline:導(dǎo)出一個(gè)資源的 data URI,編碼到 bundle 中輸出,類似于 url-loader。
- asset/source:導(dǎo)出資源的源代碼,類似于 raw-loader。
- asset:提供了一種通用的資源類型,根據(jù)設(shè)置的 Rule.parser.dataUrlCondition.maxSize 自動(dòng)的在 asset/resource、asset/inline 之間做選擇,小于該大小指定的文件視為 inline 模塊類型,否則視為 resource 模塊類型。
下面是一個(gè)示例,大于 4kb 的輸出到目錄 static 中。
- {
- test: /\.(png|jpe?g|gif|svg|eot|ttf|woff|woff2)$/i,
- type: "asset",
- parser: {
- dataUrlCondition: {
- maxSize: 4 * 1024 // 4kb
- }
- },
- generator: {
- filename: 'static/[hash][ext][query]'
- }
- },
參考文檔 Webpack 文檔 Assert module[6]。
強(qiáng)大的 tree-shaking 能力
tree-shaking 是一個(gè)術(shù)語(yǔ),翻譯為中文為 “樹搖”,想想一下一顆長(zhǎng)滿果子的樹木,其中有些已經(jīng)熟透了,當(dāng)搖晃樹木時(shí)是不是一部分會(huì)被搖掉。
圖片來(lái)源:https://cdn.pixabay.com/photo/2019/05/16/23/39/apple-tree-4208594_1280.jpg
對(duì)于我們代碼層面來(lái)說(shuō),那些上下文未引用的 JavaScript 代碼,也可以通過(guò)工具移除(“搖掉”),實(shí)現(xiàn)打包體積的優(yōu)化。
嵌套的 tree-shaking
在這種情況下,可以刪除未使用的變量 b,生產(chǎn)環(huán)境默認(rèn)開啟。
- // inner.js
- export const a = 1;
- export const b = 2;
- // module.js
- export * as inner from './inner';
- // user.js
- import * as module from './module';
- console.log(module.inner.a);
內(nèi)部模塊 tree-shaking
Webpack v5 還增加了模塊導(dǎo)出和引用之間的依賴關(guān)系分析,通過(guò)配置 optimization.innerGraph 控制,生產(chǎn)環(huán)境默認(rèn)開啟。
以下示例,something 只有在使用 test 導(dǎo)出時(shí)才會(huì)使用。
- import { something } from './something';
- function usingSomething() {
- return something;
- }
- export function test() {
- return usingSomething();
- }
支持 CommonJS Tree Shaking
新增 CommonJS 模塊的導(dǎo)出和引用之間的依賴分析,下例,可以刪除未使用的變量 b。
- // inner.js
- exports.a = 1;
- exports.b = 2;
- // module.js
- exports.inner = require('./inner');
- // user.js
- const module = require('./module');
- console.log(module.inner.a);
參考 Webpack 文檔 tree-shaking[7]。
Node.js 調(diào)用 webpack API
之前在團(tuán)隊(duì)內(nèi)部,基于 webpack 這些構(gòu)建工具封裝了適合團(tuán)隊(duì)內(nèi)部的構(gòu)建工具模塊,是通過(guò) API 調(diào)用的,有些問(wèn)題還是要注意下。
生產(chǎn)環(huán)境
調(diào)用 webpack() 創(chuàng)建一個(gè) compiler 實(shí)例,之后調(diào)用 run() 方法執(zhí)行,需要注意的是在完成之后記得關(guān)閉 compiler,這樣低優(yōu)先級(jí)的工作(比如持久緩存)就有機(jī)會(huì)完成,否則,有時(shí)候會(huì)發(fā)現(xiàn)每次都是重新構(gòu)建沒(méi)有利用上緩存。
下例中的 stats 參數(shù)可以獲取到代碼編譯過(guò)程產(chǎn)生的錯(cuò)誤和警告、計(jì)時(shí)信息、module 和 chunk 信息,如果想達(dá)到 cli 環(huán)境下的日志輸出格式,調(diào)用 stats.toString() 方法即可。
- const compiler = webpack(config);
- return new Promise((resolve, reject) => {
- compiler.run((err, stats) => {
- if (err) {
- return reject(err);
- }
- console.log(stats.toString({
- chunks: false,
- colors: true
- }));
- compiler.close(closeErr => {
- if (closeErr) {
- console.log(chalk.red(`compiler close failed with message ${closeErr.message}`));
- }
- });
- return resolve(stats);
- });
- });
開發(fā)環(huán)境
與生產(chǎn)環(huán)境 API 調(diào)用不同,開發(fā)環(huán)境我們需要熱更新,在創(chuàng)建一個(gè) compiler 后需要調(diào)用 webpack-dev-server 插件。
還有個(gè)問(wèn)題是 devServer 中的配置選項(xiàng)將被忽略,但可以將配置選項(xiàng)作為第二個(gè)參數(shù)傳入。
- const compiler = webpack(config);
- const devServerOptions = Object.assign({}, config.devServer, {
- port: port,
- host: DEFAULT_HOST,
- open: true,
- });
- const server = new WebpackDevServer(compiler, devServerOptions);
- server.listen(port, DEFAULT_HOST);
參考文檔 Webpack 文檔 Node.js API 接口[8]。
原生 Web Worker 支持
從 webpack 5 開始,使用 Web Workers 代替 worker-loader,這種語(yǔ)法也是為了實(shí)現(xiàn)不使用 bundler 就可以運(yùn)行代碼。
Web Worker 是解決一些密集型的任務(wù),例如一些加解密、圖片處理等一些耗時(shí)的計(jì)算任務(wù)可以放置于工作線程處理,處理完畢在通知到主線程,在處理的過(guò)程不會(huì)影響用戶在界面上的一些其它操作。
- const worker = new Worker(new URL('./worker-calculate.js', import.meta.url));
- worker.postMessage({
- question:
- 'The Answer to the Ultimate Question of Life, The Universe, and Everything.',
- });
- worker.onmessage = ({ data: { answer } }) => {
- console.log(answer);
- };
- // worker-calculate.js
- self.onmessage = ({ data: { question } }) => {
- self.postMessage({
- answer: 42,
- });
- };
Web Workers 可以在瀏覽器中的原生 ECMAScript 模塊中使用,也可以用于 Node.js 中,如果采用 ESM 模塊規(guī)范,Node.js 需要 >= 12.17.0。
- import { Worker } from 'worker_threads';
- new Worker(new URL('./worker.js', import.meta.url));
Node.js 通過(guò) worker_threads 模塊提供支持,在 Node.js 中如果你使用 CommonJS 規(guī)范在 v10.0.5 版本就已經(jīng)支持了。
- const { Worker } = require('worker_threads');
更多詳情參考 webpack 文檔 Web Workers[9]。
其它 NPM 組件兼容性問(wèn)題
開發(fā)環(huán)境熱更新
如果是從 webpack v3.x 升級(jí)的,會(huì)發(fā)現(xiàn)之前的熱更新方式會(huì)報(bào)如下錯(cuò)誤。
- Error: Cannot find module 'webpack/bin/config-yargs'
Webpack v5 使用 webpack serve 啟動(dòng)開發(fā)環(huán)境,解決這個(gè)問(wèn)題就是重新安裝 webpack-cli、還有 webpack-dev-server 也是需要安裝的。
- // For webpack-cli 3.x:
- "scripts": { "start:dev": "webpack-dev-server" }
- // For webpack-cli 4.x:
- "scripts": { "start:dev": "webpack serve" }
問(wèn)題參考 cannot-find-module-webpack-bin-config-yargs。
compiler.plugin is not a function with InterpolateHtmlPlugin
可能會(huì)遇到以下錯(cuò)誤:
- TypeError: compiler.plugin is not a function
- at InterpolateHtmlPlugin.apply (/Users/qufei/Documents/code/f-designer-tool-webpack5-test/node_modules/react-dev-utils/InterpolateHtmlPlugin.js:25:14)
單獨(dú)安裝插件 yarn add -D interpolate-html-plugin 替換 InterpolateHtmlPlugin = require('react-dev-utils/interpolate-html-plugin'); 為 const InterpolateHtmlPlugin = require('interpolate-html-plugin');
最后確保 InterpolateHtmlPlugin 出現(xiàn)在插件列表中的 HtmlWebpackPlugin 之后。
webpack.NamedModulesPlugin is not a constructor with NamedModulesPlugin
當(dāng)開啟 HMR 的時(shí)候使用該插件會(huì)顯示模塊的相對(duì)路徑,該插件已廢棄,在 Webpack4 中建議設(shè)置為 optimization.namedModules,但是在 Webpack5 也被廢棄,如果需要請(qǐng)改為 optimization.moduleIds: 'named',在 Webpack5 中的建議是 “請(qǐng)考慮將 optimization.moduleIds 和 optimization.chunkIds 從你 webpack 配置中移除。使用默認(rèn)值會(huì)更合適,因?yàn)樗鼈儠?huì)在 production 模式 下支持長(zhǎng)效緩存且可以在 development 模式下進(jìn)行調(diào)試。” 參見 https://webpack.docschina.org/migrate/5/#clean-up-configuration
移除 Node.js 模塊 Polyfills
Webpack5 移除了 Node.js 模塊的 Polyfills,更專注于前端模塊兼容。認(rèn)為這會(huì)在構(gòu)建時(shí)給 bundle 附加龐大的 Polyfills,大部分情況下也并非必須的,如果你的模塊或你安裝的第三方模塊引用了 cypto、process 這些模塊,就會(huì)看到報(bào)錯(cuò)。
- process is not a function
- xxx is not a function
參考 automatic-nodejs-polyfills-removed,對(duì)于 webpack v5,可以從 webpack.config.js 的相應(yīng)插件部分引用 process/browser。
參考 webpack-bundle-js-uncaught-referenceerror-process-is-not-defined。
- const webpack = require('webpack')
- module.exports = {
- plugins: [
- // fix "process is not defined" error:
- // (do "npm install process" before running the build)
- new webpack.ProvidePlugin({
- process: 'process/browser',
- }),
- ]
- }
compiler.plugin is not a function with react-dev-utils/WatchMissingNodeModulesPlugin
- new WatchMissingNodeModulesPlugin(),
- // 運(yùn)行之后報(bào)錯(cuò)
- // TypeError: compiler.plugin is not a function
- // at WatchMissingNodeModulesPlugin.apply (/Users/xxx/node_modules/react-dev-utils/WatchMissingNodeModulesPlugin.js:20:14)
這個(gè)錯(cuò)誤是在 Webpack 4 upgrade PR,升級(jí) react-dev-utils yarn add react-dev-utils -D。
babel-eslint has been deprecated
之前在使用 eslint 代碼檢查時(shí),如果有用到 eslint 不支持的試驗(yàn)性特性時(shí)會(huì)需要用到 babel-eslint,但是這個(gè)項(xiàng)目已經(jīng)廢棄了,到官網(wǎng)會(huì)看到這樣一句話:
- babel-eslint is now @babel/eslint-parser. This package will no longer receive updates.
現(xiàn)在推薦使用 @babel/eslint-parser 代替。
更多新功能
- Top Level Await 支持:目前在 Webpack v5 計(jì)劃中屬于試驗(yàn)性支持,可通過(guò)開啟 experiments.topLevelAwait 配置支持,這對(duì)于文件頭部初始化資源很有用,無(wú)需讓 await 必須在 async 里面。參考 Webpack v5 配置#experiments。
- 模塊聯(lián)邦(Federated Modules):是 webpack v5 增加的一個(gè)新功能,為前端項(xiàng)目打包模式提供了新的方式,對(duì)多個(gè)不存在依賴關(guān)系的多個(gè)項(xiàng)目可以獨(dú)立構(gòu)建組成一個(gè)應(yīng)用程序,從開發(fā)者的角度看,模塊可以從遠(yuǎn)程構(gòu)建中導(dǎo)入。這通常稱為微前端,也并不僅限于此。更多可參考 Webpack 提供的單獨(dú)指南 module-federation,也可看看下面的社區(qū)實(shí)踐。
- 原生支持 WebAssembly 構(gòu)建:webpack v5 原生支持了 WebAssembly 的代碼構(gòu)建,只需開啟 experiments.syncWebAssembly 配置即可,這個(gè)功能也屬于試驗(yàn)性支持。
- ... 更多功能參考 webpack v5 release 日志。
來(lái)自社區(qū)實(shí)踐
- 字節(jié):Webpack5 新特性業(yè)務(wù)落地實(shí)戰(zhàn)
- 騰訊:構(gòu)建效率大幅提升,webpack5 在企鵝輔導(dǎo)的升級(jí)實(shí)踐
- 螞蟻:調(diào)研 Federated Modules,應(yīng)用秒開,應(yīng)用集方案,微前端加載方案改進(jìn)等
- 百度:Webpack 5 升級(jí)實(shí)驗(yàn)
- 飛書:Webpack5 上手測(cè)評(píng)
因?yàn)槲⑿艑?duì)外鏈的限制,文中有些鏈接不能打開,可以 “閱讀原文” 查看。
參考資料
[1]Webpack 文檔 TerserWebpackPlugin: https://webpack.docschina.org/plugins/terser-webpack-plugin/
[2]Webpack 文檔 mini-css-extract-plugin: https://webpack.docschina.org/plugins/mini-css-extract-plugin
[3]Webpack 文檔 cache: https://webpack.docschina.org/configuration/other-options/#cache
[4][譯] webpack 5 之持久化緩存: https://juejin.cn/post/6844903967793627149
[5]Webpack release 日志記錄 — 重大變更:長(zhǎng)期緩存: https://webpack.docschina.org/blog/2020-10-10-webpack-5-release/#major-changes-long-term-caching
[6]Webpack 文檔 Assert module: https://webpack.docschina.org/guides/asset-modules/
[7]Webpack 文檔 tree-shaking: https://www.webpackjs.com/guides/tree-shaking/
[8]Webpack 文檔 Node.js API 接口: https://webpack.docschina.org/api/node/
[9]webpack 文檔 Web Workers: https://webpack.docschina.org/guides/web-workers/