跳到主要内容

代码规范与 Lint

问题

前端项目中如何建立统一的代码规范?ESLint、Prettier、Husky、commitlint 等工具分别承担什么角色?如何让它们协同工作?

答案

代码规范是前端工程化的基石。一个成熟的团队项目通常需要多个工具协同配合,覆盖代码质量检查代码风格格式化Git 提交规范编辑器配置统一等多个层面。

工具全景图

核心原则

代码规范工具链的设计目标是:开发时自动提示,保存时自动格式化,提交时强制检查。让规范融入日常工作流,而非依赖人工审查。


一、ESLint -- 代码质量守护者

ESLint 是 JavaScript/TypeScript 生态中最主流的静态代码分析工具,用于发现和修复代码中的问题。它不仅能检查语法错误,还能强制执行最佳实践和团队约定的编码规范。

核心概念

1. 规则(Rules)

规则是 ESLint 的最小检查单元,每条规则检查代码的一个特定方面。规则有三种级别:

级别含义
"off"0关闭规则
"warn"1警告(不阻断流程)
"error"2错误(退出码非 0,阻断 CI)
规则配置示例
// 规则可以只设置级别,也可以带配置选项
const rules = {
"no-console": "warn", // 禁止 console,警告级别
"no-unused-vars": "error", // 禁止未使用变量,错误级别
"eqeqeq": ["error", "always"], // 必须使用 === ,附带选项
"max-lines-per-function": ["warn", { max: 50 }], // 函数最多 50 行
};

2. 插件(Plugins)

插件是一组规则的集合,用于扩展 ESLint 的检查能力。常见插件:

插件用途
@typescript-eslint/eslint-pluginTypeScript 专属规则
eslint-plugin-reactReact JSX 规则
eslint-plugin-react-hooksReact Hooks 规则(依赖数组检查等)
eslint-plugin-vueVue SFC 规则
eslint-plugin-import模块导入规则(排序、路径检查)
eslint-plugin-jsx-a11y无障碍可访问性检查

3. 解析器(Parser)

ESLint 默认只能解析标准 JavaScript。如果要检查 TypeScript 或 Vue SFC,需要使用对应的解析器:

解析器用途
@typescript-eslint/parser解析 TypeScript 代码
vue-eslint-parser解析 Vue 单文件组件
@babel/eslint-parser解析 Babel 转译的代码(实验性语法)

4. 配置文件格式:Flat Config vs Legacy

ESLint v9 起默认使用 Flat Config(扁平配置),这是当前推荐的配置方式。

注意

ESLint v9+ 默认只识别 eslint.config.js(Flat Config)。旧版的 .eslintrc.* 格式(Legacy Config)已不再推荐使用。如果你正在维护老项目,建议逐步迁移到 Flat Config。

特性Flat Config(推荐)Legacy Config(已弃用)
配置文件eslint.config.js / eslint.config.mjs.eslintrc.js / .eslintrc.json / .eslintrc.yml
导出格式配置对象数组单个配置对象
插件引入import 后直接赋值字符串名称
extends不需要,用展开运算符extends: [...]
忽略文件配置中 ignores 字段独立的 .eslintignore 文件
生效范围通过 files 精确控制通过 overrides 控制

Flat Config 示例(推荐):

eslint.config.mjs
import eslint from "@eslint/js";
import tseslint from "typescript-eslint";
import pluginReact from "eslint-plugin-react";
import pluginReactHooks from "eslint-plugin-react-hooks";
import pluginImport from "eslint-plugin-import";

export default tseslint.config(
// 全局忽略
{
ignores: ["dist/", "node_modules/", "*.config.js"],
},
// ESLint 推荐规则
eslint.configs.recommended,
// TypeScript 推荐规则(展开数组)
...tseslint.configs.recommended,
// 自定义配置
{
files: ["**/*.{ts,tsx}"],
plugins: {
react: pluginReact,
"react-hooks": pluginReactHooks,
import: pluginImport,
},
languageOptions: {
parserOptions: {
ecmaFeatures: { jsx: true },
},
},
rules: {
"react/react-in-jsx-scope": "off", // React 17+ 不需要引入 React
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn",
"import/order": [
"error",
{
groups: ["builtin", "external", "internal", "parent", "sibling"],
"newlines-between": "always",
},
],
"@typescript-eslint/no-explicit-any": "warn",
"@typescript-eslint/no-unused-vars": [
"error",
{ argsIgnorePattern: "^_" }, // 下划线开头的参数允许未使用
],
},
}
);

Legacy Config 示例(旧版参考):

.eslintrc.cjs(旧版格式)
module.exports = {
root: true,
parser: "@typescript-eslint/parser",
extends: [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:react/recommended",
"plugin:react-hooks/recommended",
"prettier", // 放在最后,关闭与 Prettier 冲突的规则
],
plugins: ["@typescript-eslint", "react", "import"],
rules: {
"react/react-in-jsx-scope": "off",
"@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }],
},
settings: {
react: { version: "detect" },
},
};

二、Prettier -- 代码格式化专家

Prettier 是一个**强约定(Opinionated)**的代码格式化工具。它的核心理念是:减少格式争论,让团队使用统一的代码风格。与 ESLint 不同,Prettier 只关注代码的外观(缩进、换行、引号等),不关心代码逻辑。

Prettier 配置

.prettierrc
{
"semi": true,
"singleQuote": true,
"trailingComma": "all",
"printWidth": 80,
"tabWidth": 2,
"useTabs": false,
"bracketSpacing": true,
"arrowParens": "always",
"endOfLine": "lf"
}

忽略不需要格式化的文件:

.prettierignore
dist/
node_modules/
pnpm-lock.yaml
*.min.js

ESLint vs Prettier

一句话总结

ESLint 管代码质量,Prettier 管代码格式。 两者职责不同,应该配合使用而非二选一。

维度ESLintPrettier
定位代码质量 + 部分格式检查纯代码格式化
检查内容未使用变量、隐式类型转换、Hooks 规则等缩进、换行、引号、分号、尾逗号等
是否可修复部分规则支持 --fix全部可自动修复
配置灵活度数百条规则可单独配置极少配置项(强约定)
支持语言JS/TS(通过插件扩展)JS/TS/CSS/HTML/JSON/Markdown 等

ESLint 与 Prettier 协作方案

ESLint 和 Prettier 在某些格式规则上可能产生冲突(例如 ESLint 要求双引号,Prettier 配置了单引号)。解决方案是使用 eslint-config-prettier

npm install --save-dev prettier eslint-config-prettier

在 Flat Config 中集成:

eslint.config.mjs
import eslint from "@eslint/js";
import prettierConfig from "eslint-config-prettier";
import tseslint from "typescript-eslint";

export default tseslint.config(
eslint.configs.recommended,
...tseslint.configs.recommended,
// eslint-config-prettier 必须放在最后,关闭所有与 Prettier 冲突的 ESLint 规则
prettierConfig,
{
rules: {
// 你的自定义规则...
},
}
);
最佳实践

推荐的工作流是:ESLint 只负责代码质量规则,所有格式化相关的规则全部交给 Prettier。通过 eslint-config-prettier 关闭 ESLint 中与 Prettier 冲突的格式规则。在 VSCode 中配置保存时自动运行 Prettier 格式化和 ESLint 修复。

VSCode 配置示例:

.vscode/settings.json
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
},
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}

三、Husky + lint-staged -- Git 提交守门员

即使开发者本地没有配置编辑器插件,也应该在 Git 提交时强制执行检查。Husky 用于管理 Git Hooks,lint-staged 用于只对暂存区(staged)的文件执行检查,避免全量扫描带来的性能问题。

安装与初始化

npm install --save-dev husky lint-staged
npx husky init

husky init 会自动:

  1. package.json 中添加 prepare 脚本
  2. 创建 .husky/pre-commit 钩子文件

配置 pre-commit 钩子

.husky/pre-commit
npx lint-staged

配置 lint-staged

package.json 中或单独的配置文件中定义 lint-staged 的行为:

package.json(lint-staged 部分)
{
"lint-staged": {
"*.{ts,tsx}": ["eslint --fix", "prettier --write"],
"*.{css,scss}": ["stylelint --fix", "prettier --write"],
"*.{json,md}": ["prettier --write"]
}
}
工作原理

lint-staged 只对 git add 加入暂存区的文件执行命令。假设你修改了 10 个文件但只暂存了 3 个,lint-staged 只会检查这 3 个文件。这大幅提升了检查速度,也避免了因他人代码不规范而阻塞自己的提交。

执行流程

注意

如果 ESLint 发现了无法自动修复的错误(如未使用的变量、类型错误等),lint-staged 会中断提交流程。开发者需要手动修复后重新提交。这是保障代码质量的最后一道防线。


四、commitlint -- 提交信息规范

commitlint 用于校验 Git 提交信息是否符合规范。配合 Conventional Commits 规范,可以实现自动生成 CHANGELOG、语义化版本管理等能力。

Conventional Commits 格式

<type>(<scope>): <subject>

<body>

<footer>

常用的 type 类型:

type说明示例
feat新功能feat(auth): add login page
fix修复 Bugfix(api): handle null response
docs文档变更docs: update README
style代码格式(不影响逻辑)style: fix indentation
refactor重构(非新增功能、非修复)refactor(utils): simplify helper
perf性能优化perf(list): add virtual scroll
test测试相关test(auth): add login tests
chore构建/工具链变更chore: update eslint config
ciCI/CD 配置变更ci: add GitHub Actions
revert回退提交revert: revert "feat: ..."

安装与配置

npm install --save-dev @commitlint/cli @commitlint/config-conventional
commitlint.config.ts
import type { UserConfig } from "@commitlint/types";

const config: UserConfig = {
extends: ["@commitlint/config-conventional"],
rules: {
"type-enum": [
2,
"always",
["feat", "fix", "docs", "style", "refactor", "perf", "test", "chore", "ci", "revert"],
],
"subject-max-length": [2, "always", 72], // 标题最长 72 个字符
"body-max-line-length": [1, "always", 200], // body 每行最长 200 字符
},
};

export default config;

配置 commit-msg 钩子

.husky/commit-msg
npx --no -- commitlint --edit $1

这样每次 git commit 时,Husky 会先触发 pre-commit(lint-staged),再触发 commit-msg(commitlint),双重保障。

错误示例

# 错误:缺少 type
git commit -m "add login page"
# commitlint 报错: subject may not be empty, type may not be empty

# 错误:type 不在允许列表中
git commit -m "feature: add login page"
# commitlint 报错: type must be one of [feat, fix, docs, ...]

# 正确
git commit -m "feat(auth): add login page"
辅助工具

可以使用 commitizen 提供交互式提交界面,让开发者通过选择菜单来生成符合规范的提交信息,降低记忆成本:

npm install --save-dev commitizen cz-conventional-changelog
# 在 package.json 中配置
# "config": { "commitizen": { "path": "cz-conventional-changelog" } }
# 然后使用 npx cz 代替 git commit

五、EditorConfig -- 编辑器配置统一

EditorConfig 用于在不同编辑器和 IDE 之间统一基础编码格式。它的优势是不依赖任何 Node.js 工具链,几乎所有主流编辑器都原生支持或有插件支持。

.editorconfig
# 表示这是最顶层的 EditorConfig 文件
root = true

# 所有文件的通用配置
[*]
charset = utf-8
end_of_line = lf
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true

# Markdown 文件允许行尾空格(用于换行语法)
[*.md]
trim_trailing_whitespace = false

# Makefile 必须使用 Tab 缩进
[Makefile]
indent_style = tab
EditorConfig vs Prettier

EditorConfig 在编辑器层面统一基础格式(缩进、换行符等),Prettier 在工具链层面格式化代码。两者并不冲突:EditorConfig 保证开发者输入时的一致性,Prettier 保证输出时的一致性。建议两者都配置。


六、Stylelint -- CSS 代码检查

Stylelint 是 CSS/SCSS/Less 的 Lint 工具,类似于 ESLint 之于 JavaScript。它可以检测 CSS 中的错误、强制属性排序、防止重复声明等。

安装与配置

npm install --save-dev stylelint stylelint-config-standard stylelint-config-recess-order stylelint-config-prettier-scss
npm install --save-dev stylelint-config-standard-scss
.stylelintrc.json
{
"extends": [
"stylelint-config-standard-scss",
"stylelint-config-recess-order",
"stylelint-config-prettier-scss"
],
"rules": {
"selector-class-pattern": "^[a-z][a-zA-Z0-9]+$",
"color-named": "never",
"declaration-block-no-duplicate-properties": true,
"no-descending-specificity": true,
"scss/no-global-function-names": null
},
"ignoreFiles": ["dist/**", "node_modules/**"]
}

属性排序示例

使用 stylelint-config-recess-order 可以强制 CSS 属性按 Recess 顺序(定位 > 盒模型 > 排版 > 视觉 > 其他)排列:

正确的属性顺序
.card {
/* 定位 */
position: relative;
top: 0;

/* 盒模型 */
display: flex;
width: 200px;
padding: 16px;
margin: 8px;

/* 排版 */
font-size: 14px;
line-height: 1.5;

/* 视觉 */
color: #333;
background-color: #fff;
border: 1px solid #eee;
border-radius: 8px;

/* 其他 */
cursor: pointer;
transition: all 0.3s ease;
}

七、完整的团队配置示例

下面是一个完整的团队代码规范工具链配置,涵盖所有环节:

package.json 核心配置

package.json
{
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"lint": "eslint . --fix",
"lint:style": "stylelint \"src/**/*.{css,scss}\" --fix",
"format": "prettier --write \"src/**/*.{ts,tsx,css,scss,json,md}\"",
"prepare": "husky"
},
"lint-staged": {
"*.{ts,tsx}": [
"eslint --fix",
"prettier --write"
],
"*.{css,scss}": [
"stylelint --fix",
"prettier --write"
],
"*.{json,md,html}": [
"prettier --write"
]
},
"devDependencies": {
"@commitlint/cli": "^19.0.0",
"@commitlint/config-conventional": "^19.0.0",
"@eslint/js": "^9.0.0",
"eslint": "^9.0.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-react": "^7.35.0",
"eslint-plugin-react-hooks": "^5.0.0",
"husky": "^9.0.0",
"lint-staged": "^15.0.0",
"prettier": "^3.0.0",
"stylelint": "^16.0.0",
"stylelint-config-standard-scss": "^13.0.0",
"stylelint-config-recess-order": "^5.0.0",
"typescript-eslint": "^8.0.0"
}
}

项目文件结构

project/
├── .editorconfig # 编辑器统一配置
├── .husky/
│ ├── pre-commit # 提交前执行 lint-staged
│ └── commit-msg # 校验提交信息
├── .vscode/
│ └── settings.json # VSCode 统一配置
├── .prettierrc # Prettier 配置
├── .prettierignore # Prettier 忽略文件
├── .stylelintrc.json # Stylelint 配置
├── commitlint.config.ts # commitlint 配置
├── eslint.config.mjs # ESLint Flat Config
├── package.json # lint-staged 配置 + 脚本
└── src/

一键初始化脚本

你可以编写一个初始化脚本,帮助团队快速搭建规范工具链:

scripts/init-lint.ts
import { execSync } from "node:child_process";
import { writeFileSync } from "node:fs";

// 安装所有依赖
const deps: string[] = [
"eslint",
"@eslint/js",
"typescript-eslint",
"eslint-config-prettier",
"prettier",
"husky",
"lint-staged",
"@commitlint/cli",
"@commitlint/config-conventional",
"stylelint",
"stylelint-config-standard-scss",
"stylelint-config-recess-order",
];

execSync(`pnpm add -D ${deps.join(" ")}`, { stdio: "inherit" });

// 初始化 Husky
execSync("npx husky init", { stdio: "inherit" });

// 创建 pre-commit 钩子
writeFileSync(".husky/pre-commit", "npx lint-staged\n");

// 创建 commit-msg 钩子
writeFileSync(".husky/commit-msg", "npx --no -- commitlint --edit $1\n");

console.log("代码规范工具链初始化完成!");

常见面试问题

Q1: ESLint 和 Prettier 有什么区别?为什么要同时使用?如何解决它们之间的冲突?

答案

ESLint 和 Prettier 的定位完全不同:

维度ESLintPrettier
核心职责代码质量检查(逻辑错误、最佳实践)代码格式化(外观统一)
典型规则no-unused-varsno-implicit-coercionreact-hooks/exhaustive-deps缩进、引号、分号、换行、尾逗号
自动修复部分规则支持 --fix100% 可自动修复
配置哲学高度可配置,数百条规则强约定,极少配置项

为什么要同时使用:

  • ESLint 擅长发现代码质量问题(如未使用的变量、错误的 Hooks 使用方式、潜在的类型问题),这些 Prettier 无法处理。
  • Prettier 擅长统一代码外观,且支持 CSS、JSON、Markdown 等多种语言,覆盖面比 ESLint 的格式规则更广。
  • 单独使用 ESLint 做格式化会导致大量格式规则需要配置和维护;单独使用 Prettier 又无法发现逻辑问题。两者配合是最佳方案。

解决冲突的方法:

安装 eslint-config-prettier,它会关闭 ESLint 中所有与 Prettier 冲突的格式规则:

eslint.config.mjs
import eslint from "@eslint/js";
import prettierConfig from "eslint-config-prettier";
import tseslint from "typescript-eslint";

export default tseslint.config(
eslint.configs.recommended,
...tseslint.configs.recommended,
prettierConfig, // 必须放在最后
);

这样 ESLint 只管质量,Prettier 只管格式,各司其职,互不冲突。


Q2: Husky + lint-staged 的工作原理是什么?为什么不直接对全量代码运行 ESLint?

答案

Husky 的原理:

Husky 利用 Git 的 Hooks 机制。Git 在执行特定操作(如 commit、push)前后会触发对应的钩子脚本。Husky 通过在 .husky/ 目录下创建钩子脚本,在 git commit 时自动触发 pre-commitcommit-msg 钩子。

package.json 中的 "prepare": "husky" 脚本会在 pnpm install 后自动执行,确保每个克隆项目的开发者都会安装 Git Hooks。

lint-staged 的原理:

lint-staged 通过 git diff --staged --name-only 获取暂存区文件列表,然后只对这些文件执行指定的命令。

为什么不全量检查:

对比分析
// 假设项目有 500 个 TS 文件,本次只修改了 3 个

// 方案一:全量检查(不推荐)
// eslint src/ --fix
// 耗时:可能 30 秒以上
// 问题:历史遗留代码的错误会阻塞当前提交

// 方案二:lint-staged(推荐)
// 只检查 3 个暂存文件
// 耗时:1-2 秒
// 优势:不受历史代码影响,开发体验好
方案检查范围耗时历史代码影响
全量 eslint src/全部文件较慢历史错误会阻塞提交
lint-staged仅暂存文件极快不受影响

全量检查更适合在 CI/CD 流水线中执行(作为兜底),而 lint-staged 用于本地开发提交时的快速检查。


Q3: 如何从零搭建一套完整的团队代码规范方案?需要考虑哪些方面?

答案

搭建团队代码规范需要覆盖以下 5 个层面:

1. 编辑器层 -- EditorConfig

统一最基础的编码格式,确保不同编辑器的开发者写出一致的代码:

.editorconfig
root = true

[*]
charset = utf-8
end_of_line = lf
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true

2. 代码质量层 -- ESLint + Stylelint

检查 JS/TS 和 CSS 的代码质量:

npm install --save-dev eslint typescript-eslint @eslint/js stylelint stylelint-config-standard-scss

3. 代码格式层 -- Prettier

统一代码风格,消除团队内的格式争论:

npm install --save-dev prettier eslint-config-prettier

4. Git 提交层 -- Husky + lint-staged + commitlint

在提交时强制执行检查,确保不合规代码无法进入仓库:

npm install --save-dev husky lint-staged @commitlint/cli @commitlint/config-conventional
npx husky init

5. CI/CD 层 -- 流水线兜底

在 CI 中运行全量检查,作为最后一道防线:

.github/workflows/lint.yml
name: Lint
on: [pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: "pnpm"
- run: pnpm install
- run: pnpm lint
- run: pnpm lint:style

实施建议:

阶段建议
新项目一开始就配置完整的工具链,全量规则开启
老项目迁移先以 warn 级别引入,逐步升级为 error
团队推广提供 VSCode 配置和初始化脚本,降低接入成本
持续维护定期更新工具版本,关注 ESLint Flat Config 生态

Q4: ESLint 和 Prettier 的职责区别是什么?如何让它们协同工作?

答案

ESLint 和 Prettier 是两个定位完全不同的工具,理解它们的职责边界是解决冲突的关键。

职责划分:

维度ESLintPrettier
核心职责代码质量(逻辑正确性、最佳实践)代码格式(外观统一)
检查示例未使用的变量、=====、Hooks 规则缩进、引号、分号、换行、尾逗号
可修复性部分规则支持 --fix100% 可自动修复
配置哲学高度可配置,数百条规则强约定(Opinionated),极少配置项
支持语言JS/TS(通过插件扩展)JS/TS/CSS/HTML/JSON/Markdown 等

为什么需要两者配合?

  • ESLint 能发现 no-unused-varsreact-hooks/exhaustive-deps逻辑问题,这些 Prettier 无法处理
  • Prettier 能统一 CSS、JSON、Markdown 等多语言的格式,覆盖面远超 ESLint
  • 单独使用其中一个都有缺陷:ESLint 格式化能力弱且规则维护成本高;Prettier 无法检查代码逻辑

冲突来源与解决方案:

ESLint 中有一些规则也涉及格式(如 indentquotessemi),这些规则可能与 Prettier 的格式化结果冲突。解决方案是使用 eslint-config-prettier

npm install --save-dev eslint-config-prettier

eslint-config-prettier 的作用是关闭 ESLint 中所有与 Prettier 冲突的格式规则,让 ESLint 只管质量,Prettier 只管格式:

eslint.config.mjs — Flat Config 集成
import eslint from "@eslint/js";
import tseslint from "typescript-eslint";
import prettierConfig from "eslint-config-prettier";

export default tseslint.config(
eslint.configs.recommended,
...tseslint.configs.recommended,
prettierConfig, // 必须放在最后!关闭所有与 Prettier 冲突的 ESLint 规则
{
rules: {
// 这里只配置代码质量规则,不配置格式规则
"@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }],
"react-hooks/exhaustive-deps": "warn",
},
}
);
eslint-plugin-prettier 还需要吗?

早期社区推荐使用 eslint-plugin-prettier,它会将 Prettier 作为 ESLint 的一条规则运行,在 ESLint 检查时同时执行格式化。但这种方式会拖慢 ESLint 的执行速度,且错误报告混杂在一起不易区分。现在的推荐做法是:ESLint 和 Prettier 分别独立运行,用 eslint-config-prettier 关闭冲突规则即可。

推荐的 VSCode 配置——保存时自动执行两者:

.vscode/settings.json
{
// 保存时自动用 Prettier 格式化
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
// 保存时自动运行 ESLint 修复
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
},
"[typescript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" },
"[typescriptreact]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }
}

这样开发者在保存文件时,Prettier 先格式化代码外观,ESLint 再修复可自动修复的质量问题,两者互不干扰。


Q5: 如何在团队中落地代码规范?Git Hooks 的作用是什么?

答案

在团队中落地代码规范,仅靠"口头约定"或"文档说明"是远远不够的。需要通过工具链自动化来强制执行,确保不合规代码无法进入代码仓库。

落地代码规范的五个层次:

第一层:EditorConfig — 统一编辑器行为

确保不同编辑器(VSCode、WebStorm、Vim 等)产出的基础格式一致:

.editorconfig
root = true

[*]
charset = utf-8
end_of_line = lf
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true

第二/三层:ESLint + Prettier — 代码检查与格式化

开发时提供实时提示和自动修复,提前发现问题。

第四层:Git Hooks — 提交时强制检查(核心)

这是落地代码规范的关键环节。通过 Husky + lint-staged + commitlint 在 Git 操作时自动执行检查:

完整配置步骤
// 1. 安装依赖
// pnpm add -D husky lint-staged @commitlint/cli @commitlint/config-conventional

// 2. 初始化 Husky
// pnpm husky init

// 3. 配置 pre-commit 钩子 — 代码检查
// .husky/pre-commit 文件内容:
// npx lint-staged

// 4. 配置 commit-msg 钩子 — 提交信息校验
// .husky/commit-msg 文件内容:
// npx --no -- commitlint --edit $1

lint-staged 配置——只检查暂存区文件:

package.json
{
"lint-staged": {
"*.{ts,tsx}": [
"eslint --fix", // 先执行 ESLint 修复
"prettier --write" // 再执行 Prettier 格式化
],
"*.{css,scss}": [
"stylelint --fix",
"prettier --write"
],
"*.{json,md,html}": [
"prettier --write"
]
}
}
为什么用 lint-staged 而不是全量检查?

假设项目有 500 个文件,本次只修改了 3 个。全量 eslint src/ 可能需要 30 秒以上,且历史遗留的规范问题会阻塞当前提交。lint-staged 只检查暂存区的 3 个文件,1-2 秒即可完成,不受历史代码影响。

commitlint 配置——规范提交信息:

commitlint.config.ts
import type { UserConfig } from "@commitlint/types";

const config: UserConfig = {
extends: ["@commitlint/config-conventional"],
rules: {
"type-enum": [
2, "always",
["feat", "fix", "docs", "style", "refactor", "perf", "test", "chore", "ci", "revert"],
],
"subject-max-length": [2, "always", 72],
},
};

export default config;

提交信息必须符合 type(scope): subject 格式,否则 commitlint 会阻止提交:

# 错误 — commitlint 报错
git commit -m "add login page"

# 正确
git commit -m "feat(auth): add login page"

第五层:CI/CD 兜底——最后一道防线

即使开发者跳过了 Git Hooks(如 git commit --no-verify),CI 流水线也会进行全量检查:

.github/workflows/lint.yml
name: Code Quality
on: [pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: "pnpm"
- run: pnpm install --frozen-lockfile
- run: pnpm lint # ESLint 全量检查
- run: pnpm tsc --noEmit # TypeScript 类型检查
- run: pnpm lint:style # Stylelint 检查

渐进式落地策略(针对老项目):

阶段策略说明
第一阶段新文件 error,旧文件 warn只对新代码严格要求
第二阶段lint-staged 只检查暂存文件不影响存量代码
第三阶段CI 中对增量代码执行 error 级别防止新的不合规代码合入
第四阶段逐步修复存量代码,全面开启 error达到全量合规
常见的落地失败原因
  1. 一步到位开启所有规则:历史代码大量报错,团队抵触
  2. 只配置不执行:没有 Git Hooks 和 CI 检查,全靠人工自觉
  3. 缺少 VSCode 统一配置:每个人的编辑器行为不一致
  4. 没有提供修复工具:只报错不提供 --fix,增加开发者负担

正确做法:先配好工具链(ESLint + Prettier + Husky + CI),再渐进式开启规则。


相关链接