1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        Monorepo 的過去、現(xiàn)在、和未來

        共 5617字,需瀏覽 12分鐘

         ·

        2022-05-15 19:28

        大廠技術(shù)??高級前端??Node進(jìn)階

        點(diǎn)擊上方?程序員成長指北,關(guān)注公眾號

        回復(fù)1,加入高級Node交流群

        Monorepo 的過去、現(xiàn)在、和未來

        最近在準(zhǔn)備 QCon+ Monorepo 專題 的分享,突然發(fā)現(xiàn) Monorepo 方案其實(shí)一直在不斷演進(jìn),每一個(gè)階段都解決了上一個(gè)時(shí)代的數(shù)個(gè)核心問題。因此,這里簡單地寫一篇文章分享下 Monorepo 方案至今的進(jìn)化路線(真的非常非常之簡單)。

        刀耕火種:偽 Monorepo

        在這一時(shí)代,lerna 等方案還未出現(xiàn),除了各大公司如 Google、Meta 自建的巨型 Monorepo 方案以外,外界開發(fā)者常用的 Monorepo 方案就是將所有的項(xiàng)目直接放進(jìn)一個(gè)文件夾/倉庫下,比如這樣:

        -?web-app
        -?mobile-app
        -?web-admin
        -?miniprogram
        -?node-server
        -?node-bff
        -?rpc
        -?...

        嚴(yán)格來說,這一組織方式并不屬于 Monorepo,它只是把所有項(xiàng)目放在一起,每個(gè)項(xiàng)目自己安裝一次依賴,如果項(xiàng)目之間存在依賴關(guān)系,需要自己手動 link 。項(xiàng)目的開發(fā)、發(fā)布、CI/CD 等同樣會讓你懷疑人生:在五個(gè) Terminal 之間來回切換一個(gè)個(gè)啟動項(xiàng)目、手動改版本然后按照依賴項(xiàng)目-主體項(xiàng)目的順序發(fā)布、足夠你開幾把游戲的 CI/CD 時(shí)間。

        使用這種方式和使用 Polyrepo,即一個(gè)項(xiàng)目一個(gè)倉庫的管理方式并沒有什么區(qū)別,因此我們這里稱它是偽·Monorepo。

        要想成為一個(gè)真正意義上的 Monorepo,我理解至少有兩點(diǎn)入門門檻需要滿足:

        • 項(xiàng)目之間存在自治的依賴關(guān)系。這里的自治,指的是不需要人工按照拓?fù)渑判蜻M(jìn)行項(xiàng)目之間的 link,而是有一定的自動化程度。比如在上面的例子里如果我們提供一個(gè) script/link.js 文件,那么也可以勉強(qiáng)認(rèn)為滿足了這一點(diǎn)。項(xiàng)目依賴關(guān)系的構(gòu)建主要是在開發(fā)時(shí)起到作用,如你可以直接在主項(xiàng)目中 link 子項(xiàng)目,然后對子項(xiàng)目的修改能夠立刻在主項(xiàng)目中產(chǎn)生效果。
        • 工程子項(xiàng)目的依賴(node_modules)之間并非是完全獨(dú)立的,對于版本一致的三方包,只應(yīng)該被安裝一次。以及,對于對單例有要求的依賴包如 React,版本之間存在沖突又可能并存的包如 Webpack,ESBuild / SWC / NodeSass 這一類非 JavaScript 編寫的依賴包,都需要得到妥善的處理。

        總結(jié)一下其實(shí)就是這么兩個(gè)問題:

        • 項(xiàng)目依賴關(guān)系構(gòu)建
        • 工程依賴處理

        實(shí)際上,這一刀耕火種階段的 Monorepo 方案其實(shí)現(xiàn)在很難見到使用了,畢竟這么做還不如拆分成一個(gè)項(xiàng)目一個(gè)倉庫的 Polyrepo 架構(gòu)。很快,我們迎來了新的、面向社區(qū)的 Monorepo 方案,它們很好地解決了上面的兩個(gè)問題,為我們帶來了真正的 Monorepo 開發(fā)體驗(yàn)。

        面向社區(qū):Google 、Meta、Microsoft 等公司的 Monorepo 方案其實(shí)此時(shí)還未開源出來,或者說并不適合社區(qū)的普通個(gè)人開發(fā)者 / 團(tuán)隊(duì)來使用。

        工業(yè)革命:Task Runner 與 Workspace

        現(xiàn)在,應(yīng)該很少有沒有聽說過 lerna 的前端同學(xué)了,這里也不準(zhǔn)備介紹它的使用方式。如果還未了解,你可以閱讀官方文檔。

        lerna 這一方案要解決的核心問題,其實(shí)正對應(yīng)著上面列出的兩個(gè)痛點(diǎn)。

        對于項(xiàng)目依賴關(guān)系構(gòu)建,lerna 在初始化項(xiàng)目時(shí)(lerna bootstrap)會自動地為存在依賴關(guān)系的子項(xiàng)目創(chuàng)建 link,同時(shí)也支持了依賴關(guān)系創(chuàng)建(lerna add)、基于依賴關(guān)系的版本升級與發(fā)布(lerna version、lerna publish)等。lerna 其實(shí)還支持 --include-dependents 這一類全局選項(xiàng),來快速從一個(gè)項(xiàng)目入口出發(fā),構(gòu)建基于其上游依賴或下游依賴的 Project Graph(更詳細(xì)地說,屬于有向無環(huán)圖) ,但確實(shí)用著比較麻煩(include / exclude,dependents / dependencies)。

        實(shí)際上,這里的依賴關(guān)系,其最簡本質(zhì)也就是將一個(gè)有向無環(huán)圖(DAG)進(jìn)行拓?fù)渑判虻倪^程。對于部分 script,必須嚴(yán)格按照這一順序來執(zhí)行(如 start、build、test 等),否則會出現(xiàn)依賴引用的產(chǎn)物版本不一致等問題。

        如果你只是需要任務(wù)調(diào)度能力,而不需要 lerna 的其他能力,可以使用一些輕量的替代方案,如 ultra-runner 等。

        對于工程依賴,lerna 以及后面的 yarn workspace 都進(jìn)行了更好的處理,如共同依賴地提升(hoist)等。當(dāng)然,依賴提升等特性又帶來了新問題,如幽靈依賴(Phantom Dependencies)使得包可以去訪問未在 package.json 中聲明的依賴(如依賴的依賴),以及 Doppelgangers 問題中被重復(fù)安裝多次的依賴,但這些不是本文的重點(diǎn),就不做展開講解了。

        除了 Lerna + Yarn Workspace 以外,新晉的包管理器 pnpm 也有著自己的 Monorepo 方案,同時(shí)由于其獨(dú)特的依賴處理機(jī)制、強(qiáng)大的 filtering 語法(用 ... ^ 的簡寫形式代替了 dependents / dependencies,支持了基于 git 的 affected 檢測等),許多知名開源項(xiàng)目都在從 lerna 遷移向 pnpm workspace,如 Vue、Vite 等等。我個(gè)人的項(xiàng)目也基本上全部完成了到 pnpm workspace 的遷移,如我的起手式項(xiàng)目 starter-collections ,為了獲得更好的 pnpm workspace 下的開發(fā)體驗(yàn),我還為它開發(fā)了個(gè) VS Code 擴(kuò)展 pnpm-vscode-helper。

        在過去,Lerna + Yarn Workspace 的形式是社區(qū)中使用率最高的 Monorepo 方案(應(yīng)該目前不需要加上之一)。在這一搭配中, lerna 負(fù)責(zé)任務(wù)調(diào)度,yarn workspace 負(fù)責(zé)依賴處理,看起來好像解決了所有問題,但程序員就是一個(gè)永遠(yuǎn)不會知足的團(tuán)體,有一部分人開始思考,Monorepo 下的工作流是否還可以更絲滑?

        當(dāng)下與未來:Monorepo Framework

        要想獲得 Monorepo 下更好的工作流體驗(yàn),我們得先確認(rèn)目前有哪些不好的部分?

        • 任務(wù)調(diào)度雖然很棒,但實(shí)際上開發(fā)中可能下游的依賴項(xiàng)目改動頻率比較低,雖然構(gòu)建器可能有緩存功能(tsc 等),但是我更希望直接跳過未發(fā)生改動的項(xiàng)目,極致地壓縮性能。
        • 我需要在 package.json 中描述依賴關(guān)系,相當(dāng)于我還是要有一點(diǎn)心智負(fù)擔(dān)在,能否由 Task Runner 來自動分析依賴關(guān)系?
        • 上面說了有些 script 是一定要按照拓?fù)渑判騺順?gòu)建的,但有些也是不用的,比如 lint 這種,就可以無視依賴關(guān)系并行執(zhí)行。這樣我的任務(wù)執(zhí)行是否能再快一些?
        • 如果是大型的、多人甚至多團(tuán)隊(duì)協(xié)作的大型 Monorepo 項(xiàng)目,那各個(gè)團(tuán)隊(duì)的項(xiàng)目之間其實(shí)還是需要有一定的約束關(guān)系存在,比如我不希望別的團(tuán)隊(duì)在沒經(jīng)過允許的情況下去修改我的代碼,也不希望未經(jīng)過允許就直接使用我團(tuán)隊(duì)的基礎(chǔ)庫,然后出了問題又要找我修,我拒絕!
        • 對于構(gòu)建開銷非常大的項(xiàng)目,如果多個(gè)團(tuán)隊(duì)成員使用的是同一個(gè)版本,那么其實(shí)只需要有一個(gè)人構(gòu)建完畢,把產(chǎn)物上傳到云端,其他人開始構(gòu)建時(shí)直接下載此產(chǎn)物即可,這不比構(gòu)建快多了?
        • ......

        而要想解決這些問題,只靠 Task Runner 可就不夠了。我們需要的是一整套的、全生命周期覆蓋的解決方案,我這里將其稱為 Monorepo Framework(目前似乎沒看到別人這么稱呼,那我偷偷聲明一下原創(chuàng)權(quán)利?。壳拔艺J(rèn)為可被稱作 Framework 級別的解決方案主要有這么幾個(gè):

        • Nx,我個(gè)人最推薦的一個(gè)解決方案。作者是前 Google 工程師,團(tuán)隊(duì)中也有許多專注于 Monorepo 的大廠成員。按照官方的敘述,Nx 吸收了許多 Google、Meta 內(nèi)部 Monorepo 方案的優(yōu)點(diǎn)。
        • Turborepo,2021 年的新起之秀,原是個(gè)人項(xiàng)目,后被 Vercel 收購。我個(gè)人認(rèn)為這是 Vercel 在前端工程中的進(jìn)一步開疆拓土,現(xiàn)在你的框架、倉庫管理、部署都可以只靠 Vercel 完成了。
        • Rush,微軟開源的 Monorepo 方案,我個(gè)人沒有做過比較深入的了解,這里不做評論。
        • 一些企業(yè)級的解決方案,如 Google 的 Bazel、Gradle 這種,這些和前端的關(guān)系不是太大,上手成本也較高,這里同樣不做評論。

        我個(gè)人比較想詳細(xì)介紹一下 Nx 和 Turborepo,我們來一點(diǎn)點(diǎn)說一下上面解決的問題。

        • 任務(wù)緩存。Nx 和 Turborepo 都支持了任務(wù)的緩存(Local Computation Caching),但實(shí)現(xiàn)方式略有差異。Nx 對緩存計(jì)算使用了類似 React (VDOM)那樣的 diff 計(jì)算,性能方面要更好。
        • 依賴關(guān)系分析。Nx 的依賴分析除了 package.json 以外,還會基于 AST 的分析來確定一個(gè)子項(xiàng)目的上下游依賴。而 Turborepo 則和此前的 workspace 方案類似,主要基于 package.json 中的 workspace: 協(xié)議。
        • 可并行的任務(wù)調(diào)度。實(shí)際上這里的任務(wù)調(diào)度要比 lerna 這一類方案強(qiáng)大得多,如你可以更加自由的定義任務(wù)的執(zhí)行方式、前置后置任務(wù)等等。而 Turborepo 比 Nx 強(qiáng)大的地方在于,通過 pipeline 配置的方式,支持了更好的任務(wù)鏈計(jì)算,如這張圖片就能很好地說明其優(yōu)勢:

        turborepo

        實(shí)際上,Turborepo 的這一特色來自于微軟的 Lage,一個(gè)介于 Task Runner 和 Framework 之間的方案。說它只是 Runner 吧,它又支持了云端產(chǎn)物緩存,說它是 Framework 吧,它又沒有其他拿得出手的功能。

        • 項(xiàng)目間引用約束。Nx 中支持為項(xiàng)目配置中添加 Tag,通過維護(hù)一份合法的 Tag 間引用關(guān)系以及 ESLint 插件配置,來實(shí)現(xiàn)項(xiàng)目之間的隔離,如 TeamA Tag 的項(xiàng)目不允許引用 TeamB Tag 項(xiàng)目。而 Turborepo 目前暫時(shí)不支持。
        • 云端緩存。先說 Turborepo ,由于 Vercel 強(qiáng)大的 PaaS 能力,Turborepo 的構(gòu)建緩存可以非常自然地融入其中,但缺點(diǎn)在于鎖死了平臺,對于部分大廠來說并不能接受將自己的應(yīng)用代碼存儲在別的公司云端中。而 Nx 既提供了 Nx Cloud 這一方案,也提供了基于 Nx Cloud API 將云端緩存能力融入到內(nèi)部 CI/CD 流水線的方案,河南拔智齒。如果這一方案能夠在大廠中進(jìn)行落地,那么其已經(jīng)非常絲滑的構(gòu)建體驗(yàn)又將再上一個(gè)等級。

        除了這些能力以外,Monorepo Framework 其實(shí)還提供了許多貼心的功能。如 Nx 支持了 Angular 式的 Schematics 與 Builder,在 Nx 中這兩個(gè)被稱為 Generator 與 Executor。Generator 即快速生成項(xiàng)目起始模板或基礎(chǔ)代碼片段的能力,而 Executor 提供了項(xiàng)目的各個(gè) script target 自定義執(zhí)行的能力,如你可以基于內(nèi)部的構(gòu)建器提供 Executor。通常來說,Nx 中通過 Plugin 的形式來整合同一框架級別的 Plugin,如 @nrwl/react, @nrwl/nest 等等。

        這也是為何我更推崇 Nx 的原因,它允許你通過插件的形式非常自由地接入任何你想用但官方?jīng)]有提供集成的框架,這些集成可以是社區(qū)新秀如 Vite、SvelteKit,可以是非 Web 項(xiàng)目的 Electron、Ionic 等,甚至可以是其他語言如 Go 。

        以上我們所講述的就是目前的 Monorepo Framework 方案,不妨放飛你的想象力,想想未來的 Monorepo 方案又會如何演進(jìn)?我個(gè)人大概有這么幾點(diǎn)盼望:

        • 和包管理器的深度集成,上面我們說到的這些方案和包管理器仍然是割裂的,比如要想使用 pnpm workspace 和 nx ,就需要啟用 pnpm 的 shamefullyHoist,并且仍然可能會遇到問題。
        • 和 Local Registry(如 Verdaccio)的深度集成,Monorepo 中開發(fā)項(xiàng)目時(shí),如果就在 workspace 內(nèi)部開發(fā)還好,而一旦引用項(xiàng)目在 workspace 外部,就又要回歸到原始的 link 了。
        • 更加開放的插件體系,Nx 做得挺好,但還不夠好。
        • 從 Polyrepo 到 Monorepo 更好、更穩(wěn)定的遷移方案,比如給定幾個(gè)倉庫,能夠自動地按照 package.json 進(jìn)行依賴關(guān)系構(gòu)建,自動引入對應(yīng)框架的配置(甚至對于項(xiàng)目內(nèi)定制配置,也可以用過類 nx run-commands 的方式進(jìn)行遷移)。

        很明顯,Monorepo 方案還有很長的路可以走,也希望更多的團(tuán)隊(duì)與公司嘗試擁抱 Monorepo 方案。它不僅僅是一種項(xiàng)目管理方式,更是一種擁抱開放的態(tài)度。

        Node 社群



        我組建了一個(gè)氛圍特別好的 Node.js 社群,里面有很多 Node.js小伙伴,如果你對Node.js學(xué)習(xí)感興趣的話(后續(xù)有計(jì)劃也可以),我們可以一起進(jìn)行Node.js相關(guān)的交流、學(xué)習(xí)、共建。下方加 考拉 好友回復(fù)「Node」即可。



        如果你覺得這篇內(nèi)容對你有幫助,我想請你幫我2個(gè)小忙:

        1. 點(diǎn)個(gè)「在看」,讓更多人也能看到這篇文章
        2. 訂閱官方博客?www.inode.club?讓我們一起成長

        點(diǎn)贊和在看就是最大的支持??

        瀏覽 78
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評論
        圖片
        表情
        推薦
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            欧美透逼 | 大香蕉在线影院观看一区 | 扒开美女狂揉火影 | 美女岔开腿让男生桶动态图 | 国产欧美日韩精品在线观看 | 哺乳期丰满乳亲伦小说 | 在线亚洲免费观看 | 国产女人18水真多毛片18 | 韩国激烈床片段 | 91青娱乐国产 |