ESLint 代码规范完全指南——从 AST 原理到 flat config 逐行解析

ESLint 代码规范完全指南------从 AST 原理到 flat config 逐行解析

摘要:readme 里把 eslint 标注为「代码风格规范」,但这五个字背后藏着一整套机制。本文从「为什么需要 ESLint」讲到它底层的 AST 原理,再逐行拆解一份真实的 eslint.config.mjspackage.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 moduleimport/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" :全局变量的定义包。浏览器里的 windowdocument 等变量,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 从「是什么」到「怎么配」讲透:

  1. 为什么:统一代码风格 + 提前发现坑,readme 定义为「代码风格规范」。
  2. 是什么:静态分析工具,不运行代码,靠「读源码」找问题。
  3. 底层:把源码解析成 AST 抽象语法树,遍历节点套规则------规则本质就是「找某种 AST 形状并判断违规」。
  4. 级别2/1/0 即 error/warn/off,机器能确定的设 error,需人判断的设 warn。
  5. 配置 :flat config(eslint.config.mjs)用扁平数组组织,files 定范围、rules 定规则、tseslint 扩展 TS。
  6. 落地npm run lint 检查,npm run lint:fix 自动修复。

理解了 AST + 规则级别 + flat config 这三件事,ESLint 就不再是「看不懂的配置文件」,而是一套可以自己掌控的代码质量工具


相关推荐
用户938515635072 小时前
深入理解 Next.js:从 SPA 的 SEO 问题到约定式路由与 SSR 原理
前端·后端·全栈
kyriewen4 小时前
今年裁了16万技术人——但字节前端岗反而涨了23%
前端·javascript·面试
码事漫谈4 小时前
Deepseek涨价了,前后对比,竟然差这么多
后端
东风破_5 小时前
大前端手里的 Next.js:从 #root 到 SEO,一份 HTML 的两种命运
前端·后端
IT_陈寒5 小时前
Vue的v-for不听话?我被这个Key的坑整懵了
前端·人工智能·后端
众人皆醒我独醉5 小时前
训练加速实战:Flash Attention、Gradient Checkpointing 与数据流水线
后端·面试·gpu
王林不想说话5 小时前
ES6 到 ES2026 全面进阶指南:新特性、原理、示例与工程落地
前端·javascript·面试
阿黎梨梨5 小时前
Next.js 全栈开发:从 SPA 的痛点到 SSR 的破局之道
前端·后端
WZzz5 小时前
JavaScript 中 this 的「指向」完整教程
javascript