基於yarn1.x的monorepo實踐分享
背景介紹
幾天前,曉東船長微信問我,你們團隊有沒有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,嗯。
參考文獻
-
MonoRepo: //en.wikipedia.org/wiki/Monorepo
-
yarn2 workspaces: //yarnpkg.com/features/workspaces
-
yarn1 workspaces: //classic.yarnpkg.com/en/docs/workspaces

