We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
最近我接手了一个项目,代码量比较大、有点复杂。仓库 clone 下来代码有 50+ MB,npm install 安装完体积飚到了近 2GB …… 熟悉了一下,这个项目比较复杂,采用了 monorepo 的方式进行代码的管理。折腾几天后,对 monorepo 也有个大概的了解……
Monorepo 是管理项目代码的一个方式,指在一个项目仓库 (repo) 中管理多个模块/包 (package),不同于常见的每个模块建一个 repo。
目前有不少大型开源项目采用了这种方式,如 Babel:
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 进行管理。
packages
├── packages | ├── pkg1 | | ├── package.json | ├── pkg2 | | ├── package.json ├── package.json
monorepo 最主要的好处是统一的工作流和Code Sharing。比如我想看一个 pacakge 的代码、了解某段逻辑,不需要找它的 repo,直接就在当前 repo;当某个需求要修改多个 pacakge 时,不需要分别到各自的 repo 进行修改、测试、发版或者 npm link,直接在当前 repo 修改,统一测试、统一发版。只要搭建一套脚手架,就能管理(构建、测试、发布)多个 package。
npm link
不好的方面则主要是 repo 的体积较大。特别是,因为各个 package 理论上都是独立的,所以每个 package 都维护着自己的 dependencies,而很大的可能性,package 之间有不少相同的依赖,而这就可能使install时出现重复安装,使本来就很大的 node_modues 继续膨胀(我称这为「依赖爆炸」...)。
install
node_modues
基于对以上的理解,我认为当项目到一定的复杂度,需要且可以划分模块、但模块间联系紧密的,比较适合用 monorepo 组织代码。
目前最常见的 monorepo 解决方案是 Lerna 和 yarn 的 workspaces 特性。其中,lerna 是一个独立的包,其官网的介绍是:
yarn
workspaces
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 约定在这并不起作用。
lerna.json
lerna bootstrap
lerna run
lerna publish
--hoist
具体的使用方法移步 Lerna 官网:https://lerna.js.org
而使用 yarn 作为包管理器的同学,可以在 package.json 中以 workspaces 字段声明 packages,yarn 就会以 monorepo 的方式管理 packages。相比 lerna,yarn 突出的是对依赖的管理,包括 packages 的相互依赖、packages 对第三方的依赖,yarn 会以 semver 约定来分析 dependencies 的版本,安装依赖时更快、占用体积更小;但欠缺了「统一工作流」方面的实现。
package.json
yarn 官网对 workspace的详细说明:Workspaces | Yarn
workspace
lerna 和 yarn-workspace 并不是只能选其一,大多 monorepo 即会使用 lerna 又会在 package.json 声明 workspaces。这样的话,无论你的包管理器是 npm 还是 yarn,都能发挥 monorepo 的优势;要是包管理是 yarn ,lerna 就会把依赖安装交给 yarn 处理。
再说回我那项目呢,安装依赖后体积实在是大,折腾了两天想要优化一下,但目前大量脚本严重依赖 npm,我……
还是后面考虑慢慢迁移到 yarn 吧。
通过 Git 子模块,可以在当前 repo 中包含其它 repos、作为当前 repo 的子目录使用,同时能够保持 repos 之间的独立。
# 在当前 repo 添加一个子模块 git submodule add [email protected]:xxx/xxx.git path/to/xxx
可以在 .gitmodule文件中看到当前 repo 有哪些 submodule,分别的 name, branch 等。
.gitmodule
name
branch
# clone 含有 submodule 的 repo 后: # 初始化 git submodule 信息 git submodule init # 更新 submodule,相当于 git pull 吧 git submodule update
修改子模块文件后,在当前 repo 执行 git status 只会看到有模块的 changes,而不是具体子模块文件:
git status
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。
dirty
子模块的其它 commit, pull 等各操作,还是到其目录下,按普通 repo 操作即可。
commit
pull
man git-submodule
git 有一个以二分法帮助定位问题的命令——bisect。
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
The text was updated successfully, but these errors were encountered:
No branches or pull requests
最近我接手了一个项目,代码量比较大、有点复杂。仓库 clone 下来代码有 50+ MB,npm install 安装完体积飚到了近 2GB …… 熟悉了一下,这个项目比较复杂,采用了 monorepo 的方式进行代码的管理。折腾几天后,对 monorepo 也有个大概的了解……
Monorepo
Monorepo 是管理项目代码的一个方式,指在一个项目仓库 (repo) 中管理多个模块/包 (package),不同于常见的每个模块建一个 repo。
目前有不少大型开源项目采用了这种方式,如 Babel:
还有 create-react-app, react-router 等。可以看到这些项目的第一级目录的内容以脚手架为主,主要内容都在
packages
目录中、分多个 package 进行管理。monorepo 最主要的好处是统一的工作流和Code Sharing。比如我想看一个 pacakge 的代码、了解某段逻辑,不需要找它的 repo,直接就在当前 repo;当某个需求要修改多个 pacakge 时,不需要分别到各自的 repo 进行修改、测试、发版或者
npm link
,直接在当前 repo 修改,统一测试、统一发版。只要搭建一套脚手架,就能管理(构建、测试、发布)多个 package。不好的方面则主要是 repo 的体积较大。特别是,因为各个 package 理论上都是独立的,所以每个 package 都维护着自己的 dependencies,而很大的可能性,package 之间有不少相同的依赖,而这就可能使
install
时出现重复安装,使本来就很大的node_modues
继续膨胀(我称这为「依赖爆炸」...)。基于对以上的理解,我认为当项目到一定的复杂度,需要且可以划分模块、但模块间联系紧密的,比较适合用 monorepo 组织代码。
目前最常见的 monorepo 解决方案是 Lerna 和
yarn
的workspaces
特性。其中,lerna 是一个独立的包,其官网的介绍是:上面提到的 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 约定在这并不起作用。具体的使用方法移步 Lerna 官网:https://lerna.js.org
而使用 yarn 作为包管理器的同学,可以在
package.json
中以workspaces
字段声明 packages,yarn 就会以 monorepo 的方式管理 packages。相比 lerna,yarn 突出的是对依赖的管理,包括 packages 的相互依赖、packages 对第三方的依赖,yarn 会以 semver 约定来分析 dependencies 的版本,安装依赖时更快、占用体积更小;但欠缺了「统一工作流」方面的实现。yarn 官网对
workspace
的详细说明:Workspaces | Yarnlerna 和 yarn-workspace 并不是只能选其一,大多 monorepo 即会使用 lerna 又会在
package.json
声明workspaces
。这样的话,无论你的包管理器是 npm 还是 yarn,都能发挥 monorepo 的优势;要是包管理是 yarn ,lerna 就会把依赖安装交给 yarn 处理。再说回我那项目呢,安装依赖后体积实在是大,折腾了两天想要优化一下,但目前大量脚本严重依赖 npm,我……
还是后面考虑慢慢迁移到 yarn 吧。
Reference
Others
git-submodule
通过 Git 子模块,可以在当前 repo 中包含其它 repos、作为当前 repo 的子目录使用,同时能够保持 repos 之间的独立。
可以在
.gitmodule
文件中看到当前 repo 有哪些 submodule,分别的name
,branch
等。修改子模块文件后,在当前 repo 执行
git status
只会看到有模块的 changes,而不是具体子模块文件:dirty
表示子模块的修改还不是 commit。如果子模块的修改 commit 后,这个改动就会是具体的 commit id。子模块的其它
commit
,pull
等各操作,还是到其目录下,按普通 repo 操作即可。Reference
man git-submodule
git-bisect
git 有一个以二分法帮助定位问题的命令——
bisect
。Reference
The text was updated successfully, but these errors were encountered: