别再写"烂代码"了!ESLint v10 完整实战,用 Flat Config 治好团队的代码洁癖

别再写"烂代码"了!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/jsrecommended 预设包含了约 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 的讨论话题,而是工具自动保证的底线。


如果这篇文章对你有帮助,欢迎点赞收藏

相关推荐
烬羽2 小时前
ESLint 10 把 .eslintrc 删了:flat config 到底怎么配,我帮你踩完了
代码规范·eslint·前端工程化
Asize6 小时前
ESLint 到底在帮我们守住什么?从规则配置到工程实践
代码规范·eslint
嘟嘟07176 小时前
ESLint 入门:从 npm run lint 到 --fix 与规则级别一次讲清
javascript·代码规范·eslint
Flynt15 天前
我用Biome替换ESLint+Prettier在项目上踩了不少坑
rust·eslint·前端工程化
任磊abc1 个月前
react native项目配置eslint+prettier
react native·eslint·prettier
任磊abc2 个月前
nextjs16配置eslint+prettier
前端·eslint·nextjs·prettier
梵得儿SHI3 个月前
Vue 项目实战与性能优化:工程化与协作全指南(规范 + 配置 + 协作 + 文档)
前端·vue.js·代码规范·eslint·团队协作·前端工程化·前端架构
LIO4 个月前
ESLint 极简指南:让代码既规范又一致
前端·eslint
晓得迷路了4 个月前
栗子前端技术周刊第 124 期 - ESLint v10.2.0、React Native 0.85、Node.js 25.9.0...
前端·javascript·eslint