Monorepo——大型前端項目的代碼管理方式

最近我接手了一個項目,代碼量比較大、有點複雜。倉庫 clone 下來代碼有 50+ MB,npm install 安裝完體積飈到了近 2GB …… 熟悉了一下,這個項目比較複雜,採用了 monorepo 的方式進行代碼的管理。折騰幾天後,對 monorepo 也有個大概的瞭解……前端

Monorepo

Monorepo 是管理項目代碼的一個方式,指在一個項目倉庫 (repo) 中管理多個模塊/包 (package),不一樣於常見的每一個模塊建一個 repo。node

目前有很多大型開源項目採用了這種方式,如 Babelreact

How is the repo structured?
The Babel repo is managed as a monorepo that is composed of many npm packages.

還有 create-react-app, react-router 等。能夠看到這些項目的第一級目錄的內容以腳手架爲主,主要內容都在 packages 目錄中、分多個 package 進行管理。git

├── packages
|   ├── pkg1
|   |   ├── package.json
|   ├── pkg2
|   |   ├── package.json
├── package.json

monorepo 最主要的好處是統一的工做流Code Sharing。好比我想看一個 pacakge 的代碼、瞭解某段邏輯,不須要找它的 repo,直接就在當前 repo;當某個需求要修改多個 pacakge 時,不須要分別到各自的 repo 進行修改、測試、發版或者 npm link,直接在當前 repo 修改,統一測試、統一發版。只要搭建一套腳手架,就能管理(構建、測試、發佈)多個 packagegithub

很差的方面則主要是 repo 的體積較大。特別是,由於各個 package 理論上都是獨立的,因此每一個 package 都維護着本身的 dependencies,而很大的可能性,package 之間有很多相同的依賴,而這就可能使install時出現重複安裝,使原本就很大的 node_modues 繼續膨脹(我稱這爲「依賴爆炸」...)。shell

基於對以上的理解,我認爲當項目到必定的複雜度,須要且能夠劃分模塊、但模塊間聯繫緊密的,比較適合用 monorepo 組織代碼。npm

目前最多見的 monorepo 解決方案是 Lernayarnworkspaces 特性。其中,lerna 是一個獨立的包,其官網的介紹是:json

a tool that optimizes the workflow around managing multi-package repositories with git and npm.

上面提到的 Babel, create-react-app 等都是用 lerna 進行管理的。在項目 repo 中以lerna.json聲明 packages 後,lerna 爲項目提供了統一的 repo 依賴安裝 (lerna bootstrap),統一的執行 package scripts (lerna run),統一的 npm 發版 (lerna publish) 等特性。對於「依賴爆炸」的問題,lerna 在安裝依賴時提供了--hoist選項,相同的依賴,會「提高」到 repo 根目錄下安裝,但……太雞肋了,lerna 直接以字符串對比 dependency 的版本號,徹底相同才提高,semver 約定在這並不起做用。bootstrap

具體的使用方法移步 Lerna 官網:https://lerna.js.orgbabel

而使用 yarn 做爲包管理器的同窗,能夠在 package.json 中以 workspaces 字段聲明 packages,yarn 就會以 monorepo 的方式管理 packages。相比 lerna,yarn 突出的是對依賴的管理,包括 packages 的相互依賴、packages 對第三方的依賴,yarn 會以 semver 約定來分析 dependencies 的版本,安裝依賴時更快、佔用體積更小;但欠缺了「統一工做流」方面的實現。

yarn 官網對 workspace的詳細說明:Workspaces | Yarn

lerna 和 yarn-workspace 並非只能選其一,大多 monorepo 即會使用 lerna 又會在 package.json 聲明 workspaces。這樣的話,不管你的包管理器是 npm 仍是 yarn,都能發揮 monorepo 的優點;要是包管理是 yarn ,lerna 就會把依賴安裝交給 yarn 處理。

再說回我那項目呢,安裝依賴後體積實在是大,折騰了兩天想要優化一下,但目前大量腳本嚴重依賴 npm,我……

仍是後面考慮慢慢遷移到 yarn 吧。

Reference

Others

git-submodule

經過 Git 子模塊,能夠在當前 repo 中包含其它 repos、做爲當前 repo 的子目錄使用,同時可以保持 repos 之間的獨立。

# 在當前 repo 添加一個子模塊
git submodule add git@github.com:xxx/xxx.git path/to/xxx

能夠在 .gitmodule文件中看到當前 repo 有哪些 submodule,分別的 name, branch 等。

# clone 含有 submodule 的 repo 後:
# 初始化 git submodule 信息
git submodule init
# 更新 submodule,至關於 git pull 吧
git submodule update

修改子模塊文件後,在當前 repo 執行 git status 只會看到有模塊的 changes,而不是具體子模塊文件:

diff --git a/path/to/submodule b/path/to/submodule
--- a/path/to/submodule
+++ b/path/to/submodule
@@ -1 +1 @@
-Subproject commit xxxxxxx
+Subproject commit xxxxxxx-dirty

dirty表示子模塊的修改還不是 commit。若是子模塊的修改 commit 後,這個改動就會是具體的 commit id。

子模塊的其它 commit, pull 等各操做,仍是到其目錄下,按普通 repo 操做便可。

Reference

git-bisect

git 有一個以二分法幫助定位問題的命令——bisect

# 開始二分查找問題
git bisect start
# 標記當前有問題
git bisect bad
# 標記哪一個 commit 或 tag 時是沒問題的
git bisect good v1.0.0

# 此時 git 會 checkout 兩個點之間的某個 commit,
# 若是此時仍是有問題:
git bisect bad
# 若是此時沒有問題:
git bisect good
# 接着 git 會 checkout 下一個「有問題」和「沒問題」之間的 commit

# 直到定位到問題,git 會提示:xxxxxxx is first bad commit

Reference


歡迎關注個人訂閱號——「車迷與碼農」,不按期分享關於前端、汽車和有趣的事兒~

圖片描述

相關文章
相關標籤/搜索