文章

代码规范工具深度解析:ESLint、Prettier与Husky的工程化实践

前端代码规范体系是职责分离三件套:ESLint 管质量(写得对不对)、Prettier 管风格(好不好看)、Husky 加 lint-staged 管提交门槛。 三者组成从编辑器到仓库的完整质量防线,面试常考 ESLint 基于 AST 的工作方式与 lint-staged 的暂存区过滤。

代码规范工具深度解析:ESLint、Prettier与Husky的工程化实践

一句话概括

前端代码规范体系的本质是职责分离三件套:ESLint 管「代码质量」(你写得对不对)、Prettier 管「代码风格」(你写得好不好看)、Husky + lint-staged 管「提交门槛」(不规范不让提交)——三者组成从编辑器到仓库的完整质量防线。

核心知识点

1. ESLint:基于 AST 的代码质量分析器

ESLint 将源码解析为 AST,遍历每种节点类型并执行规则。你可以用 Flat Config 清晰声明规则:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// eslint.config.js(ESLint 9+ Flat Config)
import js from '@eslint/js';

export default [
  js.configs.recommended,
  {
    rules: {
      'no-console': ['warn', { allow: ['warn', 'error'] }],
      'no-var': 'error',
      'prefer-const': 'error',
      'eqeqeq': ['error', 'always'],  // 必须用 === 而非 ==
    },
  },
  {
    ignores: ['dist/**', 'node_modules/**', '.next/**'],
  },
];

Flat Config 的核心优势:数组顺序即应用顺序,后面的规则可以覆盖前面的,彻底告别旧版 extends 的隐式继承迷宫。

2. Prettier:固执己见的确定性格式化

Prettier 和 ESLint 的根本区别:ESLint 告诉你”应该(不)做什么”,Prettier 强制”代码必须长什么样”。Prettier 基于 Philip Wadler 的”A prettier printer”论文,使用 Doc IR(中间表示)实现确定性格式化——相同代码 + 相同配置 = 永远相同输出

1
2
3
4
5
6
7
8
9
// prettier.config.js
export default {
  printWidth: 100,
  tabWidth: 2,
  singleQuote: true,
  semi: true,
  trailingComma: 'all',
  arrowParens: 'always',
};

与 ESLint 的职责分离:用 eslint-config-prettier 关闭 ESLint 中所有格式化相关规则,让 ESLint 只做质量检查,格式化全部交给 Prettier。

1
2
3
4
5
6
7
8
// eslint.config.js
import eslintConfigPrettier from 'eslint-config-prettier';

export default [
  js.configs.recommended,
  // ...自定义质量规则
  eslintConfigPrettier,  // ⚠️ 必须放最后,关闭冲突的样式规则
];

3. Husky 9+:Git Hook 的轻量管理器

Husky 9+ 彻底重写,不再依赖 package.json 的配置字段,纯粹基于 .husky/ 目录的 shell 脚本:

1
2
3
4
5
6
7
8
9
10
# 初始化
npx husky init

# .husky/pre-commit — commit 前自动检查
#!/usr/bin/env sh
npx lint-staged

# .husky/commit-msg — 校验 commit message
#!/usr/bin/env sh
npx --no -- commitlint --edit $1

底层机制:npx husky 在 .git/hooks/ 创建符号链接指向 .husky/_/husky.sh,运行时自动查找 .husky/ 下对应 hook 名的脚本。设计师之所以巧妙:钩子脚本在仓库版本控制中,而 .git/hooks 本身不提交。

4. lint-staged:只检查暂存区文件

1000 个文件的仓库只改了 3 个,为什么跑全量 ESLint?lint-staged 只读取 git diff --staged --name-only 的输出,对暂存区文件运行指定命令:

1
2
3
4
5
6
// lint-staged.config.js
export default {
  '*.{ts,tsx}': ['eslint --fix', 'prettier --write'],
  '*.{json,md,css}': ['prettier --write'],
  '*.{png,jpg,svg}': ['imagemin-lint-staged'],  // 图片压缩检查
};

核心价值:1 秒完成 3 个文件的检查,不会因为其他 997 个文件的历史遗留问题阻塞 commit。

5. 自定义 ESLint 规则:防魔数

理解 ESLint 核心机制的最佳实践是手写一条规则:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// no-magic-numbers.js
export default {
  meta: { type: 'suggestion', docs: { description: '禁止未命名的数字常量' } },
  create(context) {
    return {
      Literal(node) {
        if (typeof node.value !== 'number') return;
        // 排除默认值、数组索引等合理场景
        const { parent } = node;
        if (['VariableDeclarator', 'ArrayExpression', 'SwitchCase'].includes(parent.type)) return;
        if (parent.type === 'BinaryExpression') {
          context.report({ node, message: `魔数 ${node.value},建议定义为命名常量` });
        }
      },
    };
  },
};

其实你每天都在用

  • VS Code 保存时自动格式化:ESLint + Prettier 扩展的 editor.codeActionsOnSave 配置,保存瞬间自动修复
  • git commit 被 lint-staged 拦截:commt 前 eslint --fix 自动修复可修复的问题,失败时 commit 被阻止
  • console.log 被 ESLint 报 warning:no-console: 'warn' 防止调试代码遗漏到生产
  • 团队成员用不同缩进却不出冲突:Prettier 在 commit 前统一格式化,个人编辑器偏好无所谓
  • CI 流水线中 --max-warnings 0:任何 ESLint 警告都会导致 CI 失败,强制团队零警告

常见误解(FAQ)

❌ 误区 1:「ESLint 和 Prettier 可以互相替代」

ESLint 管”质量”(有没有未使用的变量、是否用了 ==),Prettier 管”格式”(换行、缩进、引号)。它们是互补关系,不是替代关系。eslint --fix 虽然也能格式化,但远不如 Prettier 的确定性算法稳定。

❌ 误区 2:「lint-staged 只是锦上添花,不是必须品」

不做 lint-staged 的危害:项目中有 1000 个文件,早期遗留了 50 个 lint 错误。每次 commit 都跑全量检查,你修改的 3 个文件没问题,但被第 501 个文件的旧错误阻塞——而且那不是你造成的。lint-staged 就是解决”谁污染谁治理”的工具。

❌ 误区 3:「Husky 和 lint-staged 是一回事」

Husky 管理 Git Hook(什么时候触发),lint-staged 执行具体命令(触发后干什么)。两者职责不同:你可以用 Husky 触发其他 hook(如 pre-push 跑测试),而 lint-staged 也可以直接通过 npm scripts 调用,不依赖 Husky。

❌ 误区 4:「配置好工具就行了,Rule 越严格越好」

过度严格的规则适得其反:max-lines-per-function: 20 在工具函数里会让人强行拆分出无意义的”伪函数”;无差别禁止 any 会导致大量 // eslint-disable-next-line。好的规范是约束关键问题,而非寸草不生。

一句话总结

代码规范工具不是给开发者戴枷锁,而是用自动化约束消除团队无意义的风格争论——与其在 Code Review 里讨论”这里该用单引号还是双引号”,不如让 Prettier 在保存那一瞬间就替你决定了。

本文由作者按照 CC BY 4.0 进行授权