代码规范与 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-plugin | TypeScript 专属规则 |
eslint-plugin-react | React JSX 规则 |
eslint-plugin-react-hooks | React Hooks 规则(依赖数组检查等) |
eslint-plugin-vue | Vue 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 示例(推荐):
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 示例(旧版参考):
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 配置
{
"semi": true,
"singleQuote": true,
"trailingComma": "all",
"printWidth": 80,
"tabWidth": 2,
"useTabs": false,
"bracketSpacing": true,
"arrowParens": "always",
"endOfLine": "lf"
}
忽略不需要格式化的文件:
dist/
node_modules/
pnpm-lock.yaml
*.min.js
ESLint vs Prettier
ESLint 管代码质量,Prettier 管代码格式。 两者职责不同,应该配合使用而非二选一。
| 维度 | ESLint | Prettier |
|---|---|---|
| 定位 | 代码质量 + 部分格式检查 | 纯代码格式化 |
| 检查内容 | 未使用变量、隐式类型转换、Hooks 规则等 | 缩进、换行、引号、分号、尾逗号等 |
| 是否可修复 | 部分规则支持 --fix | 全部可自动修复 |
| 配置灵活度 | 数百条规则可单独配置 | 极少配置项(强约定) |
| 支持语言 | JS/TS(通过插件扩展) | JS/TS/CSS/HTML/JSON/Markdown 等 |
ESLint 与 Prettier 协作方案
ESLint 和 Prettier 在某些格式规则上可能产生冲突(例如 ESLint 要求双引号,Prettier 配置了单引号)。解决方案是使用 eslint-config-prettier:
- npm
- Yarn
- pnpm
- Bun
npm install --save-dev prettier eslint-config-prettier
yarn add --dev prettier eslint-config-prettier
pnpm add --save-dev prettier eslint-config-prettier
bun add --dev prettier eslint-config-prettier
在 Flat Config 中集成:
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 配置示例:
{
"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
- Yarn
- pnpm
- Bun
npm install --save-dev husky lint-staged
yarn add --dev husky lint-staged
pnpm add --save-dev husky lint-staged
bun add --dev husky lint-staged
- npm
- yarn
- pnpm
npx husky init
yarn husky init
pnpm husky init
husky init 会自动:
- 在
package.json中添加prepare脚本 - 创建
.husky/pre-commit钩子文件
配置 pre-commit 钩子
npx lint-staged
配置 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 | 修复 Bug | fix(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 |
ci | CI/CD 配置变更 | ci: add GitHub Actions |
revert | 回退提交 | revert: revert "feat: ..." |
安装与配置
- npm
- Yarn
- pnpm
- Bun
npm install --save-dev @commitlint/cli @commitlint/config-conventional
yarn add --dev @commitlint/cli @commitlint/config-conventional
pnpm add --save-dev @commitlint/cli @commitlint/config-conventional
bun add --dev @commitlint/cli @commitlint/config-conventional
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 钩子
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
- Yarn
- pnpm
- Bun
npm install --save-dev commitizen cz-conventional-changelog
yarn add --dev commitizen cz-conventional-changelog
pnpm add --save-dev commitizen cz-conventional-changelog
bun add --dev commitizen cz-conventional-changelog
# 在 package.json 中配置
# "config": { "commitizen": { "path": "cz-conventional-changelog" } }
# 然后使用 npx cz 代替 git commit
五、EditorConfig -- 编辑器配置统一
EditorConfig 用于在不同编辑器和 IDE 之间统一基础编码格式。它的优势是不依赖任何 Node.js 工具链,几乎所有主流编辑器都原生支持或有插件支持。
# 表示这是最顶层的 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 在编辑器层面统一基础格式(缩进、换行符等),Prettier 在工具链层面格式化代码。两者并不冲突:EditorConfig 保证开发者输入时的一致性,Prettier 保证输出时的一致性。建议两者都配置。
六、Stylelint -- CSS 代码检查
Stylelint 是 CSS/SCSS/Less 的 Lint 工具,类似于 ESLint 之于 JavaScript。它可以检测 CSS 中的错误、强制属性排序、防止重复声明等。
安装与配置
- npm
- Yarn
- pnpm
- Bun
npm install --save-dev stylelint stylelint-config-standard stylelint-config-recess-order stylelint-config-prettier-scss
yarn add --dev stylelint stylelint-config-standard stylelint-config-recess-order stylelint-config-prettier-scss
pnpm add --save-dev stylelint stylelint-config-standard stylelint-config-recess-order stylelint-config-prettier-scss
bun add --dev stylelint stylelint-config-standard stylelint-config-recess-order stylelint-config-prettier-scss
- npm
- Yarn
- pnpm
- Bun
npm install --save-dev stylelint-config-standard-scss
yarn add --dev stylelint-config-standard-scss
pnpm add --save-dev stylelint-config-standard-scss
bun add --dev stylelint-config-standard-scss
{
"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 核心配置
{
"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/
一键初始化脚本
你可以编写一个初始化脚本,帮助团队快速搭建规范工具链:
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 的定位完全不同:
| 维度 | ESLint | Prettier |
|---|---|---|
| 核心职责 | 代码质量检查(逻辑错误、最佳实践) | 代码格式化(外观统一) |
| 典型规则 | no-unused-vars、no-implicit-coercion、react-hooks/exhaustive-deps | 缩进、引号、分号、换行、尾逗号 |
| 自动修复 | 部分规则支持 --fix | 100% 可自动修复 |
| 配置哲学 | 高度可配置,数百条规则 | 强约定,极少配置项 |
为什么要同时使用:
- ESLint 擅长发现代码质量问题(如未使用的变量、错误的 Hooks 使用方式、潜在的类型问题),这些 Prettier 无法处理。
- Prettier 擅长统一代码外观,且支持 CSS、JSON、Markdown 等多种语言,覆盖面比 ESLint 的格式规则更广。
- 单独使用 ESLint 做格式化会导致大量格式规则需要配置和维护;单独使用 Prettier 又无法发现逻辑问题。两者配合是最佳方案。
解决冲突的方法:
安装 eslint-config-prettier,它会关闭 ESLint 中所有与 Prettier 冲突的格式规则:
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-commit 和 commit-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
统一最基础的编码格式,确保不同编辑器的开发者写出一致的代码:
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
- Yarn
- pnpm
- Bun
npm install --save-dev eslint typescript-eslint @eslint/js stylelint stylelint-config-standard-scss
yarn add --dev eslint typescript-eslint @eslint/js stylelint stylelint-config-standard-scss
pnpm add --save-dev eslint typescript-eslint @eslint/js stylelint stylelint-config-standard-scss
bun add --dev eslint typescript-eslint @eslint/js stylelint stylelint-config-standard-scss
3. 代码格式层 -- Prettier
统一代码风格,消除团队内的格式争论:
- npm
- Yarn
- pnpm
- Bun
npm install --save-dev prettier eslint-config-prettier
yarn add --dev prettier eslint-config-prettier
pnpm add --save-dev prettier eslint-config-prettier
bun add --dev prettier eslint-config-prettier
4. Git 提交层 -- Husky + lint-staged + commitlint
在提交时强制执行检查,确保不合规代码无法进入仓库:
- npm
- Yarn
- pnpm
- Bun
npm install --save-dev husky lint-staged @commitlint/cli @commitlint/config-conventional
yarn add --dev husky lint-staged @commitlint/cli @commitlint/config-conventional
pnpm add --save-dev husky lint-staged @commitlint/cli @commitlint/config-conventional
bun add --dev husky lint-staged @commitlint/cli @commitlint/config-conventional
- npm
- yarn
- pnpm
npx husky init
yarn husky init
pnpm husky init
5. CI/CD 层 -- 流水线兜底
在 CI 中运行全量检查,作为最后一道防线:
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 是两个定位完全不同的工具,理解它们的职责边界是解决冲突的关键。
职责划分:
| 维度 | ESLint | Prettier |
|---|---|---|
| 核心职责 | 代码质量(逻辑正确性、最佳实践) | 代码格式(外观统一) |
| 检查示例 | 未使用的变量、== 改 ===、Hooks 规则 | 缩进、引号、分号、换行、尾逗号 |
| 可修复性 | 部分规则支持 --fix | 100% 可自动修复 |
| 配置哲学 | 高度可配置,数百条规则 | 强约定(Opinionated),极少配置项 |
| 支持语言 | JS/TS(通过插件扩展) | JS/TS/CSS/HTML/JSON/Markdown 等 |
为什么需要两者配合?
- ESLint 能发现
no-unused-vars、react-hooks/exhaustive-deps等逻辑问题,这些 Prettier 无法处理 - Prettier 能统一 CSS、JSON、Markdown 等多语言的格式,覆盖面远超 ESLint
- 单独使用其中一个都有缺陷:ESLint 格式化能力弱且规则维护成本高;Prettier 无法检查代码逻辑
冲突来源与解决方案:
ESLint 中有一些规则也涉及格式(如 indent、quotes、semi),这些规则可能与 Prettier 的格式化结果冲突。解决方案是使用 eslint-config-prettier:
- npm
- Yarn
- pnpm
- Bun
npm install --save-dev eslint-config-prettier
yarn add --dev eslint-config-prettier
pnpm add --save-dev eslint-config-prettier
bun add --dev eslint-config-prettier
eslint-config-prettier 的作用是关闭 ESLint 中所有与 Prettier 冲突的格式规则,让 ESLint 只管质量,Prettier 只管格式:
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,它会将 Prettier 作为 ESLint 的一条规则运行,在 ESLint 检查时同时执行格式化。但这种方式会拖慢 ESLint 的执行速度,且错误报告混杂在一起不易区分。现在的推荐做法是:ESLint 和 Prettier 分别独立运行,用 eslint-config-prettier 关闭冲突规则即可。
推荐的 VSCode 配置——保存时自动执行两者:
{
// 保存时自动用 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 等)产出的基础格式一致:
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 配置——只检查暂存区文件:
{
"lint-staged": {
"*.{ts,tsx}": [
"eslint --fix", // 先执行 ESLint 修复
"prettier --write" // 再执行 Prettier 格式化
],
"*.{css,scss}": [
"stylelint --fix",
"prettier --write"
],
"*.{json,md,html}": [
"prettier --write"
]
}
}
假设项目有 500 个文件,本次只修改了 3 个。全量 eslint src/ 可能需要 30 秒以上,且历史遗留的规范问题会阻塞当前提交。lint-staged 只检查暂存区的 3 个文件,1-2 秒即可完成,不受历史代码影响。
commitlint 配置——规范提交信息:
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 流水线也会进行全量检查:
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 | 达到全量合规 |
- 一步到位开启所有规则:历史代码大量报错,团队抵触
- 只配置不执行:没有 Git Hooks 和 CI 检查,全靠人工自觉
- 缺少 VSCode 统一配置:每个人的编辑器行为不一致
- 没有提供修复工具:只报错不提供
--fix,增加开发者负担
正确做法:先配好工具链(ESLint + Prettier + Husky + CI),再渐进式开启规则。