人人都会AI编程

Husky + lint-staged + commitlint 提交规范

更新时间:2026-07-10

在团队协作中,代码提交的规范程度直接影响项目的可维护性和回溯效率。如果每一次 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 commitgit 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 在配置了这三件套之后,内部流程如下:

  1. 开发者将修改 git add 到暂存区。
  2. 执行 git commit,Husky 触发 pre-commit 钩子。
  3. pre-commit 钩子运行 lint-staged,读取暂存区中的文件列表,分别执行 ESLint 和 Prettier。如果检查失败,提交中断。
  4. 如果 pre-commit 钩子中还定义了测试或其他脚本,则继续执行,全部通过后进入提交信息阶段。
  5. Husky 触发 commit-msg 钩子,commitlint 校验输入的信息格式。如果不符合规范,提交中断。
  6. 所有检查通过,Git 正式创建提交。

整个过程中,开发者不需要记住额外的手动命令,不符合规范的内容会被自动拦截并给出明确的错误提示。

常见问题与真实场景建议

1. 如何让团队成员无感知地启用?

因为 Husky 通过 prepare 脚本自动安装钩子,团队成员在 npm install 后即可获得完整的钩子配置。如果有团队成员使用 --ignore-scripts 安装依赖,需确保项目文档中说明必须执行 npx husky installnpm 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 加载不同包的规则。使用 pnpmyarn workspace 时,需确保钩子脚本在根目录执行且可以访问所有子包的配置。

小结

Husky、lint-staged 和 commitlint 的组合是当前 Node.js 项目中最主流的提交规范方案。它把重复、机械的检查工作自动化,让团队专注于业务逻辑,而不用花精力争论代码格式和信息规范。三个工具各司其职,配置简洁,投入成本极低,却能从根本上提升代码仓库的质量和可追溯性,是工程化实践中扎实的一步。