zl程序教程

您现在的位置是:首页 >  其他

当前栏目

微前端工具箱:用subtree解决多模块复用问题

2023-03-20 15:02:06 时间

在日常开发工作中,可能会面临这样一种需求:项目 A 中,存在一个或多个有价值的功能模块,这里的所谓模块指有一个或几个页面组成的功能模组;其他项目,如项目 B,也想借助这些模块给自己赋能,且基本要求如:

  • 要使用项目 B 自己的导航菜单
  • 要重新定义路由并将各个模块分散到不同页面下
  • 接口请求要统一加特殊标识参数
  • 项目 B 要根据额外的环境变量对各模块中的功能做限制
  • 项目 B 希望以自己的上线节奏更新模块

为了理解方便,约定文档中可能通用的几个称呼:

  • 主项目 - 项目 A
  • 新项目 - 项目 B 等
  • 子模块 - 从项目 A 里划分出的几个模块

【方案对比】

经过调研,常用且纯前端的项目模块化改造方案如下:

成本 方案

iframe

微前端

npm包

Git子模块

首次改造主项目

3

3

5

5

首次改造新项目

1

3

2

2

维护子模块

2

3

3

2

更新子模块

1

5

3

1

同步子模块

1

1

2

1

总开发成本

8

16

15

11

综合到考虑各业务项目体量、具体产品需求,和由改造带来的长期用户体验等,我们可能会在工具箱中选择Git子模块方案进行。

【git subtree 介绍】

subtree 是 git submodule 技术的主要替代技术,避免了后者的一些麻烦。

具体介绍这里不做赘述,可以直接参考文末的资料,如 《submodule vs. subtree 对比》[1]

简而言之:

  • 为每个子模块创建一个独立的 git 仓库
  • 子模块的 git 仓库中的文件可以被当成一个普通的子目录添加到主项目/新项目中
  • 过程中只拉取文件,不会产生额外的 git 隐藏文件等
  • 子模块中的改变会被正常提交到所在项目的历史中
  • 在主项目/新项目层面也可完成子模块的 git 推送/拉取操作,这也是推荐的做法

分别在主项目/新项目中执行的 git 命令:

  • 添加 remote 以简化后续命令:
git remote add -f <子模块别名> git@coding.foo.com:<子模块的...`.git`>

后面以 子模块别名report 为例

  • 添加 subtree:
git subtree add --prefix=<相对路径> report <分支> --squash
  • 拉取 subtree:
git subtree pull --prefix=<相对路径> report <分支> --squash
  • 推送 subtree:
git subtree push --prefix=<相对路径> report <分支>
  • 查看 subtree 的 id:
git ls-remote report
或
git ls-remote report | grep <sha-1>

【选择一种分支策略】

和 git 本身各种流派的 workflow 一样,subtree 并没有规定特定的工作流程;这里尝试总结两种:

⇲ 单向模式:被动小分支

  • 涉及子模块中功能升级、优化、改进的内容,都在主项目中
  • 日常的以上改动都 push 到子模块的 master
  • 子模块仓库中维护某个独立分支,如用于项目 B 的 feature-projB
  • 子模块 master 有更新时手动 merge 到 feature-projB
  • 新项目只 pull 最新的 feature-projB

优点:

  1. 新项目可以随意修改特定的子模块分支
  2. 合并冲突可以在子模块中按普通方式解决
  3. 子模块代码中无需判断所在项目的环境

缺点:

  1. 如果改动需要反向同步到 master,可能需要额外的重复修改或 cherry-pick

♺ 双向模式:共建master

  • 主项目和新项目各自维护涉及子模块的相关功能
  • 新项目中的改动也可以 push 到子模块的 master
  • 新项目涉及子模块的改动应保证不污染主项目等其他项目

优点:

  1. 涉及子模块中功能升级、优化、改进的内容,可以由各个业务项目共同贡献
  2. 如果改动不复杂可以跳过再去子模块仓库下进行的各种操作

缺点:

  1. 可能需要改动 webpack 配置等以区分项目环境
  2. 代码中需要区分环境的片段需要重点关注

【注意问题】

  • 新项目和主项目结构、基础能力难免不一致,如 src/storesrc/common/store ,以及 http 等模块中方法的不同
    • 要竭尽所能地使子模块低耦合,保证多项目适配的基本可行性
    • 要在文档中(如子模块根目录的README)标明所有依赖点
  • merge 代码冲突
    • 双向模式下,如果本方没有更新,可以直接 rm -rf 掉子模块目录并重新 add

【参考文档】

[1]《submodule vs. subtree 对比》: https://einverne.github.io/post/2020/04/git-subtree-usage.html#sub-module-vs-sub-tree-%E5%AF%B9%E6%AF%94

  • https://betterprogramming.pub/git-subtree-usage-6aaba8b5d947
  • https://medium.com/@v/git-subtrees-a-tutorial-6ff568381844
  • https://medium.com/@porteneuve/mastering-git-subtrees-943d29a798ec
  • https://medium.com/codex/github-subtrees-fb93d229cdc0
  • https://segmentfault.com/a/1190000012002151
  • https://tech.youzan.com/git-subtree/
  • http://www.fwolf.com/blog/post/246
  • https://liuyufang.com/version-control/git/git-subtree
  • https://www.worldhello.net/gotgit/04-git-model/050-subtree-model.html