别再写"烂代码"了!ESLint v10 完整实战,用 Flat Config 治好团队的代码洁癖
一个团队 10 个人,写出 10 种风格的代码------有人用
var,有人用let;有人用单引号,有人用双引号;有人加分号,有人不加分号。ESLint 就是解决这个问题的工程化工具:强制团队写出一致风格的代码,严格检查潜在 Bug。本文基于 ESLint v10 全新的 Flat Config(扁平化配置)格式,从安装、配置、规则、命令到工作流,带你完整掌握代码质量工程的闭环。全文代码可直接运行,建议收藏后动手实践。
一、为什么需要 ESLint?
1.1 没有 ESLint 的世界
javascript
// 开发者 A 写的
var name = "张三"
function sayHello() {
console.log('hello')
}
sayHello()
// 开发者 B 写的
const name = '李四';
const sayHello = () => {
console.log("world");
};
sayHello();
// 开发者 C 写的
let name = "王五"
const sayHello = function(){
console.log(`hi`)
}
sayHello()
问题清单:
csharp
┌──────────────────────────────────────────────────────┐
│ 没有 ESLint 的代码库 │
│ │
│ ① 风格不统一:var / let / const 混用 │
│ ② 引号不统一:单引号 / 双引号 / 反引号混用 │
│ ③ 分号不统一:有分号 / 无分号混用 │
│ ④ 缩进不统一:2 空格 / 4 空格混用 │
│ ⑤ 潜在 Bug:var 变量提升、全局污染 │
│ ⑥ Code Review 浪费时间在风格讨论上 │
│ ⑦ 新人入职看不懂同事的代码风格 │
└──────────────────────────────────────────────────────┘
1.2 有 ESLint 的世界
javascript
// 所有人写出的代码都长这样
const name = "张三";
function sayHello() {
console.log("hello");
}
sayHello();
javascript
ESLint 做了什么:
① 禁用 var → 强制用 let / const
② 统一引号 → 强制双引号
③ 强制分号 → 每条语句必须加分号
④ 统一缩进 → 2 个空格
⑤ 禁用 console → 上线前提醒移除
⑥ 自动修复 → lint:fix 一键统一风格
二、ESLint v10 环境搭建
2.1 初始化项目
bash
mkdir eslint-demo && cd eslint-demo
npm init -y
2.2 安装依赖
bash
npm install --save-dev eslint @eslint/js globals
json
{
"name": "eslint-demo",
"version": "1.0.0",
"type": "module",
"scripts": {
"lint": "eslint .",
"lint:fix": "eslint . --fix"
},
"devDependencies": {
"@eslint/js": "^10.0.1",
"eslint": "^10.8.1",
"globals": "^17.11.0"
}
}
三个依赖各自的作用:
python
eslint → ESLint 核心引擎,负责解析、检查、报告
@eslint/js → ESLint 官方内置规则包(recommended 预设)
globals → 预定义的全局变量环境(node、browser、es2021 等)
两个 npm scripts:
arduino
npm run lint → 检查所有文件,报告错误和警告
npm run lint:fix → 检查并自动修复能修的问题(风格类)
2.3 项目结构
bash
eslint-demo/
├── eslint.config.mjs # ESLint 配置文件(Flat Config)
├── index.mjs # 待检查的代码文件
├── package.json
└── node_modules/
三、Flat Config:ESLint v10 的全新配置方式
3.1 旧配置 vs 新配置
arduino
旧配置(.eslintrc.*)------ 已废弃
├── .eslintrc.js
├── .eslintrc.json
├── .eslintrc.yml
└── .eslintrc(无后缀)
特点:
- JSON / YAML / JS 多种格式
- 配置继承(extends)层层嵌套,难以追踪
- 需要安装 parser、plugin 等额外依赖
- 配置分散在多个文件和目录
新配置(eslint.config.mjs)------ ESLint v10 唯一支持
└── eslint.config.mjs
特点:
- 只有一种格式:ESM(.mjs)
- 扁平数组结构,配置一目了然
- 插件直接 import,无需字符串引用
- 一个文件搞定所有配置
3.2 完整配置文件
javascript
// eslint.config.mjs
import js from "@eslint/js";
import globals from "globals";
import { defineConfig } from "eslint/config";
export default defineConfig([
{
files: ["**/*.{js,mjs,cjs}"],
plugins: { js },
extends: ["js/recommended"],
languageOptions: {
globals: globals.node,
},
rules: {
// 级别:2 = error(报错) 1 = warn(警告) 0 = off(关闭)
"no-var": 2, // 禁用 var
"no-console": 1, // 开发时用,上线后不用
"quotes": ["error", "double"], // 引号必须使用双引号
"semi": ["error", "always"], // 分号必须使用
"indent": ["error", 2], // 缩进必须使用 2 个空格
},
},
]);
3.3 配置结构逐层解析
css
defineConfig([{ ... }])
│
├── files: ["**/*.{js,mjs,cjs}"]
│ → 这条配置对哪些文件生效
│ → 匹配所有 .js、.mjs、.cjs 文件
│
├── plugins: { js }
│ → 注册插件(直接 import,不用字符串)
│ → js 插件提供了 recommended 规则集
│
├── extends: ["js/recommended"]
│ → 继承官方推荐规则集
│ → 包含约 100+ 条最佳实践规则
│ → 如 no-unused-vars、no-undef 等
│
├── languageOptions: { globals: globals.node }
│ → 告诉 ESLint 运行环境是 Node.js
│ → 这样 require、module、process 等不算未定义变量
│
└── rules: { ... }
→ 自定义规则,覆盖 extends 的默认设置
→ 团队约定的规则在这里配置
3.4 defineConfig 的作用
javascript
// defineConfig 是一个辅助函数
// 提供类型提示和配置校验
import { defineConfig } from "eslint/config";
export default defineConfig([
{ files: ["**/*.js"], rules: { ... } }
]);
// 等价于直接导出数组(但少了类型提示和校验)
export default [
{ files: ["**/*.js"], rules: { ... } }
];
四、规则系统:ESLint 的核心
4.1 规则的三个级别
scss
┌──────────────────────────────────────────────────────────┐
│ ESLint 规则级别 │
│ │
│ 0 (off) → 关闭规则,不检查 │
│ │
│ 1 (warn) → 警告,不影响退出码 │
│ 代码能跑,但 Control Flow 里有黄色警告 │
│ 适合"建议遵守"的规则 │
│ │
│ 2 (error) → 报错,eslint 命令退出码非 0 │
│ CI/CD 中会阻断构建 │
│ 适合"必须遵守"的规则 │
└──────────────────────────────────────────────────────────┘
javascript
rules: {
"no-var": 2, // error:必须遵守,违反则报错
"no-console": 1, // warn:建议遵守,违反只警告
"no-alert": 0, // off:不检查
}
4.2 规则的两种写法
javascript
rules: {
// 简写:只指定级别
"no-var": 2,
// 完整写法:级别 + 选项
"quotes": ["error", "double"], // 必须用双引号
"semi": ["error", "always"], // 必须加分号
"indent": ["error", 2], // 必须 2 空格缩进
}
数组写法解析:
javascript
"quotes": ["error", "double"]
// │ │
// │ └── 选项:使用双引号
// └── 级别:error
"semi": ["error", "always"]
// │
// └── 选项:总是加分号(另一个选项是 "never")
"indent": ["error", 2]
// │
// └── 选项:2 个空格(也可以是 4 或 tab)
4.3 本项目的五条规则详解
javascript
rules: {
// ① 禁用 var ------ 强制使用 let / const
"no-var": 2,
// var 有变量提升、可重复声明、函数作用域等问题
// ES6 后应该用 let(可变)和 const(不可变)替代
// ② 警告 console ------ 开发时用,上线前移除
"no-console": 1,
// console.log 会在控制台输出,上线后可能泄露信息
// 设为 warn 而非 error,开发时可以临时用
// ③ 统一引号 ------ 强制双引号
"quotes": ["error", "double"],
// 团队统一一种引号风格,避免混用
// 也可以选 "single"(单引号),看团队约定
// ④ 强制分号 ------ 每条语句必须加分号
"semi": ["error", "always"],
// 虽然 ASI(自动分号插入)让分号可选
// 但显式分号能避免一些隐蔽的 Bug
// ⑤ 统一缩进 ------ 2 个空格
"indent": ["error", 2],
// 2 空格是前端社区的主流约定
// 也可以选 4 空格,关键是统一
}
4.4 extends: js/recommended 包含了什么?
javascript
extends: ["js/recommended"]
@eslint/js 的 recommended 预设包含了约 100+ 条规则,常见的有:
| 规则 | 说明 | 级别 |
|---|---|---|
no-undef |
禁止使用未定义的变量 | error |
no-unused-vars |
禁止声明未使用的变量 | error |
no-redeclare |
禁止重复声明 | error |
no-cond-assign |
禁止在条件语句中赋值 | error |
no-debugger |
禁止 debugger 语句 | error |
no-dupe-keys |
禁止对象重复键 | error |
no-empty |
禁止空代码块 | error |
no-unreachable |
禁止 return 后的代码 | error |
prefer-const |
优先使用 const | error |
no-constant-condition |
禁止恒真条件 | error |
recommended 的价值:
→ 不用手动配置 100+ 条规则
→ 社区最佳实践,经过大量项目验证
→ 在此基础上添加团队自定义规则即可
五、globals:环境变量声明
5.1 为什么需要 globals?
javascript
// index.mjs
const fs = require("fs"); // require 是 Node.js 全局变量
console.log(process.version); // process 是 Node.js 全局变量
// 没有 globals 配置时:
// ESLint 报错:'require' is not defined (no-undef)
// ESLint 报错:'process' is not defined (no-undef)
// 因为 ESLint 不知道你的代码运行在什么环境
5.2 globals 包的使用
javascript
import globals from "globals";
languageOptions: {
globals: globals.node, // Node.js 环境
}
globals 包提供了各种环境的全局变量声明:
javascript
globals.browser // 浏览器环境:window、document、localStorage 等
globals.node // Node.js 环境:require、module、process、__dirname 等
globals.es2021 // ES2021:globalThis、BigInt 等
globals.jest // Jest 测试环境:describe、it、expect 等
globals.jquery // jQuery 环境:$、jQuery
// 可以组合多个环境
languageOptions: {
globals: {
...globals.node,
...globals.browser, // 同时支持 Node 和浏览器
},
}
5.3 globals 本质
javascript
// globals.node 的内容大致如下
{
require: "readonly",
module: "readonly",
process: "readonly",
__dirname: "readonly",
__filename: "readonly",
Buffer: "readonly",
console: "readonly",
// ...
}
// readonly → 只读,不能赋值
// writable → 可读写
// off → 忽略该全局变量
六、实战:检查与修复
6.1 待检查的代码
javascript
// index.mjs
var name = "张三";
function sayHello() {
console.log('hello')
}
sayHello();
6.2 运行检查
bash
npm run lint
检查结果:
vbnet
1:5 error Unexpected var, use let or const instead no-var
1:5 error 'name' is assigned a value but never used no-unused-vars
3:3 warning Unexpected console statement no-console
3:15 error Strings must use doublequote quotes
3:15 error Missing semicolon semi
✖ 4 errors and 1 warning
逐条分析:
go
Line 1: var name = "张三";
├── no-var (error) → 不允许用 var,应该用 let 或 const
└── no-unused-vars (error) → name 声明了但没使用
Line 3: console.log('hello')
├── no-console (warning) → 不建议用 console
├── quotes (error) → 'hello' 用了单引号,应该用双引号 "hello"
└── semi (error) → 缺少分号
Line 4: sayHello();
└── ✅ 没问题
6.3 自动修复
bash
npm run lint:fix
自动修复后:
javascript
// index.mjs(修复后)
const name = "张三";
function sayHello() {
console.log("hello");
}
sayHello();
修复了什么:
javascript
var name → const name (no-var 自动修复)
'hello' → "hello" (quotes 自动修复)
console.log('hello') → console.log("hello"); (semi 自动修复)
不能自动修复的:
javascript
no-unused-vars → 不能自动删变量,需要手动使用或删除
no-console → 不能自动删 console,需要开发者判断
6.4 lint vs lint:fix 的退出码
bash
# npm run lint
echo $? # 有 error 时退出码为 1,CI/CD 中会阻断构建
# npm run lint:fix
echo $? # 修复后仍有 error 时退出码为 1
csharp
CI/CD 中的典型用法:
npm run lint:fix # 先自动修复风格问题
git add . # 提交修复
npm run lint # 再检查一遍,确保没有 error
# 如果 lint 退出码非 0,阻断构建
七、规则的两个维度:可修复 vs 不可修复
7.1 哪些规则能自动修复?
perl
┌──────────────────────────────────────────────────────────┐
│ 规则修复能力分类 │
│ │
│ 可自动修复(lint:fix 有效) │
│ ├── quotes 单引号 → 双引号 │
│ ├── semi 缺分号 → 补分号 │
│ ├── indent 4 空格 → 2 空格 │
│ ├── no-var var → let/const │
│ ├── comma-dangle 尾逗号补全/移除 │
│ └── no-extra-semi 多余分号移除 │
│ │
│ 不能自动修复(需手动改) │
│ ├── no-unused-vars 未使用变量(无法判断该删还是该用) │
│ ├── no-console console(可能需要也可能不需要) │
│ ├── no-undef 未定义变量(需手动 import) │
│ ├── no-cond-assign 条件中赋值(需改逻辑) │
│ └── no-unreachable 不可达代码(需删代码块) │
└──────────────────────────────────────────────────────────┘
7.2 查看规则是否可修复
bash
# 查看某条规则的文档
npx eslint --print-config index.mjs
# 打印该文件生效的完整配置
# ESLint 文档中每条规则都标注了:
# ✅ Fixable:可以用 --fix 自动修复
# ❌ Not fixable:需要手动修改
八、Flat Config 的高级用法
8.1 多文件类型不同配置
javascript
import js from "@eslint/js";
import globals from "globals";
import { defineConfig } from "eslint/config";
export default defineConfig([
// 所有 JS 文件的通用配置
{
files: ["**/*.{js,mjs,cjs}"],
plugins: { js },
extends: ["js/recommended"],
languageOptions: {
globals: globals.node,
},
rules: {
"no-var": 2,
"no-console": 1,
"quotes": ["error", "double"],
"semi": ["error", "always"],
"indent": ["error", 2],
},
},
// 测试文件的额外配置
{
files: ["**/*.test.js", "**/*.spec.js"],
rules: {
"no-console": 0, // 测试文件允许 console
},
},
// 配置文件的特殊规则
{
files: ["*.config.js", "*.config.mjs"],
rules: {
"no-console": 0, // 配置文件允许 console
},
},
]);
多配置块的优先级:
ini
后面的配置块会覆盖前面同名的规则
配置块 1(通用):no-console = 1 (warn)
配置块 2(测试):no-console = 0 (off)
test.spec.js 匹配两个配置块
→ 后面的覆盖前面的
→ 最终 no-console = 0 (off)
8.2 忽略文件
javascript
import { defineConfig } from "eslint/config";
export default defineConfig([
// 忽略配置
{
ignores: [
"node_modules/**",
"dist/**",
"build/**",
"*.min.js",
],
},
// 正常的规则配置
{
files: ["**/*.{js,mjs,cjs}"],
// ...
},
]);
8.3 对比旧版 .eslintignore
arduino
旧版(两个文件):
.eslintrc.js → 配置规则
.eslintignore → 忽略文件
新版(一个文件):
eslint.config.mjs → ignores + rules 全在一起
→ 配置集中,更易维护
九、编辑器集成:实时反馈
9.1 VS Code 配置
json
// .vscode/settings.json
{
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
},
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact"
]
}
效果:
matlab
保存文件时自动执行 ESLint --fix
开发者写代码 → 保存 → 自动修复风格问题 → 只剩逻辑问题
无需手动运行 npm run lint:fix
开发体验丝滑
9.2 实时反馈流程
go
┌──────────────────────────────────────────────────────────┐
│ ESLint 实时反馈闭环 │
│ │
│ 写代码 ──→ 编辑器实时标红 ──→ 保存自动修复 │
│ │ │ │
│ │ ▼ │
│ │ 风格问题已修复 ✅ │
│ │ │ │
│ ▼ ▼ │
│ 逻辑问题需手动改 git commit │
│ │ │ │
│ ▼ ▼ │
│ 修改代码逻辑 CI/CD: npm run lint │
│ │ │ │
│ ▼ ▼ │
│ 问题消除 ✅ 有 error → 阻断构建 ❌ │
│ 无 error → 部署 ✅ │
└──────────────────────────────────────────────────────────┘
十、ESLint 工程化最佳实践
10.1 规则配置策略
javascript
rules: {
// ========== 错误级(必须遵守,CI 阻断) ==========
"no-var": "error", // 禁用 var
"no-undef": "error", // 禁用未定义变量
"no-unused-vars": "error", // 禁用未使用变量
"no-debugger": "error", // 禁用 debugger
"no-dupe-keys": "error", // 禁止对象重复键
"no-unreachable": "error", // 禁止不可达代码
"prefer-const": "error", // 优先 const
"quotes": ["error", "double"], // 统一引号
"semi": ["error", "always"], // 统一分号
"indent": ["error", 2], // 统一缩进
// ========== 警告级(建议遵守,不阻断) ==========
"no-console": "warn", // 警告 console
"no-warning-comments": "warn", // 警告 TODO/FIXME
"no-magic-numbers": "off", // 太严格,关闭
// ========== 关闭(项目不需要) ==========
"no-alert": "off", // 允许 alert
}
10.2 与 Prettier 的分工
perl
ESLint 的职责:
├── 代码质量(potential bugs)
│ no-undef、no-unused-vars、no-cond-assign...
├── 代码风格(部分)
│ quotes、semi、indent...
└── 最佳实践
prefer-const、no-var...
Prettier 的职责:
└── 纯格式化
换行位置、空格、宽度、括号...
分工策略:
ESLint 管代码质量 + 最佳实践
Prettier 管纯格式化
eslint-config-prettier 关闭 ESLint 中与 Prettier 冲突的风格规则
10.3 Git Hooks 自动化
bash
# 安装 husky + lint-staged
npm install --save-dev husky lint-staged
# 初始化 husky
npx husky init
json
// package.json
{
"lint-staged": {
"*.{js,mjs,cjs}": ["eslint --fix", "git add"]
}
}
bash
# .husky/pre-commit
npx lint-staged
效果:
markdown
git commit 时自动触发:
1. lint-staged 只检查暂存区(staged)的文件,不检查全项目
2. 对 .js/.mjs/.cjs 文件执行 eslint --fix
3. 修复后重新 git add
4. 如果有 error 无法修复 → commit 失败
从源头保证提交的代码都是符合规范的
十一、总结
11.1 知识体系图
go
ESLint 代码质量工程
│
├── 核心价值
│ ├── 统一代码风格(引号、分号、缩进)
│ ├── 检查潜在 Bug(未定义变量、重复声明)
│ ├── 强制最佳实践(prefer-const、no-var)
│ └── 团队协作一致性
│
├── Flat Config(eslint.config.mjs)
│ ├── defineConfig 辅助函数
│ ├── files 文件匹配
│ ├── plugins 插件注册(import 引入)
│ ├── extends 规则集继承(js/recommended)
│ ├── languageOptions.globals 环境声明
│ ├── rules 自定义规则
│ └── ignores 忽略文件
│
├── 规则系统
│ ├── 三个级别:0 (off) / 1 (warn) / 2 (error)
│ ├── 两种写法:简写 "no-var": 2 / 完整 ["error", "double"]
│ ├── 可修复 vs 不可修复
│ └── recommended 预设 100+ 规则
│
├── 命令工具
│ ├── eslint . → 检查所有文件
│ ├── eslint . --fix → 自动修复
│ └── 退出码:有 error → 1(CI 阻断)
│
├── 编辑器集成
│ ├── VS Code 实时标红
│ ├── 保存自动修复(source.fixAll.eslint)
│ └── 开发时实时反馈
│
└── 工程化实践
├── 与 Prettier 分工(质量 vs 格式)
├── husky + lint-staged(提交前检查)
└── CI/CD 阻断(有 error 不部署)
11.2 核心概念速查
| 概念 | 要点 |
|---|---|
| ESLint | 代码质量工具,检查风格 + 潜在 Bug |
| Flat Config | v10 唯一配置格式,eslint.config.mjs 扁平数组 |
| defineConfig | 配置辅助函数,提供类型提示 |
| 规则级别 | 0=off / 1=warn / 2=error |
| extends | 继承规则集(如 js/recommended) |
| globals | 声明运行环境全局变量(node/browser) |
| lint:fix | 自动修复风格类问题,不能修复逻辑问题 |
| no-var | 禁用 var,强制 let/const |
| no-console | 警告 console,上线前移除 |
| husky + lint-staged | Git 提交前自动检查暂存区文件 |
11.3 一句话总结
ESLint 是代码质量的守门员------用 Flat Config 统一配置,用规则系统检查每一行代码,用
--fix自动修复风格问题,用 Git Hooks 和 CI/CD 确保不合格的代码永远进不了主分支。代码风格不再是 Code Review 的讨论话题,而是工具自动保证的底线。
如果这篇文章对你有帮助,欢迎点赞 和收藏!