在团队协作中,代码提交的规范程度直接影响项目的可维护性和回溯效率。如果每一次 Git 提交都靠人工检查代码格式、运行测试、校验提交信息格式,难免会出现遗漏,久而久之仓库就会变得凌乱。Husky、lint-staged 和 commitlint 这三个工具组成了一套成熟的提交规范自动化方案,能够在 Git 操作的关键节点强制运行质量检查,从源头阻断不合规的代码和信息进入仓库。
为什么需要自动化提交规范
许多团队都会制定代码规范和提交信息格式要求,例如:
- 代码必须通过 ESLint 检查,不能有格式化问题。
- 提交信息要遵循 Conventional Commits 规范,如
feat: 新增用户登录接口、fix: 修复导出空指针异常。 - 每次提交前希望自动运行相关单元测试,避免提交有破坏性的代码。
但这些规则如果只靠口头约定或者 CI 后的反馈,存在明显的效率短板:代码已经推送到远端仓库,修复不规范的提交历史或者格式问题往往需要额外的“修复提交”甚至 rebase 操作。更理想的做法是在 git commit 之前就自动拦截不合规的内容,让团队形成“提交即合规”的习惯。
Husky 负责在 Git 生命周期钩子(如 pre-commit、commit-msg)中触发脚本,lint-staged 负责只检查本次修改的文件以提升速度,commitlint 则专门校验提交信息的格式。三者配合,形成了一条自动化的质量防线。
Husky:Git 钩子的现代管理方式
Husky 是一个流行的 Git 钩子管理工具,允许开发者在 package.json 或配置文件中声明各类 Git 钩子对应的命令。安装后,当本地执行 git commit、git push 等操作时,Husky 会自动运行预设的脚本。
安装(推荐使用 Husky v9+ 原生 ESM 版本):
npm install husky --save-dev
npx husky init
执行 npx husky init 后,项目根目录会自动创建 .husky/ 文件夹,并在其中生成一个 pre-commit 示例钩子,同时在 package.json 中添加 "prepare": "husky" 脚本,确保每次安装依赖后钩子就绪。
配置 pre-commit 钩子:
在 .husky/pre-commit 文件中,我们可以简单编写需要执行的命令:
# .husky/pre-commit
npx lint-staged
这样,每一次 git commit 都会先执行 lint-staged,由它来精细化处理暂存区文件。如果需要额外的检查,也可以继续追加命令,例如运行单元测试:
# .husky/pre-commit
npx lint-staged
npm test
Husky 本身只是一个钩子管理器,不参与具体的检查逻辑。它把 Git 钩子的配置从手动编写 .git/hooks 文件演进为项目可追踪的配置文件,使得团队成员 clone 仓库后自动生效,不再需要每个人手动设置。
lint-staged:只检查改动过的文件
如果每次提交都对整个项目执行 ESLint 或 Prettier,随着项目文件增多,检查时间会线性增长,影响开发体验。lint-staged 解决了这个问题:它接收一个文件列表(来自 git 暂存区),然后针对这些文件运行指定的 linter 和格式化命令。
安装:
npm install lint-staged --save-dev
配置:
在 package.json 中添加 lint-staged 字段,或者使用单独的配置文件 lint-staged.config.js。典型的配置如下:
{
"lint-staged": {
"*.{js,ts}": [
"eslint --fix",
"prettier --write"
],
"*.{json,md,yml}": [
"prettier --write"
]
}
}
这段配置的含义是:
- 对所有暂存区中的
.js、.ts文件,先执行eslint --fix自动修复语法和风格问题,再执行prettier --write统一格式。 - 对
.json、.md、.yml等非脚本文件,直接使用prettier --write格式化。
如果 eslint --fix 修复后仍有错误(例如无法自动修复的规则违反),lint-staged 会以非零状态码退出,从而阻止提交。开发者需要手动修正后再尝试提交。正因为限制在暂存区文件,lint-staged 通常能在几百毫秒内完成检查,几乎感受不到延迟。
进阶用法:
对于 TypeScript 项目,还可以在 lint-staged 中加入 tsc --noEmit 进行类型检查,但要注意全量类型检查耗时较长,一般建议将类型检查放在 CI 环节,而不在 pre-commit 中阻断。如果确需在提交前检查,可以使用 tsc-files 这类只检查特定文件的工具进行加速。
commitlint:标准化提交信息
即使代码格式完美,提交信息如果随意书写(如 “改了点东西”、“fix”),之后的代码审查、变更日志生成、版本发布都会受阻。commitlint 是一个提交信息校验工具,能够强制执行你所选定的提交规范,最常用的是基于 Conventional Commits 的 @commitlint/config-conventional。
安装:
npm install @commitlint/cli @commitlint/config-conventional --save-dev
配置文件 commitlint.config.js:
module.exports = {
extends: ['@commitlint/config-conventional'],
};
在 Husky 中添加 commit-msg 钩子:
# 手动创建 .husky/commit-msg 文件并写入
npx husky add .husky/commit-msg 'npx --no -- commitlint --edit $1'
# 如果 husky add 不可用,直接创建文件:
echo "npx --no -- commitlint --edit \$1" > .husky/commit-msg
这样,当执行 git commit -m "信息" 时,commitlint 会读取 .git/COMMIT_EDITMSG 中的信息并校验。不符合规范的提交将被拒绝。
常见的 Conventional Commits 格式:
<type>(scope): <subject>
[optional body]
[optional footer]
类型包括:
feat: 新功能fix: 缺陷修复docs: 文档变更style: 代码格式(不影响功能,如空格、分号)refactor: 重构(非新功能也非修 bug)perf: 性能优化test: 添加或修改测试chore: 构建过程或辅助工具的变动
示例:feat(user): 新增手机号验证码登录、fix(order): 修复金额计算精度问题。
通过这种统一格式,可以自动生成 CHANGELOG 并确定下一个语义化版本号,实现从提交到发布的自动化流水线。
三者的协作流程
一次标准的 git commit 在配置了这三件套之后,内部流程如下:
- 开发者将修改
git add到暂存区。 - 执行
git commit,Husky 触发pre-commit钩子。 pre-commit钩子运行lint-staged,读取暂存区中的文件列表,分别执行 ESLint 和 Prettier。如果检查失败,提交中断。- 如果
pre-commit钩子中还定义了测试或其他脚本,则继续执行,全部通过后进入提交信息阶段。 - Husky 触发
commit-msg钩子,commitlint 校验输入的信息格式。如果不符合规范,提交中断。 - 所有检查通过,Git 正式创建提交。
整个过程中,开发者不需要记住额外的手动命令,不符合规范的内容会被自动拦截并给出明确的错误提示。
常见问题与真实场景建议
1. 如何让团队成员无感知地启用?
因为 Husky 通过 prepare 脚本自动安装钩子,团队成员在 npm install 后即可获得完整的钩子配置。如果有团队成员使用 --ignore-scripts 安装依赖,需确保项目文档中说明必须执行 npx husky install 或 npm run prepare。
2. 提交信息填写错误想绕过?
通常不建议绕过,但确有紧急情况时,可以使用 git commit --no-verify 跳过钩子。但最好要求此类提交在后续通过 rebase 整理信息,避免破坏规范化记录。
3. lint-staged 执行速度慢怎么办?
确认只检查暂存区文件,且避免在 pre-commit 中加入 tsc --noEmit 这种全量类型检查,可将其移至 CI 阶段。如果 ESLint 仍慢,可尝试 eslint --cache 只检查修改过的文件。
4. 在使用 CI 时还需要这些本地钩子吗?
需要。本地钩子提供即时反馈,避免将不规范的代码推送到远端再被 CI 拒绝,浪费 CI 资源和修复时间。本地钩子是第一道防线,CI 是第二道,二者互补。
5. 如何与 monorepo 结合?
在 monorepo 根目录安装 Husky,配合 lint-staged 的 --config 选项指向不同的配置文件,或通过 commitlint 的 --config 加载不同包的规则。使用 pnpm 或 yarn workspace 时,需确保钩子脚本在根目录执行且可以访问所有子包的配置。
小结
Husky、lint-staged 和 commitlint 的组合是当前 Node.js 项目中最主流的提交规范方案。它把重复、机械的检查工作自动化,让团队专注于业务逻辑,而不用花精力争论代码格式和信息规范。三个工具各司其职,配置简洁,投入成本极低,却能从根本上提升代码仓库的质量和可追溯性,是工程化实践中扎实的一步。