二、实现跨Git项目复用组的探测之旅

2020-12-17 loading

随着“”的文章发布,收到了很多建设性的反馈,特地把大家好的提议在这里做下实践和补充!

# 一、使用 submodule

以下操作是在第三方库https://github.com/Heyff12/reuseParent执行的操作

# 1、 拉取子项目代码

git submodule add https://github.com/Heyff12/reuseSon.git

# 1.1 在根目录,新增

  • reuseSon 目录,里面是子项目的所有代码
  • .gitmodules 文件,记录两个库的关联

此时,push 代码,在第三库中,reuseSon 只是一个外链,并不包含真实代码。

备注:

  • 如果直接 clone 一个包含子模块的项目,里面子模块目录是空的,需要执行初始化
  git clone https://github.com/Heyff12/reuseParent
  git submodule init
  git submodule update
  # 合并上面两行命令 git submodule update --init

替代命令

git clone --recurse-submodules https://github.com/Heyff12/reuseParent

# 1.2 此时,我们在第三方库中,包含了子库代码,我们可以在第三方库中同时进行子库的开发

拉取子库最新代码

git submodule update --remote

# 2、修改后提交代码到子项目

需要在 reuseSon 目录下,单独 commit /push

git push origin HEAD:master

在子库可以通过 pull 获取最新改动

# 3、使用方式

在 app.module.ts 中引入

import { TheButtonComponent } from "reuseSon/src/app/components/the-button/the-button.component";

在文件 tsconfig.app.json 中增加编译配置

"include": [
  "reuseSon/src/app/components/**/*.ts",
],
"exclude": [
  "reuseSon/src/app/components/**/*.spec.ts"
]

# 4、总结

跟使用 subtree 比较起来,效果一样。

优点:

  • 可以在第三库对 子项目进行开发调试

缺点:

  • 在第三方库使用起来比较麻烦,不仅需要引入,还得进行单独的编译配置
    • 在 angular 项目中,组件需要 在 module 注册。 拉取过来的公用组件的 module 需要受到在第三方项目的 app.module.ts 中引入
    • 需要增加配置编译

如果使用 subtree 或者 dubmodule 的方式,最佳操作:

  • 那么在子库里面,可以只放置组件代码,不进行编译、打包配置
  • 关于子库的代码,都直接通过使用它的第三方库进行解析、编译、调试
  • 第三库在引入子项目时,可以将子项目代码指定路径,并将该路径放在第三方库的 src 目录下,这样可以不用进行单独的编译配置

建议(个人观点):

  • 公用组件库的维护来源,尽量唯一(只能在公用组件仓库开发、调试)
    • 这样可以避免合并、提交代码的冲突带来的繁琐操作
  • 第三方库只负责对于公用组件库的使用
    • 同时保证使用方式的简单,不要有太多繁琐配置

# 二、subtree 和 submodule 的比较

subtree 和 submodule 的目的都是用于 git 子仓库管理,二者的主要区别在于,subtree 属于拷贝子仓库,而 submodule 属于引用子仓库。

维度 subtree submodule 优劣对比
空间占用 subtree 在初始化 add 时,会将子仓库 copy 到父仓库中,并产生至少一次 merge 记录。所以会占用大量父仓库空间 submodule 在初始化 add 时,会在父仓库新建一个 .gitmodules 文件,用于保存子仓库的 commit hash 引用。所以不会占用父仓库空间 submodule 更优
clone subtree add 至父仓库之后,后续的 clone 操作与单一仓库操作相同 后续 clone 时 submodule 还需要 init/update 操作,且 submodule 子仓库有自己的分支 subtree 更优
update 子仓库更新后,父仓库需要 subtree pull 操作,且命令行略长,需要指定 --prefix 参数。由于无法感知子仓库的存在,可能会产生 merge 冲突需要处理 子仓库更新后,父仓库需要 submodule update 操作。父仓库只需变动子仓库 hash 引用,不会出现冲突 submodule 更优
commit 父仓库直接提交父子仓库目录里的变动。若修改了子仓库的文件,则需要执行 subtree push 父子仓库的变动需要单独分别提交。且注意先提交子仓库再提交父仓库 subtree 更优

学习参考链接:

# 三、软链

Linux ln(英文全拼:link files)命令是一个非常重要命令,它的功能是为某一个文件在另外一个位置建立一个同步的链接。

当我们需要在不同的目录,用到相同的文件时,我们不需要在每一个需要的目录下都放一个必须相同的文件,我们只要在某个固定的目录,放上该文件,然后在 其它的目录下用 ln 命令链接(link)它就可以,不必重复的占用磁盘空间。

 ln [参数][源文件或目录][目标文件或目录]

我们基于一、实现跨 Git 项目复用组的探测之旅(opens new window) 中的第四种方式,设置软链

在 reuseParent 项目根目录

ls -s ../reuseBtn ./node_modules/btnUrl

bug: 暂时无效

在第三种的基础之上,改用 npm link

# 创建link
cd reuseBtn
npm link

## /Users/yaya/.nvm/versions/node/v12.10.0/lib/node_modules/@hey_ff/testbutton -> /Users/yaya/Documents/learn/angular-study/component-repeat/reuseBtn

# 关联link
cd reuseParent
npm link @hey_ff/testbutton

## /Users/yaya/Documents/learn/angular-study/component-repeat/reuseParent/node_modules/@hey_ff/testbutton -> /Users/yaya/.nvm/versions/node/v12.10.0/lib/node_modules/@hey_ff/testbutton -> /Users/yaya/Documents/learn/angular-study/component-repeat/reuseBtn

# 解除link
npm unlink @hey_ff/testbutton

## 解除时,会导致同时删除安装包

# 五、MonoRepo

在探索的过程中,都是基于位于不同库的方式。有同事给出了 monorepo 的解决方案,特此一试。

# 1、基于 Angular 的 workspace

# 1.1 生成库

# 创建workspace
ng new my-workspace --createApplication="false"

# 进入工作区 创建应用和库
cd my-workspace
ng generate application my-first-app
ng generate library my-lib
ng generate application my-second-app

# 1.2 增加 package 命令

在 package.json 增加配置

"scripts": {
    "ng": "ng",
    "start": "ng serve",
    "start:second": "ng serve my-second-app",
    "build": "ng build",
    "build:lib": "ng build my-lib --prod",
    "build:second": "ng build my-second-app --prod",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e"
  },

# 1.3 新建组件并打包

在 my-lib 新建组件并执行打包

npm run build:lib

核心配置 tsconfig.base.json:

"paths": {
      "my-lib": [
        "dist/my-lib/my-lib",
        "dist/my-lib"
      ]
    }

# 1.4 在 my-first-app 使用

引入 app.module.ts:

import { MyLibModule } from "my-lib";

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule, AppRoutingModule, MyLibModule],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}

使用 app.component.html:

<lib-the-button text="lib-componeent"></lib-the-button>

# 1.5 持续使用变更中的 my-lib 组件

每次修改组件后,执行打包

npm run build:lib

在 my-first-app 中就会得到最新的效果

实例库(opens new window) 参考文档(opens new window)

# 1.5 总结

这个实践跟 一、实现跨 Git 项目复用组的探测之旅(opens new window) 第五种方式的实现原理相同:通过 tsconfig 文件配置实现对组件库的调用

# 2、基于 workspace 和 tsconfig 自主配置

# 2.1 准备主库和组件库

# 相关库