JackAtlas
创建多人协作 monorepo
about 4 years ago
前端开发

创建多人协作 monorepo

创建多人协作 monorepo

多人协作

在多人协作的项目中,制定规范不仅仅是提升“代码美感”,更是为了建立一套自动化的信任机制。以下是从规范制定、约束工具到自动修正三个维度的分析:

1. 规范先行:从“个人习惯”转向“团队共识”

多人协作的核心痛点在于每个开发者都有独特的编程习惯。如果没有统一的规范,代码库会迅速演变成各种风格杂糅的“乱葬岗”,增加维护和 Code Review 的心智负担。

统一语境: 规范确立了团队内通用的命名规则、目录结构和逻辑组织方式,确保任何成员打开新模块时,都能像阅读自己写的代码一样自然。

降低沟通成本: 当风格成为一种默认共识,讨论就能从“缩进用空格还是制表符”这种琐事,回归到业务逻辑和架构设计的核心问题上。

2. 强制约束:将规范转化为“刚性红线”

口头约定往往是无效的,真正的规范必须依托工具进行硬性约束。

静态分析(Linting): 引入 ESLint 等工具,在代码编写阶段实时扫描。任何违反规范的行为都会触发报错或警告,让问题在进入代码仓库前被拦截。

流程守卫: 结合 Git Hooks(如 Husky)或 CI/CD 流水线,在提交代码(git commit)或推送代码(git push)时进行自动化检查。只有符合规范的代码才具备合入主分支的资格。

3. 自动修正:实现“无感”的规范落地

规范的落地不应增加开发者的负担,而应通过自动化手段实现效率的双赢。

即写即改: 通过配置编辑器和 IDE 的“保存时自动格式化”功能,配合 Prettier 等工具,可以在保存瞬间完成缩进、引号、换行等格式的自动修整。

一键重构: 利用自动化脚本对存量代码进行批量扫描与修复,确保整站代码风格的高度一致性,实现“所见即规范”。

具体实现

1. git 提交规范

用到以下的工具:

  • @commitlint@cli commit 信息的校验工具
  • @commitlint/config-conventional 提供一套校验规则
  • commitizen 引导开发者写 commit 的工具
  • cz-git commitizen 的增强插件,对中文支持友好。

拦截不合规的提交

// 安装 husky 和 commitlint
pnpm add -D husky
pnpm add -D @commitlint/{cli,config-conventional}

// 初始化 husky
pnpm dlx husky install

// 配置 commit-msg 钩子
pnpm dlx husky add .husky/commit-msg 'pnpm dlx --no -- commitlint --edit ${1}'

当尝试提交不符合规范的信息时,会被拦截和报错。可以通过编辑 commitlint.config.js 来配置团队的提交风格,具体配置项参考官方文档

如何书写合规的提交

// 安装 commitizen 和 cz-git
npm install -D commitizen cz-git

package.json 中配置 config 字段,指定使用 cz-git 作为适配器:

{
  "scripts": {
    "commit": "git-cz"
  },
  "config": {
    "commitizen": {
      "path": "node_modules/cz-git"
    }
  }
}

cz-git 可以读取 commitlint.config.js 中的配置,开发者在终端中输入 pnpm commit 即可在终端中通过交互式菜单的引导自动生成符合规范的提交信息。

2. eslint 代码规范

集成到开发工具中

将 eslint 集成到编辑器和 IDE 中,可以:

  1. 实时提示:编写代码的同时,指出语法错误和风格问题;
  2. 自动修复:在保存文件时自动修复常见错误,如缩进、引号类型等,省心;
  3. 减少错误:帮助捕获未定义变量、未使用变量等潜在错误,降低 bug 发生概率。

下面以 VSCode 为例,配置 eslint 插件。

  1. 在扩展市场中查找并安装名为 ESLint 的插件(Microsoft 出品);
  2. 在项目根目录配置 .vscode/settings.json 文件
{
  "editor.formatOnSave": false, // 保存时自动格式化(否)
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": "neve" // 保存时自动修复 eslint 错误(从不)
  }
}
  • never 保存时从不触发
  • explicit 显式保存时触发
  • always 所有保存操作皆触发

项目根目录中可能还会存在名为 eslint.config.js.eslintrc.json.eslintrc.yaml.eslintrc 等的文件,这些文件是配置具体规则的文件,eslint 插件会自动读取。

另外,右键 eslint 插件 -> 添加到工作区建议,就会在 extensions.json 文件中增加对该插件的推荐,这样其他成员用 VSCode 打开该项目时就会收到推荐。

创建配置文件

通过 pnpm dlx eslint --init 初始化 eslint 配置文件,在初始化的过程中,eslint 会问你一系列问题来辅助生成适合项目的配置文件。如果想添加更丰富的配置项,可以参考官网中文网

手动运行 eslint

// package.json
"scripts": {
  "lint": "eslint . --ext .js,.mjs,.cjs,.vue",
  "lint:fix": "eslint . --ext .js,.mjs,.cjs,.vue --fix"
}

在预提交阶段自动执行 eslint

除了之前用到的 husky,还需要 lint-staged

pnpm add -D lint-staged

使用 husky 添加一个 pre-commit 的钩子:

pnpm dlx husky add .husky/pre-commit "pnpm dlx lint-staged"

package.json 中添加 lint-staged 配置,指定在预提交阶段要检查的文件及对应的命令。

{
  "lint-staged": {
    "*.{js,mjs,cjs,vue}": "eslint --fix"
  }
}

3. 代码格式化

eslint 的主要功能在于代码检查并给出提示,而 prittier 则更着眼于代码格式化,我常将这二者结合使用。

集成到开发工具

还是以 VSCode 为例,在扩展市场中找到名为 Prettier - Code formatter 的插件并安装,在 setting.json 中配置:

"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode"

同样也可以右键添加到工作区建议。

配置

同样的 prettier 也有配置文件 prittier.config.js,通常放在根目录。

与 eslint 冲突

有时候 eslint 和 prittier 会产生冲突,一般是这两个工具的配置不一致导致的,此时可以手动对其两边的配置,也可以安装 eslint-config-prettier 的 eslint 插件,用 prettier 的配置来覆盖 eslint 中的冲突配置。

预提交阶段美化

同样地,prettier 也可与 husky 集成,在项目中安装 prittier:

pnpm add -D prittier

package.jsonlint-staged 项中添加配置:

  "*.{js,mjs,cjs,vue,json,css,less,scss,vue,html,md}": "prettier --write"

monorepo

区别于一个仓库一个项目的模式(姑且称为 multirepo),monorepo 将关联的项目集中在一个仓库中,共享工程配置和模块代码。

二者对比

模式multirepomonorepo
代码可见性代码隔离,研发者只需关注自己负责的仓库一个仓库中多个相关项目,很容易看到整个代码库的变化趋势,更好的团队协作<br>增加了非 owner 改代码的风险
依赖管理多个仓库各有自己的 node_modules,存在依赖重复安装情况,占用磁盘空间大多项目代码都在一个仓库中,相同版本依赖提升到顶层只安装一次,节省磁盘空间
代码权限各项目单独仓库,不会出现代码被误改的情况,单个项目出现问题不会影响其他项目多个项目代码都在一个仓库中,没有项目粒度的权限管控,一个项目出问题,可能影响其他项目
开发迭代仓库体积小,模块划分清晰<br>多仓库来回切换,项目多的话效率很低。多仓库存在依赖时,需要手动 npm link,操作繁琐关联项目在一个仓库中,方便宏观安排<br>代码复用高,方便重构<br>依赖调试方便,依赖包迭代场景下,借助工具自动 npm link,简化操作流程
工程配置各项目构建、打包、代码校验都各自维护,维护耗费大,易出错多项目工程配置一致,代码风格和质量也很容易一致
构建部署多个项目间存在依赖,部署时需要手动到不同的仓库根据先后顺序修改版本进行部署,操作繁琐效率低构建工具可以配置依赖项目的构建优先级,可实现一次命令完成所有的部署

仓库的目录结构:

.
├── apps
│   ├── admin
│   │   └── package.json
│   └── tenant
│       └── package.json
├── package.json
├── packages
│   ├── api
│   │   └── package.json
│   ├── eslint-config
│   │   └── package.json
│   ├── shared
│   │   └── package.json
│   ├── typescript-config
│   │   └── package.json
│   └── ui
│       └── package.json
└── pnpm-lock.yaml
  • 应用(apps):独立的项目,通常互相隔离。
  • 包(packages):共享的模块,比如组件库、eslint 配置等。一个包可以是另一个包的依赖,也可以是应用的依赖。
  • package.json 文件:描述应用或包的元数据,每个应用或包必须具备
// pnpm-workspace.yaml
packages:
  - "packages/*"
  - "apps/*"