俯瞰 Monorepo诫硕,別一番風(fēng)景!

banner.jpeg

寫在最前

本故事簡要地介紹了 Monorepo 的 What 和 Why,重點篇幅在于搭建一個好用的 Monorepo 工程時應(yīng)該考慮的點刊侯≌掳欤可以作為你在選擇工具時的條件,也可以作為你在搭建 Monorepo 工程時查漏補缺的參考滨彻。希望這對你有所幫助藕届,哪怕只是一點點 ^O^

“在這個 AI 內(nèi)容生成泛濫的時代,依然有一批人"傻傻"堅持原創(chuàng)亭饵,如果您能讀到最后休偶,還請點贊或收藏或關(guān)注支持下我唄,感謝 ( ̄︶ ̄)↗”


What辜羊?- 獨立和關(guān)系

丹尼爾:蛋兄踏兜,好久不見,今天我們來聊聊 Monorepo 吧八秃!

蛋先生:Monorepo碱妆?就是把多個項目放在同一個倉庫里的那種嗎?

丹尼爾:是呀昔驱,感覺上就是把一堆代碼庫簡單地堆在一起

蛋先生:你說得不太準(zhǔn)確疹尾,我覺得 Monorepo 最重要的是倆關(guān)鍵字:獨立和關(guān)系

丹尼爾:怎么說?

蛋先生:獨立是指這些項目本身是完整的骤肛,一般都擁有開發(fā)纳本、測試,發(fā)布等完整的生命周期腋颠,而不是簡單的包含一堆代碼文件的文件夾

丹尼爾:哦繁成,這個我明白了。那關(guān)系呢秕豫?

蛋先生:關(guān)系是指這些項目之間存在一定的關(guān)聯(lián)朴艰,比如它們屬于同一個業(yè)務(wù)領(lǐng)域观蓄,或是有依賴關(guān)系,而不是毫無關(guān)聯(lián)地硬堆在一起

丹尼爾:懂了祠墅!


Why侮穿?- 更好地協(xié)作

丹尼爾:那用這個 Monorepo 有什么好處呢?我以前一個項目一個倉庫不也挺好的嗎毁嗦?

蛋先生:這里科普一下亲茅,一個項目一個倉庫有一個專用的名詞叫 Polyrepo。我認(rèn)為 Monorepo 最關(guān)鍵的好處在于項目與項目之間的協(xié)作

丹尼爾:怎么說呢狗准?

蛋先生:比如共享代碼以減少重復(fù)工作方面克锣。當(dāng)你在開發(fā)應(yīng)用 B 時,如果發(fā)現(xiàn)應(yīng)用 A 中已經(jīng)實現(xiàn)了很多相似的邏輯腔长,那么你需要把共享邏輯抽取到一個獨立的庫 α袭祟,然后修改應(yīng)用 A 和應(yīng)用 B 以依賴于庫 α,因為這一切都在同一個倉庫中完成捞附,非常方便巾乳,操作成本較低

丹尼爾:確實,如果采用 Polyrepo 的方式鸟召,我得新建一個倉庫胆绊,把共享邏輯抽取出來,然后通過本地 link 的方式來開發(fā)調(diào)試欧募。一切就緒后压状,還得發(fā)布到 npm,再在應(yīng)用 A 和應(yīng)用 B 中安裝依賴跟继。而且每次修改都需要重復(fù)這個過程种冬,真是麻煩

蛋先生:再比如庫修改可能導(dǎo)致項目不穩(wěn)定方面。當(dāng)一個被依賴的庫進(jìn)行迭代升級時还栓,特別是有大的變更時碌廓,如果沒有及時溝通以采取相應(yīng)的措施,就會導(dǎo)致各種問題剩盒,潛在的風(fēng)險非常大

丹尼爾:Monorepo 不會有這個問題嗎谷婆?

蛋先生:在 Monorepo 中修改是原子的,即當(dāng)你修改庫 α 時辽聊,同倉庫的應(yīng)用 A 和應(yīng)用 B 都能及時感知到變更纪挎。例如,你刪除了某個接口的入?yún)?shù)跟匆,應(yīng)用 A 和應(yīng)用 B 會立刻報錯异袄,這樣就能及時發(fā)現(xiàn)并解決潛在風(fēng)險

丹尼爾:這樣確實挺棒的

蛋先生:最根本的原因是 Polyrepo 帶來了隔離,而隔離影響了協(xié)作玛臂。Monorepo 的目標(biāo)則是為了更好地協(xié)作烤蜕。就像部門間協(xié)作和部門內(nèi)協(xié)作封孙,顯然同一個部門內(nèi)的協(xié)作效率更高,溝通成本也更低

丹尼爾:一語中的讽营!


How虎忌?- 舒適地開發(fā)

? 初始化階段 - 腳手架

丹尼爾:那采用 Monorepo 的形式來組織項目,我應(yīng)該怎么做呢橱鹏?

蛋先生:我們一起來走一走應(yīng)用開發(fā)的歷程膜蠢,看看需要有哪些工作吧

丹尼爾:好啊

蛋先生:有兩種開局方式。一種是全新開始莉兰,這樣的話你需要一個能生成 Monorepo 大倉的腳手架

丹尼爾:恩挑围,很體貼

蛋先生:不過這種情況發(fā)生的概率較低,通常是一次性的糖荒。更常見的是在已有的 Monorepo 倉庫中增加新項目杉辙。這是經(jīng)常需要做的事情,所以我們可以提供多種腳手架代碼生成器來快速初始化一個項目寂嘉,比如創(chuàng)建 TS 工具庫項目奏瞬、React 應(yīng)用項目,或者是 TS CLI 項目等等

丹尼爾:確實泉孩,常用的項目類型是可以枚舉出來的。有了這些工具并淋,后續(xù)增加項目就輕松多了寓搬,想想就很爽!那另一種開局呢县耽?

? 初始化階段 - 依賴安裝

蛋先生:另一種開局是你準(zhǔn)備在一個已存在的 Monorepo 大倉上進(jìn)行開發(fā)工作句喷。這時,你的第一件事應(yīng)該是安裝依賴兔毙,對嗎唾琼?

丹尼爾:恩,沒錯

蛋先生:不過澎剥,大倉里可能有很多項目锡溯,你總不能一個一個項目進(jìn)行安裝依賴吧,所以需要有一個可以一次性安裝全部項目依賴的能力

丹尼爾:對啊哑姚,我可不想把時間浪費在一個個項目里 cd 來 cd 去的

? 開發(fā)階段 - 任務(wù)編排

蛋先生:無論哪種開局祭饭,接下來都是進(jìn)入到開發(fā)階段了。假設(shè)你在開發(fā)應(yīng)用 A叙量,而應(yīng)用 A 依賴庫 α倡蝙,那么你是不是得先確保庫 α 有可用的構(gòu)建產(chǎn)物?

丹尼爾:是啊绞佩,所以第一步就是得知道應(yīng)用 A 依賴了哪些同倉庫中的其他庫寺鸥,并且提前對它們進(jìn)行構(gòu)建猪钮。但如果依賴關(guān)系比較復(fù)雜,就難搞了

蛋先生:正是如此胆建。所以烤低,我們希望能夠不用手動處理這些依賴,只要對應(yīng)用 A 進(jìn)行構(gòu)建眼坏,就能自動處理它所依賴的所有庫的構(gòu)建

丹尼爾:那就太好了拂玻!

蛋先生:這就需要任務(wù)編排了。我們可以配置任務(wù)之間的協(xié)作關(guān)系宰译,比如在執(zhí)行某個任務(wù)之前檐蚜,需要先執(zhí)行哪些任務(wù),這些任務(wù)是串行還是并行執(zhí)行等等

丹尼爾:哦沿侈,任務(wù)編排還真好用

? 開發(fā)階段 - 一致命令

蛋先生:好了闯第,萬事俱備,你可以開始本地開發(fā)調(diào)試了

丹尼爾:哦缀拭,那我先看看項目的 README咳短,找找本地開發(fā)調(diào)試的指引

蛋先生:不用那么麻煩,直接執(zhí)行 dev 命令吧蛛淋。無論你是在開發(fā)應(yīng)用項目還是庫項目咙好,無論是用 JavaScript 還是 Java,開發(fā)就運行 dev褐荷,構(gòu)建就運行 build勾效,測試就運行 test,等等叛甫。這樣你就不會有任何心智負(fù)擔(dān)

丹尼爾:哈哈层宫,老早就想這樣了

? 開發(fā)階段 - 影響檢測

蛋先生:開發(fā)過程中,你發(fā)現(xiàn)依賴的庫 α 提供的接口有點小問題其监,現(xiàn)在你準(zhǔn)備對應(yīng)用 A 所依賴的庫 α 進(jìn)行修改

丹尼爾:哦萌腿,反正都是在同一個倉庫,修改起來挺方便的

蛋先生:但我們得確保這個改動不會影響到依賴該庫的其他項目抖苦。至少在我們可控的范圍內(nèi)毁菱,比如同一倉庫中依賴該庫的其他項目。所以睛约,我們需要一種自動檢測機(jī)制來識別哪些項目受到了影響鼎俘,然后對這些受影響的項目進(jìn)行單元測試等操作,以確保它們的穩(wěn)定性

? 開發(fā)階段 - 依賴分析

丹尼爾:蛋兄果然很謹(jǐn)慎啊

蛋先生:咳咳~辩涝。其實贸伐,這一切都需要借助依賴分析能力。當(dāng) Monorepo 的規(guī)模越來越大時怔揩,依賴關(guān)系也會變得越來越復(fù)雜捉邢。我們需要通過依賴關(guān)系圖,清晰地了解各項目之間的聯(lián)系和影響伏伐,從而做到對項目狀況了如指掌

? 開發(fā)階段 - 依賴權(quán)限

蛋先生:你現(xiàn)在是庫 α 的主要負(fù)責(zé)人。有一天藐翎,你發(fā)現(xiàn)了一些并不想對外暴露的 API 被倉庫內(nèi)的其他項目使用,結(jié)果你在修改這些 API 時就不得不考慮對這些項目的影響

丹尼爾:啊吝镣,雖然我是聲明了 export,但這只是為了庫內(nèi)部的其他代碼使用末贾≌⒗#可其他項目卻可以通過深層導(dǎo)入來依賴這些 API

蛋先生:嗯,所以我們需要在工程層面上建立機(jī)制拱撵,防止這些 API 被誤依賴

? 開發(fā)階段 - 修改權(quán)限

蛋先生:庫 α 雖說是由你主要負(fù)責(zé)的辉川,但是由于代碼庫是放在一起的,其他擁有大倉權(quán)限的同學(xué)也就有權(quán)限進(jìn)行修改拴测。但是你并不希望他們隨意修改庫 α 的代碼乓旗,至少要經(jīng)過你的同意

丹尼爾:是啊是啊,這真的很重要集索!

蛋先生:所以我們需要引入類似 OWNER 的機(jī)制寸齐,對這些修改權(quán)限進(jìn)行限制浇雹,以確保代碼的穩(wěn)定性和一致性

? CI 階段 - 本地計算緩存

“注:CI 階段的能力谆刨,不僅僅只用于 CI惊畏,開發(fā)階段也是可以享用,只是為了劇情需要這么安排而已”

蛋先生:好了蛹含,項目修改完畢,提交塞颁。CI 開始工作了浦箱,然后你發(fā)現(xiàn)每次 CI 構(gòu)建都非常慢

丹尼爾:嗯,我加點戲哈祠锣。我喝了一杯咖啡酷窥,再回來一看,好家伙伴网,CI 還在跑蓬推。這樣可不行,得優(yōu)化性能了澡腾,不然我快要崩潰了

蛋先生:好吧沸伏,這戲加得... 回到正題糕珊。這是因為該項目直接或間接依賴了同一倉庫中的好幾個其他庫。所以毅糟,每次構(gòu)建實際上都需要構(gòu)建多個項目红选。優(yōu)化性能的思路之一就是減少不必要的計算,增量執(zhí)行就變得非常重要姆另。因此喇肋,我們需要引入本地計算緩存,緩存計算結(jié)果迹辐,避免對沒有修改的庫進(jìn)行重復(fù)構(gòu)建

丹尼爾:本地緩存蝶防,我懂

? CI 階段 - 分布式任務(wù)執(zhí)行

蛋先生:性能優(yōu)化的另一個思路是加速必要的計算

丹尼爾:昨加速捏?

蛋先生:可以采用分布式任務(wù)執(zhí)行右核。將一些可以并發(fā)執(zhí)行的任務(wù)分配到不同的服務(wù)器上并行處理慧脱,實現(xiàn)在更短的時間內(nèi)完成任務(wù)。這樣做雖然會增加一定的成本菱鸥,但對于大型項目來說躏鱼,是非常有效的性能提升方案

丹尼爾:聽上去好高級的樣子

? CI 階段 - 遠(yuǎn)程計算緩存

蛋先生:雖然使用了本地緩存染苛,但每個服務(wù)器都需要先構(gòu)建一次才能生成本地緩存。如果我們把緩存的位置移到遠(yuǎn)程云端躯概,是不是就可以進(jìn)一步優(yōu)化性能呢娶靡?

丹尼爾:Nice! 這樣就可以共享緩存了

? 發(fā)布階段

蛋先生:最后看锉,我們需要把庫 α 發(fā)布到 npm 上去伯铣,因為它提供的功能非常通用,不僅僅局限于當(dāng)前的項目倉庫內(nèi)

丹尼爾:那就趕緊發(fā)布吧焚鲜!

蛋先生:發(fā)布階段,根據(jù)需要郑兴,利用任務(wù)編排就可以了


新的問題

丹尼爾:聽起來 Monorepo 灰常好啊情连,都使用這種方式得了

蛋先生:Monorepo 確實突破了 Polyrepo 的隔離問題览效,但這樣開放的結(jié)構(gòu)也帶來了讀權(quán)限的問題锤灿。如果你的大倉中的部分項目需要由第三方團(tuán)隊來開發(fā),但你又不希望他們能看到其它項目的內(nèi)容螃诅,那么 Monorepo 就無法解決這個問題了

丹尼爾:啊术裸,那怎么辦呢亭枷?

蛋先生:這種情況下叨粘,你可以考慮將這些項目作為 git submodule 分離出去升敲。這樣一來,大倉中的其它項目仍然可以在工作空間內(nèi)直接依賴這些分離出去的 git submodule 項目

丹尼爾:那有啥需要注意的地方嗎苇羡?

蛋先生:要注意的是,git submodule 的項目就不能通過工作空間直接依賴大倉中的其它項目了锦茁,它們需要通過 npm 中央倉庫來進(jìn)行依賴管理

丹尼爾:好咧码俩,這一聊,天色已晚

蛋先生:嗯笨篷,今天就先聊到這里率翅,就此別過吧

丹尼爾:拜拜!


寫在最后

為什么不直接寫一個使用某個工具(比如 Turborepo)來搭建 Monorepo 項目的教程呢腺晾?因為我相信聰明的你悯蝉,只需要閱讀官方文檔鼻由,就可以輕松上手了

不同工具工作方式有所不同蕉世,但都是圍繞 Monorepo 來提供能力的窟感。我們應(yīng)以不變應(yīng)萬變,掌握表面之下的東西哈误,這樣才能更加靈活地應(yīng)對各種變化

“親們蜜自,都到這了重荠,要不虚茶,點贊或收藏或關(guān)注支持下我唄 o( ̄▽ ̄)d”

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市婆殿,隨后出現(xiàn)的幾起案子婆芦,更是在濱河造成了極大的恐慌,老刑警劉巖肠鲫,帶你破解...
    沈念sama閱讀 221,576評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件导饲,死亡現(xiàn)場離奇詭異帜消,居然都是意外死亡泡挺,警方通過查閱死者的電腦和手機(jī)娄猫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評論 3 399
  • 文/潘曉璐 我一進(jìn)店門媳溺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來悬蔽,“玉大人捉兴,你說我怎么就攤上這事倍啥∷渎疲” “怎么了氮趋?”我有些...
    開封第一講書人閱讀 168,017評論 0 360
  • 文/不壞的土叔 我叫張陵剩胁,是天一觀的道長摧冀。 經(jīng)常有香客問我索昂,道長椒惨,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,626評論 1 296
  • 正文 為了忘掉前任康谆,我火速辦了婚禮沃暗,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘嚼黔。我一直安慰自己,他們只是感情好盛撑,可當(dāng)我...
    茶點故事閱讀 68,625評論 6 397
  • 文/花漫 我一把揭開白布狮荔。 她就那樣靜靜地躺著轴合,像睡著了一般受葛。 火紅的嫁衣襯著肌膚如雪总滩。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,255評論 1 308
  • 那天巡雨,我揣著相機(jī)與錄音闰渔,去河邊找鬼。 笑死铐望,一個胖子當(dāng)著我的面吹牛冈涧,可吹牛的內(nèi)容都是我干的茂附。 我是一名探鬼主播,決...
    沈念sama閱讀 40,825評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼督弓,長吁一口氣:“原來是場噩夢啊……” “哼营曼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起愚隧,我...
    開封第一講書人閱讀 39,729評論 0 276
  • 序言:老撾萬榮一對情侶失蹤蒂阱,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體妈踊,經(jīng)...
    沈念sama閱讀 46,271評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡赘风,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,363評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了肪虎。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片香嗓。...
    茶點故事閱讀 40,498評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡锌雀,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 36,183評論 5 350
  • 正文 年R本政府宣布抑党,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜殴俱,卻給世界環(huán)境...
    茶點故事閱讀 41,867評論 3 333
  • 文/蒙蒙 一询筏、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸谣旁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間除破,已是汗流浹背光坝。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 48,906評論 3 376
  • 正文 我出身青樓绵患,卻偏偏與公主長得像筏勒,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子邪媳,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,507評論 2 359

推薦閱讀更多精彩內(nèi)容