摘要:引言本周精讀的文章是。精讀總的來說,雖然拆分子倉庫拆分子包是進(jìn)行項(xiàng)目隔離的天然方案,但當(dāng)倉庫內(nèi)容出現(xiàn)關(guān)聯(lián)時,沒有任何一種調(diào)試方式比源碼放在一起更高效。前端精讀幫你篩選靠譜的內(nèi)容。
1. 引言
本周精讀的文章是 The many Benefits of Using a Monorepo。
現(xiàn)在介紹 Monorepo 的文章很多,可以分為如下幾類:直接介紹 Lerna API 的;介紹如何從獨(dú)立倉庫遷移到 Lerna 的;通過舉例子說明 Monorepo 重要性的。
本文屬于第三種,從 Android 與 IOS 的開發(fā)故事說明了 Monorepo 的重要性。
筆者之所以選擇這篇文章,不是因?yàn)槠涔适聦懙暮茫钦J(rèn)可這種具有普適性的解決思路。畢竟 Lerna 作為 Monorepo 的實(shí)現(xiàn)之一也并不盡善盡美,而不同場景對 Monorepo 依賴的原因、功能也有所不同,所以希望借這篇文章,從理論上解釋清楚為什么會產(chǎn)生 Monorepo,以及 Monorepo 可以解決哪些問題,這樣在工作遇到問題時,才能想清楚自己要的是什么。
2. 概述作者的一個項(xiàng)目是 PDF 服務(wù),簡稱 PSPDFKit,需要同時兼顧 Android 與 IOS 平臺,項(xiàng)目的發(fā)展經(jīng)歷了如下幾個階段。
初始階段在 2011 到 2013 年間,PSPDFKit 僅支持 IOS 平臺,但最終項(xiàng)目需要支持 Android,因此開了一個新倉庫放置 Android 代碼。Android 倉庫的代碼不僅在 UI 上不同,同時解析 PDF 文檔的核心代碼也不同,這是因?yàn)?IOS 平臺上使用內(nèi)置 PDF 渲染引擎同時做了一些業(yè)務(wù)拓展,但使用的 OC 代碼無法在 Android 使用。
最終新建了兩個倉庫 PSPDFKit-Android 與 Core 。
倉庫 Core 中代碼依賴 Android 平臺 JNI 的支持,所以并不能實(shí)現(xiàn) Core 一處修改,兩處都生效的愿望,而我們又希望兩邊功能始終兼容,且減少分支過多帶來的潛在的沖突,因此花了很久才意識到應(yīng)該將這兩個倉庫合并起來。
考慮使用 Monorepo由于 Android 的整套流程自己控制的,因此總是可以快速修復(fù)用戶提出的 BUG,然而 IOS 提供的 CGPDF 總會遇上各種問題。所以在 2014 年,我們開啟了一個龐大的項(xiàng)目,重寫 IOS 的 Core 庫。有三中方式可供選擇:
在 IOS 代碼中引用 PSPDFKit-Android。
將 PSPDFKit-Android 提取到 Core 倉庫中并分別維護(hù)。
將 IOS 與 Android 代碼合并到一個倉庫中。
經(jīng)過討論,最終作者的團(tuán)隊(duì)選擇了第三種方案,因此目錄結(jié)構(gòu)類似如下:
- ios-platform - android-platform - core特例
Web 與后臺服務(wù)代碼一直是一個特例,我們認(rèn)為這些內(nèi)容相對獨(dú)立,所以沒有將其代碼放置到 Monorepo 中。
直到一年后,開始探索 WebAssembly 時,PSPDFKit-web 模塊就出現(xiàn)了,因?yàn)榭梢岳?WebAssembly 將 Core 的代碼編譯并在 Web 平臺使用,因此 Core 倉庫與 Web 倉庫的關(guān)系變得非常緊密,最終,我們將 Web、Server 也都遷移到 Monorepo 中了。
問題Monorepo 瑕不掩瑜,但作者還是列舉了一些缺陷。
由于源碼在一起,倉庫變更非常常見,存儲空間也變得很大,甚至幾 GB,CI 測試運(yùn)行時間也會變長。即便如此,團(tuán)隊(duì)中任何人都不想回到 git submodules 多倉庫的方式。
3. 精讀總的來說,雖然拆分子倉庫、拆分子 NPM 包(For web)是進(jìn)行項(xiàng)目隔離的天然方案,但當(dāng)倉庫內(nèi)容出現(xiàn)關(guān)聯(lián)時,沒有任何一種調(diào)試方式比源碼放在一起更高效。
工程化的最終目的是讓業(yè)務(wù)開發(fā)可以 100% 聚焦在業(yè)務(wù)邏輯上,那么這不僅僅是腳手架、框架需要從自動化、設(shè)計(jì)上解決的問題,這涉及到倉庫管理的設(shè)計(jì)。
一個理想的開發(fā)環(huán)境可以抽象成這樣:
“只關(guān)心業(yè)務(wù)代碼,可以直接跨業(yè)務(wù)復(fù)用而不關(guān)心復(fù)用方式,調(diào)試時所有代碼都在源碼中。”
在前端開發(fā)環(huán)境中,多 Git Repo,多 Npm 則是這個理想的阻力,它們導(dǎo)致復(fù)用要關(guān)心版本號,調(diào)試需要 Npm Link。
另外對于多倉庫的缺點(diǎn),文中還有一些沒有提到的因素,這里一并列舉出來:
管理、調(diào)試?yán)щy
多個 git 倉庫管理起來天然是麻煩的。對于功能類似的模塊,如果拆成了多個倉庫,無論對于多人協(xié)作還是獨(dú)立開發(fā),都需要打開多個倉庫頁面。
雖然 vscode 通過 Workspaces 解決多倉庫管理的問題,但在多人協(xié)作的場景下,無法保證每個人的環(huán)境配置一致。
對于共用的包通過 Npm 安裝,如果不能接受調(diào)試編譯后的代碼,或每次 npm link 一下,就沒有辦法調(diào)試依賴的子包。
分支管理混亂
假如一個倉庫提供給 A、B 兩個項(xiàng)目用,而 B 項(xiàng)目優(yōu)先開發(fā)了功能 b,無法與 A 項(xiàng)目兼容,此時就要在這個倉庫開一個 feature/b 的分支支持這個功能,并且在未來合并到主干同步到項(xiàng)目 A。
一旦需要開分支的組件變多了,且之間出來依賴關(guān)聯(lián),分支管理復(fù)雜度就會呈指數(shù)上升。
依賴關(guān)系復(fù)雜
獨(dú)立倉庫間組件版本號的維護(hù)需要手動操作,因?yàn)樵创a不在一起,所以沒有辦法整體分析依賴,自動化管理版本號的依賴。
三方依賴版本可能不一致
一個獨(dú)立的包擁有一套獨(dú)立的開發(fā)環(huán)境,難以保證子模塊的版本和主項(xiàng)目完全一直,就存在運(yùn)行結(jié)果不一致的風(fēng)險。
占用總空間大
正常情況下,一個公司的業(yè)務(wù)項(xiàng)目只有一個主干,多 git repo 的方式浪費(fèi)了大量存儲空間重復(fù)安裝比如 React 等大型模塊,時間久了可能會占用幾十 GB 的額外空間,對于沒有外接硬盤的同學(xué)來說,定期清理不用的項(xiàng)目下 node_modules 也是一件麻煩事。
不利于團(tuán)隊(duì)協(xié)作
一個大項(xiàng)目可能會用到數(shù)百個二方包,不同二方包的維護(hù)頻率不同,權(quán)限不同,倉庫位置也不同,主倉庫對它們的依賴方式也不同。
一旦其中一個包進(jìn)行了非正常改動,就會影響到整個項(xiàng)目,而我們精力有限,只盯著主倉庫,往往會栽在不起眼的二方包發(fā)布上。
所以對于一個非常復(fù)雜,又具有技術(shù)挑戰(zhàn)的大型系統(tǒng)在協(xié)作人員多的情況下出現(xiàn)問題的概率非常大,需要通過 Review 制度避免錯誤的發(fā)生,那么將所有相關(guān)的源碼聚合在一個倉庫下,是更好管理的。
理想 monorepo 的設(shè)計(jì)參考 Lerna 的規(guī)范,以 packages 作為子模塊根文件夾,筆者設(shè)計(jì)一個理想的 monorepo 結(jié)構(gòu):
. ├── packages │ ├─ module-a │ │ ├─ src # 模塊 a 的源碼 │ │ └─ package.json # 自動生成的,僅模塊 a 的依賴 │ └─ module-b │ ├─ src # 模塊 b 的源碼 │ └─ package.json # 自動生成的,僅模塊 b 的依賴 ├── tsconfig.json # 配置文件,對整個項(xiàng)目生效 ├── .eslintrc # 配置文件,對整個項(xiàng)目生效 ├── node_modules # 整個項(xiàng)目只有一個外層 node_modules └── package.json # 包含整個項(xiàng)目所有依賴
所有全局配置文件只有一個,這樣不會導(dǎo)致 IDE 遇到子文件夾中的配置文件,導(dǎo)致全局配置失效或異常。node_modules 也只有一個,既保證了項(xiàng)目依賴的一致性,又避免了依賴被重復(fù)安裝,節(jié)省空間的同時還提高了安裝速度。
兄弟模塊之間通過模塊 package.json 定義的 name 相互引用,保證模塊之間的獨(dú)立性,但又不需要真正發(fā)布或安裝這個模塊,通過 tsconfig.json 的 paths 與 webpack 的 alias 共同實(shí)現(xiàn)虛擬模塊路徑的效果。
再結(jié)合 Lerna 根據(jù)聯(lián)動發(fā)布功能,使每個子模塊都可以獨(dú)立發(fā)布。
4. 總結(jié)Lerna 是業(yè)界知名度最高的 Monorepo 管理工具,功能完整。但由于通用性要求非常高,需要支持任意項(xiàng)目間 Monorepo 的組合,因此在 packages 文件夾下的配置文件還是與獨(dú)立倉庫保持一致,這樣在 TS 環(huán)境下會造成配置截?cái)嗟膯栴}。同時包之間的引用也通過更通用的 symlink 完成,這導(dǎo)致了還是要在子模塊目錄存在 node_modules 文件夾,而且效果依賴項(xiàng)目初始化命令。
如果加一些限定條件,比如基于 Webpack + Typescript 環(huán)境的 Monorepo,可以換一套思路,利用這些工具自身運(yùn)行時功能,減少更多模版代碼或配置文件,進(jìn)一步提升 Monorepo 的效果。
對于別名映射,對 symlink 與 alias 進(jìn)行對比:
symlink: 更通用,適合任何構(gòu)建器。但需要初始化,且在每個關(guān)聯(lián)模塊下新增 node_modules 文件夾。
alias: 限定構(gòu)建器。但不需要初始化,不新增文件夾,甚至可以運(yùn)行時動態(tài)修改別名配置。
可見如果限定了構(gòu)建器,別名映射可以做得更輕量,且無需初始化。
今天的問題是,你的項(xiàng)目需要使用 Monorepo 嗎?你對 Monorepo 有其他要求嗎?
討論地址是:精讀《Monorepo 的優(yōu)勢》 · Issue #151 · dt-fe/weekly
如果你想?yún)⑴c討論,請 點(diǎn)擊這里,每周都有新的主題,周末或周一發(fā)布。前端精讀 - 幫你篩選靠譜的內(nèi)容。
關(guān)注 前端精讀微信公眾號
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/109444.html
摘要:目前最常見的解決方案是和的特性。具體的使用方法移步官網(wǎng)而使用作為包管理器的同學(xué),可以在中以字段聲明,就會以的方式管理。這樣的話,無論你的包管理器是還是,都能發(fā)揮的優(yōu)勢要是包管理是,就會把依賴安裝交給處理。 最近我接手了一個項(xiàng)目,代碼量比較大、有點(diǎn)復(fù)雜。倉庫 clone 下來代碼有 50+ MB,npm install 安裝完體積飚到了近 2GB …… 熟悉了一下,這個項(xiàng)目比較復(fù)雜,采用...
我們先說下 Yarn workspace 首先Yarn workspace 是 Yarn 提供的 monorepo 下,管理依賴的機(jī)制。這就說主要對代碼倉庫下,多個 package 的依賴,進(jìn)行管理:將共同的依賴,做 hosting(提升)。前述這樣就可以有效的防止 package 中的包重復(fù)安裝。 workspace 機(jī)制,會在根目錄下,統(tǒng)一安裝依賴到 node_module,并生成...
摘要:需要說明是的,這里說的專家不再關(guān)心細(xì)節(jié),不代表成為專家后學(xué)不會細(xì)節(jié),也不代表專家不了解細(xì)節(jié)。本文將從三個點(diǎn)去解釋,為什么專家看上去越來越原理技術(shù)細(xì)節(jié)。試想一個不能理解業(yè)務(wù)要做什么的人,即便懂得再多技術(shù)細(xì)節(jié),對業(yè)務(wù)也是沒有價值的。1. 引言 本周的精讀是有感而發(fā)。 筆者接觸前端已有八年,觀察了不少前端大牛的發(fā)展路徑,發(fā)現(xiàn)成功的人都具有相似的經(jīng)歷: 初期技術(shù)熱情極大 -> 大量標(biāo)志性技術(shù)項(xiàng)目 -...
摘要:最近發(fā)現(xiàn)公司一個項(xiàng)目的目錄組織挺奇怪的,所有的子項(xiàng)目都放在了目錄里,還有這種騷操作特意查了下資料,發(fā)現(xiàn)是一種比較流行的項(xiàng)目管理模式。 最近發(fā)現(xiàn)公司一個項(xiàng)目的目錄組織挺奇怪的,所有的子項(xiàng)目都放在了packages目錄里,還有這種騷操作?特意查了下資料,發(fā)現(xiàn)是一種比較流行的monorepo項(xiàng)目管理模式。近幾年比較火的React,Vue,Babel都是用的這種模式: showImg(http...
摘要:指向一個文件,這個文件描述了所有的字段以及約束。其中一個項(xiàng)目為一個子項(xiàng),如為一個項(xiàng)目,在創(chuàng)建時自動生成。整個也有此字段,默認(rèn)生效于所有。默認(rèn)項(xiàng)目,當(dāng)使用一些命令沒有指定項(xiàng)目名稱時,默認(rèn)指向的項(xiàng)目。 ... 在Angular CLI 6+的版本后,原先的angular-cli.json就被換成了angular.json,而其中里面的字段變化挺大了,如果不了解基本的組成,或者直接把老版本的...
閱讀 738·2021-11-11 16:54
閱讀 3053·2021-09-26 09:55
閱讀 2003·2021-09-07 10:20
閱讀 1198·2019-08-30 10:58
閱讀 1039·2019-08-28 18:04
閱讀 698·2019-08-26 13:57
閱讀 3584·2019-08-26 13:45
閱讀 1150·2019-08-26 11:42