跳到主要内容

保存、提交与 CI

规范工具本身并不难。更能拉开差异的,往往不是工具装上了,而是这套规则有没有接进日常流程。

如果链路没接好,常见情况会是:

  • 本地没人跑 lint
  • 提交前总能漏掉问题
  • CI 只在最后时刻把人拦下来
  • 工具很多,但每个人的使用方式都不一样

这篇重点看的是:怎样把代码规范从“文档要求”变成“日常默认行为”。

一条比较常见的落地链路

比较稳的团队链路通常是:

  1. 编辑器保存时自动处理一部分问题
  2. Git 提交前只检查暂存文件
  3. CI 再检查全项目

这三层的分工很清楚:

  • 保存:反馈最快
  • 提交:拦住明显遗漏
  • CI:保证仓库基线一致

编辑器保存这一层

这是最先影响日常手感的一层。

常见动作

  • 自动格式化
  • 自动修复一部分 ESLint / Biome 问题
  • 自动整理 imports

VS Code 常见写法

如果项目是 ESLint + Prettier

{
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
}
}

如果项目是 Biome:

{
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.biome": "explicit",
"source.organizeImports.biome": "explicit"
}
}

这里更重要的不是具体配置项,而是团队要统一:

  • 到底谁来管格式
  • 到底谁来修 imports
  • 保存时允许修到什么程度

提交前这一层

提交前最常见的做法,是只检查暂存文件,而不是每次全仓 lint。

因为全仓检查通常会有几个问题:

  • 太慢
  • 容易误伤不相关历史文件
  • 提交体验很差

常见工具组合

  • husky
  • lint-staged

一个常见配置

package.json

{
"lint-staged": {
"*.{js,jsx,ts,tsx}": [
"eslint --fix",
"prettier --write"
],
"*.{css,scss,vue}": [
"stylelint --fix",
"prettier --write"
],
"*.{json,md,yml,yaml}": [
"prettier --write"
]
}
}

.husky/pre-commit

pnpm lint-staged

这套组合的价值很直接:

  • 只处理当前提交涉及的文件
  • 尽量让低价值问题在提交前自动收掉

CI 这一层怎么放

CI 不是拿来替代本地规范的。它更像最后的仓库守门员。

比较常见的 CI 任务会拆成几步:

{
"scripts": {
"lint": "eslint .",
"format:check": "prettier . --check",
"lint:style": "stylelint \"**/*.{css,scss,vue}\"",
"typecheck": "tsc --noEmit"
}
}

CI 里更适合跑的是:

  • 全量 lint
  • 格式检查
  • 样式检查
  • 类型检查

如果项目已经用了 Biome,也可以改成更统一的入口。

为什么不要把所有事都堆给 pre-commit

很多团队一开始会想:那就把所有检查全塞进提交前。

问题很快就会出现:

  • 提交很慢
  • 临时保存的小改动也会被拖住
  • 开发体验变差

更稳的分工通常是:

  • pre-commit:只管暂存文件、只做必要检查
  • CI:负责全量和兜底

Monorepo 里怎么想这条链路

monorepo 里更要关心“范围”而不是“工具名”。

常见问题是:

  • 改了一个包,却 lint 全仓
  • 某个 app 的规则影响另一个 app
  • hooks 和 CI 的入口不一致

更稳的思路一般是:

  • 先按工作区或包划定命令范围
  • 再决定哪些检查跑局部,哪些跑全量

团队最容易忽略的一层:失败信息要读得懂

规范链路如果很难用,很多时候不是规则太严,而是报错太难看。

例如:

  • 报错里没有文件范围
  • 同一问题被三个工具重复提示
  • 提交失败但不知道下一步该跑什么命令

更好的做法通常是:

  • 入口命令统一
  • 输出信息尽量短
  • 文档里明确“本地修复命令”

一套比较稳的脚本结构

{
"scripts": {
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"format": "prettier . --write",
"format:check": "prettier . --check",
"lint:style": "stylelint \"**/*.{css,scss,vue}\"",
"lint:style:fix": "stylelint \"**/*.{css,scss,vue}\" --fix",
"typecheck": "tsc --noEmit",
"check": "pnpm typecheck && pnpm lint && pnpm format:check"
}
}

这样团队里至少能形成一个共识:

  • 本地修什么
  • 提交前跑什么
  • CI 跑什么

老项目接这条链路,怎么更稳

老项目通常不要一步到位全接满。

比较稳的顺序一般是:

  1. 先接 Prettier
  2. 再接基础 ESLint
  3. 再接 pre-commit
  4. 最后再把全量检查放进 CI

这样做的原因很简单:

  • 每一步都更容易定位问题
  • 不会一次性制造太多历史噪音
  • 团队更容易适应

更适合先记住的主线

规范链路的目标,不是“工具装齐”,而是:

  • 尽量在最早的时机发现问题
  • 尽量让机器自动处理低价值问题
  • 尽量把人的注意力留给更重要的代码和设计判断

如果这条线跑顺了,规范才算真的落地。