代码质量
前言
前端工程化体系中,代码质量保障是不可或缺的一环。随着项目规模的增长,人工 Code Review 难以覆盖所有细节,自动化工具链成为保障代码一致性和可维护性的关键手段。本文系统梳理前端代码质量工具链的各个组成部分,涵盖 ESLint、Prettier、Husky、Commitlint、EditorConfig 等工具的配置与实践,并给出完整的工具链整合流程。
ESLint
ESLint 是目前最流行的 JavaScript 静态代码分析工具,能够发现代码中的模式问题、潜在 Bug 以及风格不一致之处。
基本安装与初始化
npm init @eslint/config
# 或手动安装
npm install eslint --save-dev初始化命令会以交互式方式引导你选择项目使用的框架(React / Vue)、是否使用 TypeScript、代码运行环境(浏览器 / Node.js)等,自动生成 ESLint 配置文件。
配置文件格式
ESLint 支持多种配置文件格式,按优先级从高到低依次为:
eslint.config.js(扁平配置,ESLint v9 默认).eslintrc.js.eslintrc.cjs.eslintrc.yaml.eslintrc.json- 或在
package.json中声明eslintConfig字段
parser 配置
ESLint 默认使用内置的 Espree 解析器。当项目使用 TypeScript 或实验性语法时,需要配置特定解析器:
// .eslintrc.cjs
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
ecmaFeatures: { jsx: true }
}
};解析器说明:
| 解析器 | 适用场景 | 需要安装的包 |
|---|---|---|
| Espree | 标准 JavaScript | eslint(内置) |
@typescript-eslint/parser | TypeScript | @typescript-eslint/parser |
@babel/eslint-parser | Babel 转译项目 | @babel/core, @babel/eslint-parser |
vue-eslint-parser | Vue SFC | vue-eslint-parser |
extends 配置
extends 用于继承预设的规则集,可以理解为"配置的配置":
// .eslintrc.cjs
module.exports = {
extends: [
'eslint:recommended',
'plugin:react/recommended',
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended' // 务必放在最后
]
};常见 extends 预设:
eslint:recommended— ESLint 官方推荐规则eslint:all— 所有内置规则(不推荐用于生产)plugin:react/recommended— React 推荐规则plugin:react/jsx-runtime— React JSX 转换(React 17+)plugin:react-hooks/recommended— React Hooks 规则plugin:@typescript-eslint/recommended— TypeScript 推荐规则plugin:vue/vue3-recommended— Vue 3 推荐规则plugin:prettier/recommended— Prettier 兼容规则(必须最后)
plugins 配置
插件为 ESLint 提供额外的规则和功能:
// .eslintrc.cjs
module.exports = {
plugins: ['react', '@typescript-eslint', 'import', 'jsx-a11y']
};当从 plugin: 前缀引用规则时,插件会自动被加载,因此通常无需显式声明 plugins 字段,但显式声明可以更清晰地表达依赖关系。
规则定制
规则是 ESLint 的核心,每个规则有三种级别:
"off"或0— 关闭规则"warn"或1— 警告(不阻碍构建)"error"或2— 错误(阻碍构建,退出码非零)
module.exports = {
rules: {
'no-console': 'warn',
'no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
'react/react-in-jsx-scope': 'off', // React 17+ 不需要
'@typescript-eslint/explicit-function-return-type': 'warn'
}
};自定义规则
当内置规则无法满足需求时,可以编写自定义规则。自定义规则本质是一个 Node.js 模块,导出 meta 和 create 两个属性:
// eslint-plugin-local/rules/no-literal-url.js
module.exports = {
meta: {
type: 'suggestion',
docs: { description: '禁止在代码中出现字面量 URL' },
fixable: 'code',
schema: []
},
create(context) {
return {
Literal(node) {
const urlRegex = /https?:\/\/[^\s"'<>]+/;
if (typeof node.value === 'string' && urlRegex.test(node.value)) {
context.report({
node,
message: '发现字面量 URL,请将其提取为常量或配置文件',
fix(fixer) {
return fixer.replaceText(node, `'/* URL moved to config */'`);
}
});
}
}
};
}
};将自定义规则发布为 npm 包(命名约定 eslint-plugin-<name>),或在本地项目中通过 plugins 字段引用。
React 项目配置
// .eslintrc.cjs
module.exports = {
env: { browser: true, es2021: true },
extends: [
'eslint:recommended',
'plugin:react/recommended',
'plugin:react/jsx-runtime',
'plugin:react-hooks/recommended'
],
parserOptions: {
ecmaFeatures: { jsx: true },
ecmaVersion: 'latest',
sourceType: 'module'
},
plugins: ['react'],
settings: {
react: { version: 'detect' }
},
rules: {
'react/prop-types': 'off', // 使用 TypeScript 时关闭
'react/jsx-no-target-blank': 'error'
}
};Vue 项目配置
// .eslintrc.cjs
module.exports = {
env: { browser: true, es2021: true },
extends: [
'eslint:recommended',
'plugin:vue/vue3-recommended'
],
parser: 'vue-eslint-parser',
parserOptions: {
parser: '@typescript-eslint/parser',
ecmaVersion: 'latest',
sourceType: 'module'
},
plugins: ['vue'],
rules: {
'vue/multi-word-component-names': 'warn',
'vue/no-v-html': 'warn'
}
};TypeScript 项目配置
// .eslintrc.cjs
module.exports = {
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:@typescript-eslint/stylistic'
],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
project: './tsconfig.json'
},
plugins: ['@typescript-eslint'],
rules: {
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }]
}
};ESLint 扁平配置(Flat Config,v9+)
ESLint v9 将扁平配置(eslint.config.js)设为默认格式,终结了多年来多种配置文件格式并存的混乱局面:
// eslint.config.js
import js from '@eslint/js';
import tseslint from 'typescript-eslint';
import reactPlugin from 'eslint-plugin-react';
export default [
js.configs.recommended,
...tseslint.configs.recommended,
{
files: ['**/*.{js,jsx,ts,tsx}'],
plugins: { react: reactPlugin },
rules: {
'no-console': 'warn',
'react/jsx-no-target-blank': 'error'
},
linterOptions: {
reportUnusedDisableDirectives: true
}
},
{
ignores: ['dist/', 'node_modules/', '*.config.*']
}
];扁平配置的优势在于:无隐式继承、无配置文件层级合并、更好的 TypeScript 支持、更易组合和共享配置。
Prettier
Prettier 是一个"有主见"的代码格式化工具,通过解析代码并重新打印来确保一致的代码风格。
安装与基本配置
npm install --save-dev prettierPrettier 的配置方式:
prettier.config.js或.prettierrc.js.prettierrc(JSON 或 YAML 格式)package.json中的prettier字段
// prettier.config.js
module.exports = {
printWidth: 100,
tabWidth: 2,
useTabs: false,
semi: true,
singleQuote: true,
quoteProps: 'as-needed',
jsxSingleQuote: false,
trailingComma: 'es5',
bracketSpacing: true,
bracketSameLine: false,
arrowParens: 'always',
endOfLine: 'lf',
embeddedLanguageFormatting: 'auto'
};核心配置选项详解
| 选项 | 默认值 | 说明 |
|---|---|---|
printWidth | 80 | 每行代码最大长度,超过则换行 |
tabWidth | 2 | 缩进占用的空格数 |
useTabs | false | 是否使用 Tab 缩进 |
semi | true | 语句末尾是否加分号 |
singleQuote | false | 是否使用单引号 |
trailingComma | "all" | 多行时是否加尾逗号:"es5" / "all" / "none" |
bracketSpacing | true | 对象字面量括号间是否加空格({ foo } vs {foo}) |
arrowParens | "always" | 箭头函数参数是否加括号:"always" / "avoid" |
endOfLine | "lf" | 换行符风格:"lf" / "crlf" / "cr" / "auto" |
Prettier 与 ESLint 的配合
ESLint 负责代码逻辑质量(潜在 Bug、模式违规),Prettier 负责代码格式风格。为了避免两者规则冲突,需要借助以下工具:
eslint-config-prettier
关闭 ESLint 中与 Prettier 冲突的格式化规则:
npm install --save-dev eslint-config-prettier// .eslintrc.cjs — 确保 prettier 在 extends 最后
module.exports = {
extends: [
'eslint:recommended',
'plugin:react/recommended',
'plugin:prettier/recommended' // 集成了 eslint-config-prettier
]
};plugin:prettier/recommended 这一行等价于:
- 继承
eslint-config-prettier关闭冲突规则 - 注册
eslint-plugin-prettier将 Prettier 作为 ESLint 规则运行 - 将 Prettier 格式错误报告为 ESLint 错误
单独使用 eslint-config-prettier
如果你不希望 Prettier 作为 ESLint 插件运行(例如你使用编辑器的 Prettier 插件进行格式化),可以只安装 eslint-config-prettier:
// .eslintrc.cjs
module.exports = {
extends: [
'eslint:recommended',
'some-other-config',
'prettier' // 必须放在最后
]
};使用 prettier-eslint
prettier-eslint 先运行 Prettier 格式化代码,再运行 ESLint --fix 进行修正。但这种方式较为低效,不推荐在大型项目中使用。更常见的做法是:
- 编辑器保存时自动触发 Prettier 格式化
- 提交前通过 lint-staged 运行 Prettier
- ESLint 仅负责逻辑规则检查
Prettier 插件
Prettier 插件机制允许为特定语言或框架提供额外的格式化支持:
# 对 Tailwind CSS class 排序
npm install --save-dev prettier-plugin-tailwindcss
# 对 import 语句排序
npm install --save-dev @trivago/prettier-plugin-sort-imports// prettier.config.js
module.exports = {
plugins: [
'prettier-plugin-tailwindcss',
'@trivago/prettier-plugin-sort-imports'
],
importOrder: [
'^react',
'^next',
'<THIRD_PARTY_MODULES>',
'^@/components',
'^@/utils',
'^[./]'
],
importOrderSeparation: true,
importOrderSortSpecifiers: true
};忽略文件
创建 .prettierignore 文件来排除不需要格式化的文件和目录:
# .prettierignore
node_modules/
dist/
build/
coverage/
*.min.js
CHANGELOG.mdHusky + lint-staged
将代码检查和格式化绑定到 Git 提交前,是保障团队代码质量的最后一道防线。
Husky
Husky 简化了 Git hooks 的配置和管理,让你能够轻松地在特定 Git 事件发生时执行脚本。
安装
npm install --save-dev husky
# 初始化 Git hooks 目录
npx husky init
# 手动创建 hook
npx husky add .husky/pre-commit "npx lint-staged"Husky 配置文件示例
# .husky/pre-commit — 提交前运行 lint-staged
npx lint-staged
# .husky/commit-msg — 提交信息检查
npx --no -- commitlint --edit ${1}在 CI 环境中使用
# CI 中需要先安装并初始化 husky
npm ci
npx husky installlint-staged
lint-staged 仅在 Git 暂存区(staged)的文件上运行代码检查工具,大幅减少检查时间。
安装与配置
npm install --save-dev lint-staged// package.json
{
"lint-staged": {
"*.{js,jsx,ts,tsx}": [
"eslint --fix",
"prettier --write"
],
"*.{json,md,css,scss,less}": [
"prettier --write"
]
}
}也可以使用独立的配置文件:
// lint-staged.config.js
module.exports = {
'*.{js,jsx,ts,tsx}': ['eslint --fix', 'prettier --write'],
'*.{json,md,css,scss,less}': ['prettier --write'],
'*.{png,jpg,jpeg,gif,svg}': ['imagemin-lint-staged']
};常见问题处理
暂时跳过 hooks 进行检查:
git commit --no-verify -m "feat: 临时提交"
# 或使用简写
git commit -n -m "feat: 临时提交"只对特定文件运行 lint-staged:
# 在 pre-commit hook 中设置环境变量
# .husky/pre-commit
npx lint-staged --diff="main...HEAD"Commitlint
约定式提交让 Git 提交信息具备语义化结构,便于生成 CHANGELOG 和自动化版本管理。
约定式提交规范
提交信息的标准结构如下:
<type>(<scope>): <subject>
<body>
<footer>常用 type 类型:
| Type | 说明 | 是否影响版本 |
|---|---|---|
feat | 新功能 | 次版本号+1 |
fix | Bug 修复 | 补丁号+1 |
docs | 文档变更 | 否 |
style | 代码格式调整 | 否 |
refactor | 代码重构 | 否 |
perf | 性能优化 | 否 |
test | 测试相关 | 否 |
build | 构建系统或外部依赖变更 | 否 |
ci | CI 配置变更 | 否 |
chore | 其他杂项 | 否 |
revert | 回滚提交 | 否 |
commitlint 配置
npm install --save-dev @commitlint/cli @commitlint/config-conventional// commitlint.config.js
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [
2,
'always',
['feat', 'fix', 'docs', 'style', 'refactor', 'perf', 'test', 'build', 'ci', 'chore', 'revert']
],
'subject-case': [0], // 不限制 subject 大小写
'subject-max-length': [2, 'always', 100]
}
};commitizen
commitizen 提供交互式提交信息生成器,帮助开发者写出符合规范的提交信息:
npm install --save-dev commitizen
# 初始化 cz-conventional-changelog 适配器
npx commitizen init cz-conventional-changelog --save-dev --save-exact在 package.json 中配置:
{
"scripts": {
"commit": "cz"
}
}之后使用 npm run commit 替代 git commit,会出现交互式命令行向导:
? Select the type of change that you're committing:
feat: A new feature
fix: A bug fix
docs: Documentation only changes
style: Changes that do not affect the meaning of the code
refactor: A code change that neither fixes a bug nor adds a feature
perf: A code change that improves performance
test: Adding missing tests or correcting existing tests
...
? What is the scope of this change (e.g. component or file name)?
? Write a short description:
? Provide a longer description:
? Are there any breaking changes?
? Does this change affect any open issues?commitizen 与 Husky 配合
将 commitlint 绑定到 commit-msg hook,确保所有提交信息都通过校验:
# .husky/commit-msg
npx --no -- commitlint --edit ${1}自动生成 CHANGELOG
使用 standard-version 或 conventional-changelog-cli 根据提交信息自动生成 CHANGELOG:
npm install --save-dev standard-version{
"scripts": {
"release": "standard-version",
"release:minor": "standard-version --release-as minor",
"release:patch": "standard-version --release-as patch",
"release:major": "standard-version --release-as major"
}
}运行 npm run release 后,工具会自动:
- 根据 commit 信息计算版本号
- 更新
CHANGELOG.md - 更新
package.json的版本号 - 创建一个 Git tag
- 生成一个符合约定的提交
生成的 CHANGELOG 示例:
# Changelog
## [1.2.0] (2024-01-15)
### Features
* **user:** 新增用户头像裁剪功能 (abc1234)
* **order:** 支持批量导出订单 (def5678)
### Bug Fixes
* **payment:** 修复支付回调超时问题 (ghi9012)
* **auth:** 修复 Token 刷新竞态条件 (jkl3456)EditorConfig 配置
EditorConfig 帮助跨编辑器的团队统一缩进、编码格式等基本设置,是代码质量工具链的基石层。
配置文件示例
# .editorconfig
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
[*.{yml,yaml}]
indent_size = 2配置选项说明
| 选项 | 说明 | 推荐值 |
|---|---|---|
root | 标记配置文件是否为根配置 | true |
indent_style | 缩进风格 | space 或 tab |
indent_size | 缩进宽度 | 2 |
end_of_line | 换行符风格 | lf |
charset | 字符编码 | utf-8 |
trim_trailing_whitespace | 去除行尾空白 | true |
insert_final_newline | 文件末尾插入空行 | true |
EditorConfig 插件在主流 IDE(VS Code、WebStorm、Vim、Sublime Text 等)中均有支持,安装对应插件后自动生效。
代码规范工具链整合流程
下面给出一个完整的前端项目代码质量工具链搭建流程。
完整整合示例
# 1. 初始化项目
npm init -y
# 2. 安装核心工具
npm install --save-dev eslint prettier husky lint-staged
npm install --save-dev @commitlint/cli @commitlint/config-conventional
npm install --save-dev commitizen cz-conventional-changelog
# 3. 初始化 ESLint
npx eslint --init
# 4. 初始化 Husky
npx husky init
# 5. 创建 Git hooks
npx husky add .husky/pre-commit "npx lint-staged"
npx husky add .husky/commit-msg 'npx --no -- commitlint --edit "$1"'package.json 完整配置
{
"scripts": {
"lint": "eslint src/ --ext .js,.jsx,.ts,.tsx",
"lint:fix": "eslint src/ --ext .js,.jsx,.ts,.tsx --fix",
"format": "prettier --write \"src/**/*.{js,jsx,ts,tsx,json,css,md}\"",
"format:check": "prettier --check \"src/**/*.{js,jsx,ts,tsx,json,css,md}\"",
"commit": "cz",
"prepare": "husky install",
"release": "standard-version"
},
"lint-staged": {
"*.{js,jsx,ts,tsx}": [
"eslint --fix",
"prettier --write"
],
"*.{json,css,scss,less,md}": [
"prettier --write"
]
},
"config": {
"commitizen": {
"path": "cz-conventional-changelog"
}
}
}工作流程示意
开发者日常开发的工作流如下:
编写代码 → 编辑器保存(EditorConfig + Prettier 自动格式化)
→ git add 暂存
→ git commit 触发 pre-commit hook
→ lint-staged 运行 ESLint --fix + Prettier --write(仅检查暂存文件)
→ 如检查失败,提交终止,返回错误信息
→ commit-msg hook 触发
→ commitlint 检查提交信息格式
→ 如格式不符,提交终止
→ 提交成功
→ CI 流水线运行完整代码检查CI 中集成代码检查
在 CI 流水线中集成代码检查,可以防止不符合规范的代码进入主分支。
GitHub Actions 示例
# .github/workflows/lint.yml
name: Code Quality Check
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- name: Install dependencies
run: npm ci
- name: ESLint check
run: npm run lint
- name: Prettier check
run: npm run format:check
- name: Commitlint check
if: github.event_name == 'pull_request'
run: npx commitlint --from ${{ github.event.pull_request.base.sha }} --to ${{ github.event.pull_request.head.sha }}GitLab CI 示例
# .gitlab-ci.yml
stages:
- lint
code-quality:
stage: lint
image: node:20-alpine
before_script:
- npm ci
script:
- npm run lint
- npm run format:check
only:
- main
- merge_requestsGit Hooks 与 CI 的职责划分
| 阶段 | 检查范围 | 工具 | 响应速度 | 阻断性 |
|---|---|---|---|---|
| 编辑器保存 | 当前文件 | EditorConfig + Prettier | 毫秒级 | 非阻断 |
| Git 提交前 | 暂存区文件 | ESLint + Prettier(通过 lint-staged) | 秒级 | 阻断 |
| Git 提交信息 | 提交信息 | commitlint | 毫秒级 | 阻断 |
| CI | 全仓库 | ESLint + Prettier + 单元测试 | 分钟级 | 阻断 |
注意:本地 Git hooks 可以通过 --no-verify 跳过,因此 CI 中的检查是最后一道不可绕过的防线。建议将核心检查(lint、类型检查、测试)全部配置在 CI 流水线中。
使用 lint 工具统一配置文件
在项目根目录创建以下文件列表,确保所有工具配置集中管理:
项目根目录
├── .editorconfig # 编辑器基本设置
├── .prettierrc.js # Prettier 配置
├── .prettierignore # Prettier 忽略规则
├── .eslintrc.cjs # ESLint 配置
├── .eslintignore # ESLint 忽略规则
├── commitlint.config.js # Commitlint 配置
├── lint-staged.config.js # lint-staged 配置
├── .husky/
│ ├── pre-commit # 提交前 hook
│ └── commit-msg # 提交信息 hook
└── .github/workflows/
└── lint.yml # CI 代码检查最佳实践总结
- 分层防御:编辑器 → Git hooks → CI,形成三层防线,每层解决不同粒度的问题
- 减少噪音:lint-staged 只检查暂存区文件,避免全量检查带来的耗时和噪音
- 先格式化再检查:Prettier 先格式化代码,ESLint 再检查逻辑问题,避免格式问题干扰逻辑检查
- 渐进采用:对于存量项目,建议逐步开启规则,先从
warn级别开始,待团队适应后再提升为error - 配置即文档:所有代码规范配置都放在项目根目录的配置文件中,而非依赖编辑器插件配置,确保所有开发者使用相同的规则
- 提交信息规范化:commitizen 的交互式向导比记忆 type 列表更友好,建议推广使用
- CI 强校验:Git hooks 可通过
--no-verify跳过,因此 CI 中的检查必须强制执行
交互式配置向导
你可以使用下方的交互式配置生成器,快速生成 ESLint、Prettier、Husky 和 Commitlint 的配置文件: