基於yarn1.x的monorepo實踐分享

2022-07-19 06:01:40

背景介紹

幾天前,曉東船長微信問我,你們團隊有沒有monorepo的實踐,我很遺憾的告訴他沒有,但這在我心裡播下了一顆探索的種子,剛好最近老總要搞內蒙古的新專案,我和另一個前端兄弟組成雙槍敢死隊進行保駕護航,於是我就開始探索,有沒有一種可能,可以一個倉庫管理多個專案,這裡說的管理是指有條理有規範的管理,而不是說硬是把幾個專案蹂躪到一起。

相關概念介紹

什麼是monorepo?

在版本控制系統中,monorepo是一種軟體開發策略,其中許多專案的程式碼儲存在同一儲存庫中。這種軟體工程實踐至少可以追溯到2000年代初期,當時被稱為「共用程式碼庫」。一個相關的概念是整體,但是儘管整體將其子專案合併為一個大型專案,但整體倉庫可能包含獨立的專案。(翻譯自維基百科)

什麼是yarn?什麼又是yarn wrokspace?

簡單地說,Yarn Workspaces是Yarn提供的monorepo的依賴管理機制,從Yarn 1.0開始預設支援,用於在程式碼倉庫的根目錄下管理多個package的依賴

實踐教學

具體的教學,我覺得官網已經寫的很詳細了,我沒有必要復讀一遍,所以我這邊只介紹我這個專案相關的一些關鍵點的介紹。

我是這樣子做架構的, 將專案一分為二,applications表示應用程式目錄,裡面包含了一些專案,比如企業端、資金端、平臺端,以及小程式和h5等,而packages這一塊的話,是我把applications中公共的部分抽離出來,做到多可複用。

除此之外,專案還做了一些優化,比如

  • 設定了eslint + prettier 去規範團隊的程式碼

  • 設定了husky和commitlint去規範團隊的程式碼提交

專案的目錄結構是這樣子的

  • applications/ent: 企業端

  • applications/plat: 平臺端

  • applications/fund: 資金端

  • applications/mina: 小程式/h5

  • packages/utils: 通用工具包

  • packages/componets: 通用元件包

  • packages/service: 通用服務包

  • packages/openapi: 通用 openapi 介面包

  • packages/constants: 通用常數包

  • packages/types: 通用型別包

  • packages/styles: 通用樣式包

  • packages/hooks: 通用勾點包

根目錄下的package.json如下:

{
  "name": "",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "repository": "",
  "author": "",
  "license": "MIT",
  "private": true,
  "workspaces": [
    "applications/*",
    "packages/*"
  ],
  "scripts": {
    "build": "yarn workspaces run build",
    "clean": "yarn workspaces run clean",
    "lint:fix": "yarn workspaces run lint:fix",
    "prettier:fix": "yarn workspaces run prettier:fix",
    "ent": "yarn workspace @sunyard-fin/ent",
    "fund": "yarn workspace @sunyard-fin/fund",
    "plat": "yarn workspace @sunyard-fin/plat",
    "mina": "yarn workspace @sunyard-fin/mina",
    "components": "yarn workspace @sunyard-fin/components",
    "constants": "yarn workspace @sunyard-fin/constants",
    "openapi": "yarn workspace @sunyard-fin/openapi",
    "service": "yarn workspace @sunyard-fin/service",
    "types": "yarn workspace @sunyard-fin/types",
    "styles": "yarn workspace @sunyard-fin/styles",
    "utils": "yarn workspace @sunyard-fin/utils"
  },
  "devDependencies": {
    "@commitlint/cli": "^17.0.3",
    "@commitlint/config-conventional": "^17.0.3",
    "eslint": "^8.15.0",
    "husky": "^8.0.1",
    "lint-staged": "^13.0.3",
    "prettier": "^2.7.1",
    "typescript": "^4.7.4",
    "@antfu/eslint-config": "^0.23.0",
    "eslint-plugin-prettier": "4.0.0",
    "eslint-config-prettier": "8.5.0"
  },
  "husky": {
    "hooks": {
      "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
    }
  },
  "lint-staged": {
    "*.{md,json}": [
      "prettier --write"
    ],
    "*.{css,js,jsx,vue,ts,tsx}": [
      "prettier --write",
      "eslint --fix  --cache"
    ]
  }
}

這裡需要注意的好幾點是

  • 在根目錄下要陪著對應包目錄
 "workspaces": [
    "applications/*",
    "packages/*"
  ],
  • "build": "yarn workspaces run build", 這句話的意思就是構建打包所有的專案包

  • "ent": "yarn workspace @sunyard-fin/ent",

設定了樓上這句話以後,就相當於一個快捷方式,你不用進入子專案去執行,直接在根目錄執行yarn ent dev 就可以進入開發環境了,就相當於進入子目錄執行yarn dev, 然後可能你也看到了,這裡的專案名不一定要跟目錄名字一樣的,使用@xxx是不是感覺更有儀式感一點呢。

  • 比如說我要給企業端新增utils包的話,可以執行yarn workspace @sunyard-fin/ent add @sunyard-fin/utils -D, 當然你也可以直接寫到對應專案的package.json裡面

  • 給所有專案都安裝一個包,執行yarn add -D -W typescript,這就會給所有專案安裝typescript包

其他的就按照正常使用yarn來。

總結

monorepo適合運用在大型專案中,結合yarn1.x使用的好處是不用每個專案都安裝一遍依賴,這極大的減少專案的體積,然後管理程式碼也更有條理了,各個模組清晰了很多,也做到了高可複用。

FAQ

  • 為什麼選擇yarn1.x,不是有yarn2.x嗎?

其實最開始的選型上,也考慮過用pnpm、yarn2、lerna等等,時間緊任務重,我沒有太多的精力去一個一個調研,粗略看了下yarn1.x針對於我們目前這個專案夠用了,而且引入也沒有啥弊端目前看來,然後也是天然整合的,就不用再額外地增加學習成本,yarn2的話也很好,只不過是我設定yarn set version berry好像我不科學自強就成功不了,考慮到這是一個團隊(額,雖然也就我和另一個兄弟哈哈哈),所以我還是比較拘謹沒有采用2.x,嗯。

參考文獻