创建多人协作 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 中,可以:
- 实时提示:编写代码的同时,指出语法错误和风格问题;
- 自动修复:在保存文件时自动修复常见错误,如缩进、引号类型等,省心;
- 减少错误:帮助捕获未定义变量、未使用变量等潜在错误,降低 bug 发生概率。
下面以 VSCode 为例,配置 eslint 插件。
- 在扩展市场中查找并安装名为 ESLint 的插件(Microsoft 出品);
- 在项目根目录配置
.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.json 的 lint-staged 项中添加配置:
"*.{js,mjs,cjs,vue,json,css,less,scss,vue,html,md}": "prettier --write"
monorepo
区别于一个仓库一个项目的模式(姑且称为 multirepo),monorepo 将关联的项目集中在一个仓库中,共享工程配置和模块代码。
二者对比
| 模式 | multirepo | monorepo |
|---|---|---|
| 代码可见性 | 代码隔离,研发者只需关注自己负责的仓库 | 一个仓库中多个相关项目,很容易看到整个代码库的变化趋势,更好的团队协作<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/*"



![[译] 终于,JavaScript 有了安全的数组方法](https://img.jackatlas.xyz/blog/article-cover/cmfkkq9a00001uhcg5k63cmt3.ba9b3.jpg)

