多项目管理Monorepo技术方案调研

878 阅读6分钟

背景介绍

在多个项目开发管理的过程中,大多都会有这样的一个困扰,我们在开发A项目的过程中,发现有个功能和B项目中的一个功能类似,简单修改一下就可以拿来用,同样在开发C项目的工程中,也会遇到和A项目中相似功能的场景,一般遇到这样的情况,我们可能就需要复制粘贴到当前项目中使用,这样看来,也没有什么太大的问题;但是,随着业务量越来越大,项目中相似的功能越来越多,我们每次渠去到不同的项目仓库中copy代码,就会显得影响开发效率,还有一个更大的问题是,后期维护的话,如果某个功能改动量很大,我们是不是需要在每个仓库中都去修改一遍。基于上面的情况,此次调研了Monorepo策略来实现多个项目的管理

多个项目存在的问题

1. 仓库多,管理混乱

  • 同一个小组的项目,被分成多个仓库,不利于管理
  • 新人上手难度大,需要熟悉多个仓库的代码目录

2.代码复用率低

  • 公共组件和方法,只能通过copy的形式或npm包的形式公用
  • 对每个仓库熟悉的情况下,会造成过多重复开发,影响迭代效率

3.开发规范不统一

  • 相同类型的项目,却有着不同代码风格校验规范(ESlint、prettier等 )
  • 每个项目使用不同的工具库,管理混乱

什么是Monorepo

Monorepo 全称 Monolithic Repository,Monorepo 是一种将多个项目代码存储在一个仓库里的软件开发策略,指单个仓库可包含许多相关但独立的项目,可以由同一个团队或多个团队管理。这使得跨项目共享和管理代码变得更加容易。

Monorepo 的优势

  • 仓库中的每个项目,都可分离出来运行;
  • 共享组件&方法更简单,只需引用根目录的组件或者方法即可;
  • 维护方便,每次只需要修改一个地方,子项目每次打包发布便会同步更新;

无标题流程图.png

Monorepo策略整体架构,会先看到的是一个整体,然后在里面再分出各个端的内容,最后通过 webpack 打包多页面的方法,把各端自己的内容串起来,打包出各端的内容。

其他技术方案调研

1. npm包

我们一开始遇到代码复用的场景,最容易想到的就是将公共部分打包成一个npm包,放在私有npm仓库中,各个项目,npm install,然后直接拿来使用,npm包的形式,也算是一个办法,但相对来说,存在的一些缺点还是不能忽视的:

  • 随着项目体量增大,会需要更多的npm包,后期就需要维护更多的npm包
  • 一旦npm包有更新,就需要各个项目重新npm install, 才能使用到最新的功能

2. 微前端-Fusion前端融合组件

这种形式和npm包类似,针对不同的功能,需要维护多个仓库进行管理;另外Fusion前端融合组件,设计的初衷,是为了公司内部各个业务部门,对一些需要项目依赖的业务,而开发的一套融合方案,如果没有夸部门相互调用的场景,开发成本有点大,同样还需要后期维护;

Monorepo策略存在的缺点

  • 仓库管理权限不可控:由于多个项目在一个仓库,就需要给所有开发者管理员权限;
  • 新员工上手难度大:在 monorepo 策略下,新人可能不得不花更多精力来理清各个代码仓库之间的相互逻辑;
  • 更新同步问题:由于每个项目的迭代周期不一致,如果公共组件或者方法有修改,一些项目迭代不及时,会导致落后的版本越来越多,对后期上线,造成不可估量的风险;

Monorepo策略实践

  1. 全局安装pnpm npm install -g pnpm
  2. 新建文件夹,然后在文件夹根目录执行 pnpm init, 初始化项目
  3. 创建 pnpm-workspace.yaml、.npmrc
  • pnpm-workspace.yaml是开启workspaces的配置文件 packages: - '**' 这个配置指示 pnpm 在名为packages的文件夹下查找所有的项目

  • .npmrc用来配置信息,engine-strict=true结合根目录的package.json中的engines字段,可以指定运行的node版和pnpm版本 engine-strict=true

  • 配置根目录下的package.json文件,启用workspaces功能

    "workspaces": {
        "packages": ["packages/*"]
    }
    
  1. 创建子项目,放在新建的packages文件夹,并为每个pnpm init 每个子项目,生成package.json文件 packages projectA package.json projectB package.json

  2. 安装项目依赖

    在项目根目录下运行 pnpm install,pnpm 将会自动识别workspaces,并在所有子项目中安装依赖项。此时根目录下会生成node_modules的文件夹。这个文件夹中的.pnpm子文件夹包含了所有已安装的包,而子项目的node_modules文件夹中包含了指向这些包的链接。

  3. 安装共享依赖项

    多个项目放在一个仓库,我们通常希望能够共享一些依赖项,比如lodash、classes等,此时我们就可以在根目录中的package.json文件中,添加这些需要共享的依赖项:

    "dependencies":{
        "lodash": "^4.17.21"
    }
    

    重新在根目录执行pnpm install 后,就可以在所有子项目中使用lodash, 而不需要在每个子项目的package.json文件中单独声明它。

  4. 项目脚本命令

    使用pnpm 可以在根目录下运行子项目的脚本。 例如:在projectA项目中的package.json中定义一个名为start的脚本

        "scripts": {
            "start": "node ./index.js"
        }
    

    然后在根目录运行 pnpm --filter projectA run start 即可执行projectA的start脚本命令,后面可以配置打包命令

  5. 执行全局命令

    如果配置了单元测试,只需要在根目录中的package.json文件中配置test命令,每次执行的时候,pnpm会将所有子项目中的test脚本都执行

        "scripts": {
            "test": "pnpm run test --filter ./packages/*"
        }
    

总结

Monorepo是一个很好的多项目管理策略,但针对当前团队项目的现状,不宜进行整合,原因如下:

  • 拆分组件工作量:由于之前的多个项目是分布在多个仓库管理的,一些相似组件的调用,都是copy的形式,现在需要单独拆分出来,还需要考虑各个项目调用的差异,进行重新封装
  • 业务逻辑接入工作量:完成了上面所说的公共组件或者方法的拆分,还需要每个项目再重新接入,有一定的工作量,而且涉及到的功能模块,都需要测试回归功能完好,才能重新上线;
  • 仓库管理权限分配:项目合到一个仓库,仓库权限管理混乱,如果改动了公共逻辑,可能会造成其他项目,无法正常打包上线;
  • 迭代周期问题:由于不同的项目,有不同的迭代发布日期,如果改了公共部分逻辑,A项目发布上线,对一些不常更新的项目,欠债越来越越多,导致风险不可预估;