1. 程式人生 > 其它 >轉發:TypeScript Monorepo 最佳實踐

轉發:TypeScript Monorepo 最佳實踐

當我們跨多個程式碼倉庫管理多個專案之間的依賴關係時,既耗時又容易出錯。
monorepo 是一種處理上述問題的程式碼管理架構概念,它將多個專案的所有隔離程式碼庫整合到一個大型儲存庫中,而不是單獨管理它們。當與合適的工具一起使用時,Monorepos 很有優勢。因此許多組織採用了在單個儲存庫中維護多個專案的策略。
Google、Meta 和 Microsoft 等大公司通常在單個 monorepo 中管理組織內多個專案的程式碼庫。這種方法使他們能夠在專案之間共享依賴項、庫、元件、實用程式、文件等。在專案之間共享程式碼可確保程式碼庫的一致性和可預測性。然而,依賴管理才是 monorepo 真正的優勢所在。如果有人對共享庫進行重大的更新,所有受影響的庫將會立刻收到這個更新。
儘管 monorepo 有自己的一系列挑戰,但它也通過正確的工具解決了這些挑戰。其中一個工具是 Typescript,在這篇文章中,我們將研究管理 Typescript monorepo 的最佳實踐。

使用 TypeScript Project References

TypeScript Project References 的主要目標始終是幫助解決大型 TypeScript 專案(如 monorepo)中編譯時間長的問題。它們可以將一個巨大的專案劃分為幾個較小的模組,這些模組都可以獨立構建。此外,它還可以建立更模組化的程式碼。使用這種方法,可以大大縮短構建時間,可以在邏輯上分離元件,並且可以以更有條理和邏輯的方式重新組織你的程式碼。
這是 TypeScript Project References 的文件

管理依賴於其他包的包

在處理依賴於 Typescript monorepo 中另一個包的多個包時,你必須明確讓 Typescript 知道這種依賴關係。例如, @projectName /package-A 依賴於 @projectName /package-B。我們需要新增以下配置,讓 Typescrip 知道這個依賴。
首先,你必須在 package-b 的 tsconfig 中新增它。

// packages/package-b/tsconfig.json
{
    "extends": "../../tsconfig.json",
    "compilerOptions": {
      "outDir": "./dist",
      "composite": true
    },
    "include": ["./src"]
  }
}
複製程式碼

下一步是在 package-a 中引用包。

{
  "extends": "../../tsconfig.json",
  "compilerOptions": {
    "outDir": "dist",
    }
  },
  "include": ["**/*.ts", "**/*.tsx"],
  "exclude": ["dist/*"],
  "references": [{ "path": "../package-b/tsconfig.json" }]
}
複製程式碼

設定工作區

工作空間是在 yarn、NPM 和其他工具中發明的一個概念,可用於以有組織且一致的資料夾結構的形式為 monorepo 儲存庫中的包和應用程式提供自己的工作空間。monorepos 等大型專案可以從用於管理包和依賴項的工作區中受益。
在 Yarn 工作區中,可以建立專案,例如:

packages/  
  localPackageA/  
    package.json   
    ...  
  localPackageB/   
    package.json   
    ...

複製程式碼

使用 Yarn Workspaces,跨工作區安裝包變得更快、更輕。此外,它可以防止跨工作區的包重複,還可以在相互依賴的目錄之間建立連結,確保所有目錄在 monorepo 中都是一致的。
你可以在 yarn 工作區npm 工作區pnpm 工作區之間進行選擇。

使用絕對路徑匯入模組

匯入模組時最好使用絕對路徑而不是長的相對路徑。這對於程式碼的清晰很重要,因為隨著程式碼庫變得越來越大,會有更深的巢狀資料夾和檔案,使用它們的相對路徑匯入它們是在程式碼庫中弄亂的最快方法之一,因為這會使程式碼混亂且難以閱讀。
我們看一下下面的例子。

import { FormType } from '../../../../types/form';
import { DateType }   from '../../../../types/date';
複製程式碼

在這種匯入的寫法中,任何一個開發人員都會發現很難確切知道這些模組是從哪個資料夾中被匯入的。
所以最好使用絕對路徑。
我們再看一下比上面更好的一個例子:

import { FormType } from 'utils/types/form';
import { DateType }   from 'utils/types/date';
複製程式碼

從上面的程式碼中,匯入現在是清晰、可讀和可預測的,因為開發人員知道要匯入的模組來自哪個資料夾。
使用絕對匯入方法在某些會比較冗餘,因為它必須寫全路徑,而不是使用點和斜線。
但是從長遠來看,隨著程式碼庫建立更多資料夾和檔案而變得更大時,它會很好地發揮作用。
有一些工具可以幫助你實現這個功能。只需要在編譯程式碼時將為你的模組新增解析器的工具就可以了。其中一個工具是 Babel Plugin Module Resolver,您可以閱讀它的文件:Babel Plugin Module Resolver

Prettier 和 ESLint

具有多個專案的大型程式碼庫,每天有多個人在很長的時間內工作,因此編碼風格往往不一致。
在開發過程中捕獲常見錯誤被認為是開發人員的核心工作,而開發人員比較容易犯下愚蠢的錯誤有:錯誤的檔案匯入、未使用的變數、錯誤的變數命名、訪問為定義的變數、重複的定義函式......
如果使用 Eslint,這些問題都可以避免。
在 monorepo 中使用 Prettier 和 ESLint 配合效果很好。
使用 Prettier,你可以在所有專案中保持程式碼風格的一致性。只需在 monorepo 的根目錄中建立一個 .prettierrc 配置檔案,它會自動應用在 monorepo 中的所有包上。
使用 ESLint,可以幫助我們分析 JavaScript 和 TypeScript 程式碼。和 Prettier 一樣,它可以為 monorepos 輕鬆配置。你只需在 monorepo 的根目錄中定義一個 .eslintrc.json 配置檔案,它會應用於所有專案。
但是,如果 monorepo 中有很多檔案,Prettier 或 ESLint 可能需要很長時間才能執行。這可以通過將指令碼定義新增到本地包的 package.json 來解決,這個檔案引用專案根目錄中的 Prettier 和 ESLint 配置,這樣可以讓 Prettier 和 ESLint 只針對特定的包執行。

使用 Turborepo

有幾個很棒的工具可以幫助 monorepos 提供流暢的開發體驗。
其中一個工具是 Turborepo,它是一個強大的工具,有助於在 JavaScript 和 Typescript monorepos 中開發高質量和高效能的構建系統。它具有很多高階功能,其中之一是並行執行任務。
當我們從根資料夾執行 npm run dev 或 yarn dev 時,它會啟動 monorepo 中所有可用的專案,只要這些專案的 package.json 檔案中有一個 dev 指令碼。同樣的事情也適用於其他命令,例如 npm run build, npm run lint, npm run start......
在 Turborepo 中,你可以通過配置專案根資料夾下的 package.json 檔案來實現:

"scripts": {
  "dev": "turbo run dev",
  "lint": "turbo run lint",
  "build": "turbo run build",
  "clean": "turbo run clean",
  ...
},
"devDependencies": {
  ...
  "turbo": "latest"
}

複製程式碼

Turborepo 還附帶了各種工具和許多其他配置,預設情況下允許你在深度巢狀的工作空間中並行執行指令碼,或者你也可以選擇按順序執行它們或過濾它們。

"scripts": {
  "dev": "turbo run dev --filter=\"docs\"",
  ...
},

複製程式碼

Turborepo 帶有高階遠端快取功能,本地檔案的高效能構建是預設功能,也適用於遠端檔案。你可以隨時選擇退出本地快取。
此外,你還可以使用 Turborepo 建立用於執行指令碼的 monorepo 管道。
你可以檢視 Turborepo 的文件以瞭解更多資訊。
除了 turborepo 外,還有其他類似的工具,比如 LernaNx,你可以使用它們來實現相同的功能。

正確的構建工具

為你的 monorepo 專案的部署選擇正確的構建工具是一個非常重要的事情。
你應該謹慎選擇,因為如果沒有做好構建工作,我們可能會遇到必須在倉庫中部署所有程式碼的問題,即使部署的內容只包含必要的原始檔。
就像我們使用 Jest 一樣,我們也可以使用 Webpack 在一個可以配置為使用 Typescript 引用的 monorepo 中。這可以通過簡單地使用 ts-loader 來實現,並且一切都可以設定成自動工作的模式。
我們還有更多工具可以使用,例如 esbuild。Esbuild 預設提供 TypeScript 支援,因此它會自動解析所有的本地引用,因為我們已經配置了 TypeScript project references。你還可以使用 @yarnpkg 外掛新增其他配置,這有助於 Esbuild 從本地 Yarn 快取中解析外部依賴項。
Changesets 也是一種流行的版本控制工具,用於管理儲存庫中的多個包,例如 monorepo,它為維護人員提供了一個工作流,有助於自動更新包版本和釋出新包。

總結

在 monorepo 中使用 Typescript 可能需要一些繁瑣的配置和使用一些最佳實踐。
但這樣做你將能夠提高程式碼庫的可維護性,並在你的公司整個程式碼庫中實現了統一性和可見性,不再需要去跟蹤不同的程式碼倉庫。