两只小蚂蚁

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

背景

在采用MFE架构的组织和业务中,跨项目功能复用是必须面对的一个问题,一般情景下这种情况通常可以通过npm公共包的形式来实现。但这种npm包抽取适用于更新频率较低,且具有普适性的非业务功能或模块。同时,npm包版本管理,依赖版本收集也会成为一个问题,这部分问题将会放到支撑层的脚手架一文中进行讨论。

对于在同一个toB业务SPA中的共享一般需要支撑PC和H5两端(对于国内的包括各种小程序在内的多端复用可以参考uni-app等框架),而大部分的领域和业务代码是可以被复用的,接下来看看在这种场景下怎么来架构一个SPA项目的结构能更好的适应这种场景。

submodule

submodule是git很早就出来的一个解决代码共享问题的一个方案,下面看看如何使用

cd /repo1
git submodule add https://git/sharedRepo/sharedRepo.git
git status

这种情况下会在repo1目录下新增2个文件

.gitmodules
sharedRepo

同样的,可以在repo2中也引入sharedRepo仓库

cd /repo2
git submodule add https://git/sharedRepo/sharedRepo.git

这样的话repo1和repo2就可以共用sharedRepo的代码了。但是,sharedRepo目录只是一个link,他不会随主repo1或repo2被clone、pull、push等。因此在使用过程中需要特别注意其自身的管理流程。

管理流程

  1. 对主项目repo1和repo2添加submodule后业务测开发者进行clone
  2. 需要使用单独的命令来下载submodule
git submodule init
git submodule update
  1. 如果在主项目中有对submodule的更改,需要进入到sharedRepo的目录下单独进行commit和push
cd /repo1/sharedRepo
git checkout feature/XXX
git add .
git commit
git push
  1. 由sharedRepo的负责人进行多个业务开发提交的合并

  2. sharedRepo负责人通知repo1和repo2的负责人(和业务开发者)进行submodule更新

    进入到sharedRepo目录进行单独拉取

cd /repo1/sharedRepo
git checkout master
git pull
//OR
git submodule foreach git pull

​ 由于submodule是一个独立的仓库,如果再同时submodule的package.json版本有更新的话,还需要

git submodule foreach git submodule update

思考

submodule是将共用仓库链接到业务仓库中,他的管理遵循普通的git仓库管理流程,有自己独立的分支和提交历史,也就是说业务仓库对其的更改不会跟随业务仓库被提交或更新。这种模式下对普通侧开发人员来说需要由自己进行业务层和共用层的分别管理,造成一定的复杂性,特别是对于新人很不友好。

同时,由于submodule存在独立版本和分支机制,更新需要特别小心是否有版本更新,如果有需要既pull也update。另外需注意submodule 的 HEAD 默认是处于游离状态的,每次切换到submodule目录需要切分支

总体来说,submodule使用上并不是太方便。

subtree

subtree是git从2012年5月(v1.7.11)开始引入的一个用于代码共享的方案,先看看怎么用

//第一个项目
cd /repo1  
git remote add -f sharedRepo https://git/sharedRepo/sharedRepo.git
git subtree add --prefix=./sharedRepoLib sharedRepo master --squash
//第二个项目
cd /repo2
git remote add -f sharedRepo https://git/sharedRepo/sharedRepo.git
git subtree add --prefix=./sharedRepoLib sharedRepo master --squash

--squash 不拉取sharedRepo的历史commit到当前repo1或repo2项目,只生成一条commit信息

这样的话,sharedRepo这个项目就作为一个共用项目,被添加到了repo1和repo2项目中

|--repo1
|  |--sharedRepoLib
|--package.json
|
|--repo2
|  |--sharedRepoLib
|--package.json

管理流程

  1. 对主项目repo1和repo2的业务开发人员来说,subtree的加入对业务开发来说没有任何的影响,平时怎么clone和commit还是怎么clone和commit,业务人员用自己的开发分支(feature/xxx)进行提交。
  2. 主项目repo1和repo2的负责人合并各feature/xxx分支到发布分支release/xxx
  3. 共用项目sharedRepo需要有专门的人负责,他需要负责收集repo1和repo2中对sharedRepo的更改并进行合并。
cd /repo1git checkout release/xxxgit subtree push --prefix=./sharedRepoLib sharedRepo release/repo1_xxxcd /repo2git checkout release/xxxgit subtree push --prefix=./sharedRepoLib sharedRepo release/repo2_xxx
  1. sharedRepo负责人将release/repo1_xxx和release/repo2_xxx进行merge到sharedRepo的master分支(也可以采用release分支管理)
  2. sharedRepo负责人通知repo1和repo2的负责人更新最新的sharedRepo代码
cd /repo1git subtree pull --prefix=./sharedRepoLib sharedRepo master --squashcd /repo2git subtree pull --prefix=./sharedRepoLib sharedRepo master --squash

思考

subtree实际上是复制了一份共用代码到业务仓库中,对业务仓库的pull和push会更改该仓库下subtree的代码,这点与submodule有很大不同。由于subtree是copy模式,无法单独切换分支,业务代码层如果需要对subtree进行多版本管理将会造成很混乱的多对多分支问题,应尽量使用单一的subtree分支避免这种情况。

substree模式对普通业务开发侧人员来说,完全是透明和无感的。但同时,需要由项目负责人来对subtree进行统一的管理。

monorepo

monorepo是概念,指管理项目代码的一个方式,在一个项目仓库 (repo) 中管理多个模块/包 (package),lerna(或yarn的workspace)是工具,用于具体实现这种思想,比如下面这种项目结构:

|-- packages
|   |── H5Project
|   |   ├── package.json
|   |── PCProject
|   |   ├── package.json
|   |-- sharedRepo
|   |   |-- package.json
|-- package.json
|-- lerna.json

其中H5和PC端都共用sharedRepo中的业务抽象代码,以达到模块/功能复用的目的。看看如何构建上面的项目结构:

lerna initmkdir H5Projectcd H5Projectnpm initmkdir PCProjectmkdir sharedRepo...lerna add sharedRepo --scope=H5Projectlerna add sharedRepo --scope=PCProject

跑了lerna add之后,在H5Project和PCProject中的package.json都添加了对sharedRepo的依赖,同时在node_modules里生成了指向sharedRepo的软链,类似npm link的效果。

{  "name": "PCProject",  "version": "1.0.0",  "description": "",  "main": "index.js",  "scripts": {    "test": "echo \"Error: no test specified\" && exit 1"  },  "keywords": [],  "author": "",  "license": "ISC",  "dependencies": {    "sharedRepo": "^1.0.0"  }}

以上就能实现业务抽象层代码在同一SPA下的跨端共用架构,关于lerna的另外一个功能依赖提升在这里只能说作为一个辅助功能吧。
最近兴起的turborepo工具对比lerna要好用些,不过工具链还不是太多,静待发展吧。

管理流程

使用lerna的场景下项目的git流程与普通的git项目没有任何区别因为他就是一个整的git项目,但是,使用lerna后会对后续项目权限管理和的CI/CD带来影响。

  1. 项目权限

    由于所有的项目都归并到了一个lerna工程下,权限赋予只能给与顶级访问权限,这意味着可以修改所有子项目中的代码。对于读权限来说,暂时无法解决,对于写权限,这种情况下只有通过第三方的插件或工具(如dangerjs等)来做约束了,但是由于做MR的人一般权限较高,其实这还是一个痛点。

  2. CI/CD

    采用lerna后项目结构为多项目,不能按照原来的方式在提交时触发该整体项目的CI/CD,而是需要在CI/CD层接入文件提交检查,在分别对不同目录的文件有提交时再处理对应的独立CI/CD流程。

思考

相比与subtree或submodule,lerna的共用层完全由业务侧开发者按照常规方式进行使用和管理,是现今最为流行的一种依赖共享解决方案。但同时,随着使用共用模块的项目在仓库中增多,仓库的体积会变得很大,代码的权限管理会带来一定的风险。因此,monorepo还是适合单SPA在多端的业务层复用,多MFE间的复用还是需要考虑npm package优先。

posted on 2021-12-27 18:38  两只小蚂蚁  阅读(160)  评论(0编辑  收藏  举报