ESLint 代码规范完全指南------从 AST 原理到 flat config 逐行解析
摘要:readme 里把
eslint标注为「代码风格规范」,但这五个字背后藏着一整套机制。本文从「为什么需要 ESLint」讲到它底层的 AST 原理,再逐行拆解一份真实的eslint.config.mjs和package.json,让你不仅能看懂配置,还能自己写规则。
一、为什么需要 ESLint:代码风格规范
一个团队多人协作,如果每个人缩进、引号、分号习惯都不同,代码会越来越乱、越来越难维护。更糟的是,有些写法本身就是坑 (比如 var 的作用域提升、忘写分号导致的 ASI 陷阱、console.log 泄露调试信息上线)。
ESLint 做的事就是:用规则把「风格」和「质量」锁死 。不满足规则的代码,直接报错或警告。所以 readme 给它下的定义很准------代码风格规范。
ESLint 有个特别之处:它只检查,不改你的代码 (除非加 --fix)。它像一个「代码审查员」,指出问题,把决定权留给你。
二、ESLint 是什么:静态代码分析工具
ESLint 是一种 Linter ,做的是静态分析 ------不运行你的代码,只靠「读源码」就能发现问题和风格偏差。
类比一下:
- 动态分析:把代码跑起来,看它会不会崩(像测试)。
- 静态分析 :代码一行没跑,光看文本和结构就能指出「这里
var不该用」「这里少分号」(像 Code Review)。
因为它不运行代码,所以快,能集成到编辑器里实时提示(你写错一个分号,编辑器立刻标红),也能在提交前作为一道关卡。
三、底层原理:AST 抽象语法树
这是理解 ESLint 的底层关键。ESLint 拿到你的源码后,做的第一步是解析 ------把一串字符串的代码,变成一棵抽象语法树(AST,Abstract Syntax Tree) 。
比如这行代码:
ini
const a = 1
会被解析成一棵结构化的树(简化示意):
yaml
VariableDeclaration (变量声明)
└─ VariableDeclarator
├─ Identifier: a (变量名)
└─ Literal: 1 (值)
有了这棵树,ESLint 就能遍历每一个节点 ,对每个节点套用规则。比如 no-var 这条规则的工作方式就是:遍历时,发现某个节点的类型是 VariableDeclaration 且用的是 var,就报错。
所以每条 ESLint 规则的本质都是:「在 AST 里找到某种形状的节点 → 判断它是否违规 → 报告」。理解了 AST,你就理解了 ESLint 为什么能「看懂」代码------它看到的不是文本,是结构。
源码字符串 ──解析──► AST(抽象语法树) ──遍历+套规则──► 报错/警告列表
四、规则级别:2 / 1 / 0
这是 eslint.config.mjs 注释里强调的重点:
级别 2=error 1=warn 警告 0 关闭
每条规则可以设一个「级别」,决定违规时怎么办:
| 级别 | 数字写法 | 字符串写法 | 含义 |
|---|---|---|---|
| 报错 | 2 |
"error" |
违反直接报错,通常阻断提交 |
| 警告 | 1 |
"warn" |
提示,但不阻断 |
| 关闭 | 0 |
"off" |
不管这条规则 |
数字和字符串完全等价 ,数字更省字符。所以 "no-var": 2 和 "no-var": "error" 是同一个意思。
五、配置格式的演进:flat config
打开配置文件的扩展名,能看出一个时代更替:
- 旧版 :
.eslintrc.json/.eslintrc.js------层层嵌套的配置,可读性差、扩展麻烦。 - 新版 :
eslint.config.mjs------flat config(扁平配置) ,一个扁平的数组,配置项平铺开来,清晰直接。
.mjs 后缀说明这个文件用 ES module (import/export),而不是 CommonJS。
六、逐行解析 eslint.config.mjs
php
import js from "@eslint/js";
import globals from "globals";
import tseslint from "typescript-eslint";
import { defineConfig } from "eslint/config";
export default defineConfig([
{
files: ["**/*.{js,mjs,cjs,ts,mts,cts}"],
plugins: { js },
extends: ["js/recommended"],
languageOptions: { globals: globals.browser },
rules: {
// 级别 2=error 1=warn 警告 0 关闭
"no-var": 2, //不能用var
"no-console": 1, //开发时用, 上线后不用
"quotes": ["error", "double"],
"semi": ["error", "always"],
"indent": ["error", 2] //缩进2个空格
}
},
{ files: ["**/*.js"], languageOptions: { sourceType: "script" } },
tseslint.configs.recommended,
]);
逐块理解:
import js from "@eslint/js":官方规则集。@eslint/js提供一套通用的推荐规则,是extends的基础。import globals from "globals":全局变量的定义包。浏览器里的window、document等变量,ESLint 默认不认识,需要这个包来声明。import tseslint from "typescript-eslint":让 ESLint 能理解 TypeScript 语法(ESLint 原生只懂 JS)。import { defineConfig } from "eslint/config":ESLint 提供的辅助函数,作用就是给配置加类型提示------字段写错,编辑器立刻标红。
再看 defineConfig([...]) 里这个数组,flat config 的「扁平」就体现在这:每一项都是一个独立的配置对象,从上到下依次生效。
第一项(核心配置):
files: ["**/*.{js,mjs,cjs,ts,mts,cts}"]:glob 通配符,声明「这些规则作用于哪些文件」------覆盖 JS、TS 及各种变体。**匹配任意层级。plugins: { js }+extends: ["js/recommended"]:flat config 的新写法------先注册插件,再继承它的recommended预设。languageOptions: { globals: globals.browser }:声明浏览器全局变量。不声明的话,ESLint 会把window误报成window is not defined。
第二项:
{ files: ["**/*.js"], languageOptions: { sourceType: "script" } }:单独针对.js文件,声明它按**传统脚本(CommonJS)**解析(require那套),与package.json里的"type": "commonjs"呼应。sourceType决定文件被当作「模块」还是「脚本」。
第三项:
tseslint.configs.recommended:TypeScript 的推荐规则集,直接平铺进数组,让 ESLint 也检查 TS 代码。
七、逐条解析 rules(重点)
rules 是配置的「灵魂」,每条都对应注释里的一个诉求:
"no-var": 2 ------ 不能用 var
var 有作用域提升、可重复声明等历史包袱,现代用 let/const。级别 2(error),违反直接报错。这是「代码质量」类规则------防的是坑。
"no-console": 1 ------ 开发时用,上线后不用
console.log 调试时很有用,但上线不该留。级别 1(warn),只警告不阻断------因为机器没法判断这个 console 是不是「故意的」,得靠人决定。
这里能看出规则设计的智慧:机器能确定的(
var一定不该用)设 error;机器无法判断意图的(console可能是故意留的)设 warn。
"quotes": ["error", "double"] ------ 统一双引号
数组格式:第一项是级别,第二项是规则专属配置项 。"double" 表示强制双引号("hello"),拒绝单引号('hello')。
"semi": ["error", "always"] ------ 必须加分号
"always" 表示语句末尾始终加分号。这是「风格」类规则------团队统一即可。
"indent": ["error", 2] ------ 缩进 2 个空格
2 表示缩进宽度是 2 个空格(不是 Tab,不是 4 空格)。风格类规则。
八、package.json:怎么跑 ESLint
perl
{
"name": "eslint-demo",
"scripts": {
"lint": "eslint .",
"lint:fix": "eslint . --fix"
},
"type": "commonjs",
"devDependencies": {
"@eslint/js": "^10.0.1",
"eslint": "^10.8.1",
"globals": "^17.11.0",
"typescript-eslint": "^8.66.0"
}
}
"lint": "eslint .":检查当前目录所有文件,列出不符合规则的地方(只检查,不改)。"lint:fix": "eslint . --fix":检查并自动修复 。像分号、缩进、引号这种「机器能确定正确答案」的问题,ESLint 直接帮你改好;但no-console这类「得靠人判断」的,只警告不动。
以后终端跑 npm run lint 检查,npm run lint:fix 检查 + 自动修。
-
devDependencies:ESLint 全家桶装在devDependencies(开发期依赖),因为它们是开发工具,不打包进最终产物。四个包各司其职:eslint:核心引擎@eslint/js:官方规则集globals:全局变量定义typescript-eslint:TypeScript 支持
总结
一篇把 ESLint 从「是什么」到「怎么配」讲透:
- 为什么:统一代码风格 + 提前发现坑,readme 定义为「代码风格规范」。
- 是什么:静态分析工具,不运行代码,靠「读源码」找问题。
- 底层:把源码解析成 AST 抽象语法树,遍历节点套规则------规则本质就是「找某种 AST 形状并判断违规」。
- 级别 :
2/1/0即 error/warn/off,机器能确定的设 error,需人判断的设 warn。 - 配置 :flat config(
eslint.config.mjs)用扁平数组组织,files定范围、rules定规则、tseslint扩展 TS。 - 落地 :
npm run lint检查,npm run lint:fix自动修复。
理解了 AST + 规则级别 + flat config 这三件事,ESLint 就不再是「看不懂的配置文件」,而是一套可以自己掌控的代码质量工具。