成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

在字節(jié)某中后臺(tái)項(xiàng)目中落地 Bundleless,我經(jīng)歷了什么?

開(kāi)發(fā) 架構(gòu)
現(xiàn)在前端主流的打包工具主要以 Webpack 為代表,但隨著項(xiàng)目規(guī)模的發(fā)展,構(gòu)建方面的痛點(diǎn)越來(lái)越突出,最大的感受就是太慢了。

[[422796]]

最近在公司探索落地 Bundless 構(gòu)建工具,嘗試將現(xiàn)有的一些業(yè)務(wù)項(xiàng)目 從 Webpack 往 Vite 遷移,由于中后臺(tái)項(xiàng)目一般對(duì)瀏覽器兼容性要求不高,可以大膽引入一些前沿且激進(jìn)的方案,因此在公司找到了一個(gè)業(yè)務(wù)中后臺(tái)項(xiàng)目初步嘗試引入 Vite。

當(dāng)然,在遷移的過(guò)程中也不是直接使用 Vite,而是在 Vite 上層有做了一層封裝,以接入團(tuán)隊(duì)目前研發(fā)的構(gòu)建工具的架構(gòu)當(dāng)中,項(xiàng)目構(gòu)建配置方面會(huì)和原始的 Vite 配置不太一樣。不過(guò)這不影響后續(xù)的踩坑和原理介紹,我也盡量把現(xiàn)場(chǎng)還原成原始的 Vite 配置,以便大家理解。其中有一些內(nèi)部工具與業(yè)務(wù)相關(guān)的問(wèn)題,這些細(xì)節(jié)就不公之于眾了,還請(qǐng)大家理解。

前言

現(xiàn)在前端主流的打包工具主要以 Webpack 為代表,但隨著項(xiàng)目規(guī)模的發(fā)展,構(gòu)建方面的痛點(diǎn)越來(lái)越突出,最大的感受就是太慢了,一方面項(xiàng)目冷啟動(dòng)時(shí)必須遞歸打包整個(gè)項(xiàng)目的依賴樹(shù),另一方面 JavaScript 語(yǔ)言本身(解釋執(zhí)行、單線程執(zhí)行)的限制,導(dǎo)致構(gòu)建性能遇到瓶頸。

在這樣的背景下,一些被稱為 Bundleless (或者 Unbundled) 的構(gòu)建工具應(yīng)運(yùn)而生,諸如 Snowpack、Vite,其中 Vite 最近在社區(qū)的呼聲越來(lái)越高,GitHub 上的 star 30k+,甚至已經(jīng)超過(guò) vue3 倉(cāng)庫(kù)的 star 數(shù)(目前 24.1k),可見(jiàn)其影響力之大。

和 Webpack 這種傳統(tǒng)的打包工具相比,Vite 主要有兩大主要優(yōu)勢(shì):

  1. 利用瀏覽器內(nèi)置 ES Module 的支持(script 標(biāo)簽加上屬性 type="module" 即可),瀏覽器直接向 dev server 逐個(gè)請(qǐng)求各個(gè)模塊,而不需要提前把所有文件打包。
  2. 借助 esbuild 超快的編譯速度把第三方庫(kù)進(jìn)行預(yù)構(gòu)建,一方面將零散的文件打到一起,減少網(wǎng)絡(luò)請(qǐng)求,另一方面全面轉(zhuǎn)換為 ESM 模塊語(yǔ)法,以適配瀏覽器內(nèi)置的 ESM 支持。

顧名思義,Bundleless 和傳統(tǒng)的構(gòu)建工具相比,最大的特點(diǎn)就是不用將業(yè)務(wù)代碼打包(雖然第三方庫(kù)還是得打包,這個(gè)沒(méi)有辦法),尤其是在項(xiàng)目逐漸龐大的時(shí)候,可以極大地提升構(gòu)建效率和開(kāi)發(fā)體驗(yàn)。經(jīng)過(guò)在公司業(yè)務(wù)項(xiàng)目的落地,底層從 Webpack 切到 Vite,冷啟動(dòng)速度提升 400% 以上,原本 20 秒啟動(dòng)的項(xiàng)目現(xiàn)在 3~4 秒 即可冷啟,并且在依賴沒(méi)有變動(dòng)的情況下,二次啟動(dòng)直接秒開(kāi),不得不感慨:實(shí)在是太快了!

遷移問(wèn)題

SVG 組件報(bào)錯(cuò)

Vite 本身沒(méi)有對(duì) svg 組件寫法的支持,在默認(rèn)情況下,下面的寫法會(huì)導(dǎo)致瀏覽器報(bào)錯(cuò):

  1. import Up from 'common/imgs/up.svg'
  2.  
  3. function Home() { 
  4.   return { 
  5.     <> 
  6.       // 省略其他子組件 
  7.       <Up className="admin-header-user-up" /> 
  8.     </> 
  9.   } 

為了解決這個(gè)問(wèn)題,在社區(qū)現(xiàn)有的生態(tài)中找到 vite-plugin-react-svg 插件,添加到 Vite 的 plugins數(shù)組中,實(shí)現(xiàn)了以組件方式引用 SVG 資源的能力,并以下面的方式來(lái)引入 svg 文件:

  1. import Up from 'common/imgs/up.svg?component'

第三方包內(nèi)部 bug

在 esbuild 進(jìn)行預(yù)構(gòu)建的時(shí)候,會(huì)檢查到相關(guān)第三方包依賴的報(bào)錯(cuò)。本項(xiàng)目中遇到了如下的報(bào)錯(cuò),關(guān)于 react-virtualized 當(dāng)中 esm 產(chǎn)物出現(xiàn)了問(wèn)題:

  1. // 在 WindowScroller.js 并沒(méi)有導(dǎo)出這個(gè)變量! 
  2. import { bpfrpt_proptype_WindowScroller } from "../WindowScroller.js"

在這個(gè)庫(kù)官方的 GitHub 倉(cāng)庫(kù)中也發(fā)現(xiàn)尤大提出了同樣的問(wèn)題(issue 地址: https://github.com/bvaughn/react-virtualized/issues/1632),里面報(bào)錯(cuò)的這一句引入代碼是有問(wèn)題的,引入了不存在的變量,但 webpack/rollup 沒(méi)有捕獲這個(gè)問(wèn)題,但在 esbuild 當(dāng)中報(bào)錯(cuò),總體來(lái)說(shuō)屬于第三方庫(kù)產(chǎn)物的 bug。

一般來(lái)說(shuō),解決 node_modules 中第三方庫(kù)的 bug 大概有兩種思路:

第一種思路是將第三方庫(kù)中有問(wèn)題的文件 copy 一份進(jìn)行修復(fù),放在項(xiàng)目目錄里面(非 node_modules),然后通過(guò)構(gòu)建工具 resolve.alias 能力重定向到修復(fù)后的位置。

另一種是通過(guò) patch-package 記錄 node_modules 更改記錄,生成 patches 目錄,然后通過(guò)項(xiàng)目的 post-install 腳本在團(tuán)隊(duì)中同步這個(gè)更改。

這里采用后者來(lái)實(shí)現(xiàn)第三方庫(kù)的臨時(shí) patch,當(dāng)然這也適合其他第三方庫(kù)問(wèn)題的臨時(shí)修復(fù)。

  1. // 1. 安裝 
  2. yarn add patch-package postinstall-postinstall 
  3. // 2. 修改 node_modules 代碼后執(zhí)行: 
  4. yarn patch-package react-virtualized 
  5. // 3. package.json 中 scripts 增加:  
  6.   "postinstall""patch-package" 

預(yù)構(gòu)建反復(fù)執(zhí)行

如果剛才這點(diǎn)問(wèn)題只是小打小鬧,那接下來(lái)的 bug 簡(jiǎn)直就是究極折磨。在做完基本的配置之后,項(xiàng)目的確是能夠正常顯示了,但每次清理緩存重新構(gòu)建時(shí),在預(yù)構(gòu)建階段會(huì)出現(xiàn)如下十分詭異的現(xiàn)象:

  • 控制臺(tái)一直顯示 new dependencies xxx 類似的 log,服務(wù)頻繁 reload
  • 預(yù)構(gòu)建緩存目錄 .Vite 中會(huì)不斷產(chǎn)生新的依賴緩存文件,隨著服務(wù)頻繁 reload,不斷清空所有緩存文件,再次產(chǎn)生更多的新依賴,也就是不斷地重刷緩存目錄,過(guò)大概 20 多秒才穩(wěn)定下來(lái)。
  • 在預(yù)構(gòu)建不斷重刷目錄的 20 多秒當(dāng)中,頁(yè)面無(wú)法訪問(wèn),一直處于卡死狀態(tài)

放一張事故現(xiàn)場(chǎng)圖各位好好體會(huì)一下:

1. 問(wèn)題定位

首先,拿正常情況下的 demo 項(xiàng)目試了一下,正常的預(yù)構(gòu)建情況是這樣的:

  • 一次性輸出所有的構(gòu)建緩存文件
  • 終端的 log 也很簡(jiǎn)潔,如下圖所示:

根據(jù)正常情況下的 log 在 Vite 源碼中全局搜索 Pre-bundling,反向追溯預(yù)構(gòu)建部分的源代碼。總結(jié)流程如下:

  • Vite Server 啟動(dòng)階段,在 server.listen 的回調(diào)中執(zhí)行 runOptimize 邏輯,進(jìn)入預(yù)構(gòu)建階段。
  • runOptimize 中調(diào)用 optimizeDeps,內(nèi)部調(diào)用 esbuild 進(jìn)行構(gòu)建, 并往 esbuild 里面?zhèn)魅胱远x的 scan 插件,esbuild 構(gòu)建過(guò)程中進(jìn)行依賴分析,并將依賴項(xiàng)賦給 deps
  • 拿到 deps 后打印出上述的終端 log,第一次預(yù)構(gòu)建結(jié)束。

函數(shù)調(diào)用流程如下:

  1. startServer -> runOptimize -> optimizeDeps -> scanImports -> esbuild.build 

終端所輸出的依賴信息來(lái)自于 deps 變量,這個(gè)變量在 optimizeDeps中通過(guò)執(zhí)行 scanImports 拿到:

所以我們斷點(diǎn)進(jìn)入到 scanImports 當(dāng)中:

可以看到這里會(huì)讀取配置中的 input 配置,項(xiàng)目里面配置的是./src/index.tsx,如果配置了這個(gè)后會(huì)根據(jù) root 和 input 拼接后的路徑作為入口來(lái)搜尋依賴。但不幸的是,這個(gè)入口路徑是找不到的,如下圖所示:

原因是配置文件是這樣的:

  1.   input: './src/index.js'
  2.   root: path.resolve(__dirname, 'src'

當(dāng)時(shí)為了將 html 放到 src 目錄下,就隨手設(shè)置了 root 參數(shù),導(dǎo)致和 input 拼接后的路徑找不到了,中間出現(xiàn)了src/src,很顯然是多寫了層src, 那么這種情況下相當(dāng)于沒(méi)有找到 entry,就直接返回一個(gè)空對(duì)象了,Vite 會(huì)認(rèn)為找不到入口,也就無(wú)法進(jìn)行依賴預(yù)構(gòu)建了。

2. 解決方案

解決的核心在于保證 entry 路徑是可以被 Vite 解析的,有兩種方式:

  • 去掉 root 的配置,這樣默認(rèn)會(huì)用 process.cwd() 作為 root,最后 entry 路徑是可以找到的。
  • 去掉 input 的配置,這時(shí)候 Vite 的行為是搜尋 root 目錄下的 html 文件,即使是 root 配置成項(xiàng)目根目錄下的 src 也能解析到。

3. 問(wèn)題復(fù)盤

現(xiàn)在一切正常了,但回到最初的問(wèn)題,為什么命令行中會(huì)刷出這么多 new dependencies之類的 log,構(gòu)建緩存目錄會(huì)一次次刷新,頁(yè)面會(huì)一直卡住?雖然問(wèn)題解決了,但對(duì)于這些詭異的問(wèn)題還是有必要探究清楚的。

翻閱源碼后,發(fā)現(xiàn)問(wèn)題的根源在于 Vite 預(yù)構(gòu)建并不只有在服務(wù)啟動(dòng)的時(shí)候進(jìn)行,在請(qǐng)求進(jìn)入的時(shí)候也有可能觸發(fā)預(yù)構(gòu)建,也就是說(shuō),預(yù)構(gòu)建的行為不只是在最開(kāi)始觸發(fā)一次,在瀏覽器訪問(wèn)項(xiàng)目的時(shí)候有可能再次觸發(fā),甚至是多次觸發(fā)。

之前提到的第一次預(yù)構(gòu)建的流程,在其中 runOptimize 函數(shù)中會(huì)注冊(cè)運(yùn)行時(shí)優(yōu)化的邏輯,如下圖箭頭處所示:

里面返回一個(gè)閉包函數(shù),主要是運(yùn)行時(shí)優(yōu)化的邏輯,其中會(huì)再次調(diào)用 optimizeDeps,接著順理成章地執(zhí)行 scanImports + esbuild.build,最后重啟 Vite Server。

這個(gè)閉包函數(shù)會(huì)在 Vite 的 resolvePlugin 中被調(diào)用。簡(jiǎn)單來(lái)說(shuō),當(dāng)瀏覽器發(fā)起請(qǐng)求時(shí),請(qǐng)求進(jìn)入 Vite 服務(wù)器中,首先是執(zhí)行一系列的插件(順序參考官網(wǎng)),其中就會(huì)在比較靠前的位置走到 resolvePlugin,這個(gè)插件中分析項(xiàng)目中的依賴關(guān)系,如果發(fā)現(xiàn)了有依賴沒(méi)有被預(yù)構(gòu)建,那么會(huì)執(zhí)行 _registerMissingImport 將這個(gè)依賴進(jìn)行預(yù)構(gòu)建,并重啟 Vite Server。

_registerMissingImport 調(diào)用之后會(huì)進(jìn)行二次預(yù)構(gòu)建,但不是立即執(zhí)行,相當(dāng)于每隔 100 ms 批量收集一次然后一起構(gòu)建,實(shí)際上有一個(gè)截流的過(guò)程,這樣一來(lái)不用對(duì)每個(gè)依賴都調(diào)用 optimizeDeps ,也能提高預(yù)構(gòu)建的效率,屬于 Vite 里面細(xì)節(jié)性的優(yōu)化,和 Vue 里面的nextTick 批量更新有異曲同工之妙。這里貼下_registerMissingImport返回的函數(shù)代碼:

  1. return function registerMissingImport( 
  2.   id: string, 
  3.   resolved: string, 
  4.   ssr?: boolean 
  5. ) { 
  6.   // 沒(méi)被預(yù)構(gòu)建 
  7.   if (!knownOptimized[id]) { 
  8.     currentMissing[id] = resolved 
  9.     // 截流 
  10.     if (handle) clearTimeout(handle) 
  11.     // 100 ms 后批量預(yù)構(gòu)建依賴 
  12.     handle = setTimeout(() => rerun(ssr), debounceMs) 
  13.     // 重啟 Vite Server 
  14.     server._pendingReload = new Promise((r) => { 
  15.       pendingResolve = r 
  16.     }) 
  17.   } 

實(shí)際上經(jīng)過(guò)截流的二次優(yōu)化過(guò)程也能夠解釋上述大片的 log 產(chǎn)生的原因,每隔 100 ms 都會(huì)發(fā)現(xiàn)新的依賴,都會(huì)重啟 Vite Server 預(yù)構(gòu)建,必然會(huì)導(dǎo)致大量的 new dependecies ,這個(gè)時(shí)候服務(wù)器頻繁重啟,頁(yè)面卡住也是非常正常的了。

4. 一些延伸

上述分析過(guò)程算是找到這個(gè)踩坑問(wèn)題的根源所在,不過(guò),我在 Vite 倉(cāng)庫(kù)也搜了相關(guān)的 issue,像這種二次預(yù)構(gòu)建的過(guò)程其實(shí)在正常的項(xiàng)目中也是會(huì)真實(shí)存在的,主要是為了處理項(xiàng)目中一些動(dòng)態(tài) import 的場(chǎng)景,當(dāng)這種通過(guò)動(dòng)態(tài) import 的依賴多了之后,也會(huì)非常影響構(gòu)建性能,這種場(chǎng)景下也可以用 antfu 同學(xué) 開(kāi)發(fā)的 vite-plugin-optimize-persist 這個(gè)插件進(jìn)行自動(dòng)優(yōu)化。

后續(xù)思考

遷移過(guò)后項(xiàng)目能夠正常運(yùn)行了,并獲得了相當(dāng)不錯(cuò)的構(gòu)建效率提升,但目前來(lái)看還有兩個(gè)問(wèn)題讓我陷入了思考。

1. 落地前景

首先,這個(gè)業(yè)務(wù)項(xiàng)目相對(duì)來(lái)說(shuō)架構(gòu)沒(méi)有那么復(fù)雜,如果涉及到 Monorepo、SSR 構(gòu)建或者更復(fù)雜的架構(gòu),Vite 是否還可以遷移過(guò)去?Bundless 是否可以大規(guī)模地運(yùn)用到當(dāng)前中后臺(tái)業(yè)務(wù)當(dāng)中?

這一點(diǎn)需要近期不斷的投入和實(shí)踐來(lái)驗(yàn)證,也會(huì)跟大家持續(xù)分享。

2. Vite 是否上生產(chǎn)環(huán)境

另一方面,是否應(yīng)該讓 Vite 接管生產(chǎn)環(huán)境的構(gòu)建?要知道它對(duì)于開(kāi)發(fā)環(huán)境效率的提升是非常明顯的,但是否真的適合生產(chǎn)環(huán)境?

我個(gè)人覺(jué)得開(kāi)發(fā)環(huán)境和生產(chǎn)環(huán)境得分開(kāi)看,前者的痛點(diǎn)是效率,而后者的訴求是穩(wěn)定、質(zhì)量,這個(gè)時(shí)候我覺(jué)得相比 Vite 打包用的 Rollup,生產(chǎn)環(huán)境上 Webpack 是一個(gè)更好的選擇。

但這樣一來(lái)又會(huì)帶來(lái)新的問(wèn)題——兩者的配置差異巨大,如何解決配置統(tǒng)一的問(wèn)題?關(guān)于這個(gè)問(wèn)題,之后再來(lái)專門詳細(xì)地進(jìn)行探討,我個(gè)人對(duì)此持樂(lè)觀態(tài)度,至少是有可能去實(shí)現(xiàn)的。或許真的當(dāng) Webpack 和 Vite 之間的配置差異能夠被某個(gè)方案抹平的時(shí)候,Vite 可以適用于所有正在使用 Webpack 的項(xiàng)目,換句話說(shuō),當(dāng)那一天真正到來(lái)的時(shí)候,在開(kāi)發(fā)環(huán)境下,Vite 可以完全取代 Webpack 了。

 

責(zé)任編輯:姜華 來(lái)源: 三元同學(xué)
相關(guān)推薦

2022-06-02 08:30:55

項(xiàng)目React重構(gòu)

2018-05-03 06:56:43

項(xiàng)目經(jīng)理代碼項(xiàng)目管理

2024-01-31 16:36:53

2023-07-14 13:34:34

StableDiffusion模型

2019-06-19 09:33:17

微服務(wù)面試電商

2017-04-24 11:40:26

大數(shù)據(jù)制造企業(yè)

2022-06-02 11:11:43

AI深度學(xué)習(xí)

2013-03-20 09:54:43

某單位軟件

2024-11-01 17:00:03

2023-10-06 15:29:07

MySQL數(shù)據(jù)庫(kù)更新

2017-12-04 09:26:56

架構(gòu)師碼農(nóng)菜鳥(niǎo)

2018-03-20 05:33:17

2019-09-09 13:58:07

2020-03-18 13:10:09

華為

2021-04-06 11:07:02

字節(jié)跳動(dòng)組織架構(gòu)

2015-03-09 17:49:40

SDN

2019-04-04 12:59:03

微服務(wù)企業(yè)數(shù)字化

2015-08-17 11:02:27

創(chuàng)業(yè)經(jīng)歷

2023-07-23 17:19:34

人工智能系統(tǒng)

2025-05-14 04:00:00

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 亚洲精品免费观看 | 91精品国模一区二区三区 | 99久久久久久久久 | 免费99视频 | 亚洲激情一区二区三区 | 一区二区三区视频 | 日韩精品亚洲专区在线观看 | 免费精品久久久久久中文字幕 | 欧美大片黄 | 欧美a区 | 国产成人在线视频免费观看 | 国产日产精品一区二区三区四区 | 久久99精品久久久久久青青日本 | 中文av字幕 | 日本天堂一区 | 免费爱爱视频 | 欧美一区在线视频 | 521av网站 | 91看片网| www.久| 成人在线小视频 | 一级特黄网站 | 亚洲国产精品日韩av不卡在线 | 久久精品国产a三级三级三级 | av福利网 | 超碰免费在线观看 | 久久这里只有精品首页 | 91精品国产91久久综合桃花 | 亚洲国产精品久久久久婷婷老年 | 欧美男人天堂 | 欧美a∨ | 一区欧美| 国产精品久久久久无码av | 国产精品一区二区三区四区 | 久草精品在线 | 亚洲黄色片免费观看 | 久久黄网 | 成人高清视频在线观看 | 久久久久久久久久爱 | 成人福利网站 | 国产在线视频一区 |