Eslint 配置

芸殇
2025-06-11 / 0 评论 / 0 阅读 / 正在检测是否收录...

ESLint 从入门到实战:作用、配置字段与 JavaScript / TypeScript / Vue 配置详解

项目接入 ESLint 后,为什么还是不检查 .vue?为什么浏览器代码提示 window is not defined?为什么安装了插件,规则却没有生效?

这些问题通常来自三件事:文件没有匹配到配置、解析器不理解文件语法,或者规则与运行环境没有正确设置。

本文围绕这条链路解释 ESLint,并提供可独立使用的配置示例。文章以 Flat Config 为主,使用 eslint.config.mjs。

示例要求 ESLint 支持 eslint/config 中的 defineConfig、globalIgnores 和 extends,插件也需要支持 Flat Config。安装前检查 Node.js、ESLint、TypeScript 和插件的兼容要求,安装后提交锁文件。

一、ESLint 到底解决什么问题

ESLint 是静态代码检查工具。它读取源文件,解析代码结构,再按照启用的规则报告问题;部分问题可以自动修复。

例如:

const count = 1;
console.log(coutn);

coutn 很可能是拼写错误。启用 JavaScript 的 no-undef 规则后,可以在运行前发现这个未声明变量。

再如:

let total = 10;
console.log(total);

如果后续没有重新赋值,prefer-const 可以建议改成 const。

ESLint 可以帮助团队:

  • 提前发现未声明变量、重复声明、错误的控制流等代码问题。
  • 通过框架插件检查组件模板、框架约定和特定 API 用法。
  • 统一约定,例如未使用变量的处理、是否允许 debugger。
  • 在编辑器、提交检查和 CI 中执行同一套规则。

但 ESLint 无法证明业务逻辑正确,也不自动检查所有运行时问题。规则有没有开启、规则能否获得类型信息,以及插件能力,都会影响检测范围。

二、ESLint、TypeScript、Prettier 和测试的区别

工具主要关注点例子
ESLint已启用规则约束的代码质量和用法未使用变量、错误 Promise 用法、框架约定
TypeScript 编译器类型系统、编译语义与类型兼容性把字符串传给只接受数字的参数
Prettier代码排版换行、缩进、引号和行宽处理
单元 / 集成测试输入输出和实际行为订单金额、权限判断、接口交互

这些工具有部分重叠,但不能相互替代。开启 ESLint 的 TypeScript 类型感知规则,也不意味着可以省略 tsc --noEmit 或测试。

对于常规项目,可以让 Prettier 处理排版,ESLint 处理代码规则,减少两个工具反复修改同一段代码的问题。

三、理解检查过程:文件、解析器、规则

ESLint 的工作过程可以概括为:

输入文件
  ↓
判断 files / ignores,合并匹配配置
  ↓
解析器读取语法,生成代码结构及作用域信息
  ↓
运行规则;部分 TypeScript 规则进一步使用类型信息
  ↓
输出问题,或执行规则提供的自动修复

这也解释了几类常见问题:

  • 文件没被匹配到:修改规则通常没有帮助。
  • 出现解析错误:要先检查解析器和语法选项。
  • 没有规则报告:可能根本没启用相关规则。
  • 类型规则报缺少类型信息:需要额外的 TypeScript 项目配置。

插件、解析器和共享配置是三个不同概念:

概念职责
Parser,解析器让 ESLint 理解一种语法,例如 TypeScript 或 Vue 单文件组件
Plugin,插件提供规则,也可能提供处理器、语言和预设配置
Shareable config,共享配置将规则与选项打包,供其他配置复用

安装插件不等于启用它的规则。 注册插件后,还需要继承它的预设,或者在 rules 中逐条开启规则。官方插件说明

四、安装并运行第一个检查

以下以 pnpm 为例,在项目根目录执行:

pnpm add -D eslint @eslint/js globals

三个依赖分别负责:

包作用
eslint命令行、配置引擎、规则执行等核心能力
@eslint/jsJavaScript 官方预设,例如 recommended
globals浏览器、Node.js 等环境的全局变量集合

创建 eslint.config.mjs:

import js from '@eslint/js';
import globals from 'globals';
import { defineConfig, globalIgnores } from 'eslint/config';

export default defineConfig([
  globalIgnores(['**/dist/**', '**/coverage/**']),
  {
    name: 'app/browser-js',
    files: ['src/**/*.{js,mjs}'],
    extends: [js.configs.recommended],
    languageOptions: {
      ecmaVersion: 'latest',
      sourceType: 'module',
      globals: globals.browser,
    },
    rules: {
      eqeqeq: ['error', 'always'],
      'prefer-const': 'error',
      'no-debugger': 'error',
    },
  },
]);

检查代码:

pnpm exec eslint src

自动修复支持修复的问题:

pnpm exec eslint src --fix

建议在工作区变更可查看、可回退的情况下执行 --fix,然后审阅 diff。不是每条规则都能自动修复,有些问题需要开发者明确判断。

五、Flat Config 如何生效

Flat Config 通常导出一个配置数组。每个对象可以针对一类文件设置规则,匹配同一文件的对象按顺序参与合并,后面的配置可以覆盖前面的冲突项。

export default defineConfig([
  {
    files: ['src/**/*.js'],
    rules: { 'no-console': 'warn' },
  },
  {
    files: ['src/dev-tools/**/*.js'],
    rules: { 'no-console': 'off' },
  },
]);

src/main.js 使用第一条约定,src/dev-tools/debug.js 同时匹配两项,后面的配置关闭 no-console。

defineConfig 是配置组织和类型提示辅助,不会自动开启检查规则。本文使用 .mjs,是为了明确配置文件采用 ESM,避免与项目 package.json 的模块类型混淆。

files 与 ignores 通常相对配置位置匹配;使用 --config 或 basePath 时应注意其基准变化。运行位置、配置查找和多包仓库结构也会影响最终结果,遇到问题要对具体文件检查解析后的配置。配置文件文档

六、配置字段逐项解释

下面讨论的是 Flat Config。不要将 .eslintrc 的字段原样搬进来。

1. name:配置块的名字

{
  name: 'app/browser-code',
  files: ['src/**/*.js'],
}

它用于识别配置块,排查复杂配置时比“第几个匿名对象”更容易理解。它不是文件匹配条件,也不决定优先级。

2. files:配置作用于哪些文件

files: ['src/**/*.{js,ts}', 'scripts/**/*.mjs']

同一数组里常规字符串表示多个可匹配范围。上面的配置可作用于 src 下的 JS、TS 和 scripts 下的 MJS。

没有 files 并不等于自动检查所有扩展名。增加 .ts、.vue 等文件时,应明确列入匹配范围,并配置理解其语法的解析器。

3. ignores:排除配置作用范围

这里最容易混淆的是全局忽略与局部忽略:

globalIgnores(['**/dist/**'])

用于整个配置的忽略范围。

{
  files: ['src/**/*.js'],
  ignores: ['src/generated/**'],
  rules: { 'no-console': 'warn' },
}

表示 generated 文件不应用这个配置块,但它们仍可能被其他配置块检查。局部忽略不能当成整个项目的免检名单。

目录匹配与文件匹配的细节不同;如果目的是让构建产物整体不被检查,优先显式使用 globalIgnores。

4. basePath:为一个目录组织配置

{
  basePath: 'packages/web',
  files: ['src/**/*.js'],
  rules: { 'no-alert': 'error' },
}

这里的文件模式相对 packages/web。它适合组织 monorepo 子包配置,但不是切换 Node.js 工作目录,也不等于 TypeScript 的 tsconfigRootDir。

5. extends:继承配置

{
  files: ['src/**/*.js'],
  extends: [js.configs.recommended],
  rules: { 'no-unused-vars': 'warn' },
}

先应用推荐配置,再设置项目覆盖项。extends 可以继承配置对象、配置数组,也可以引用插件配置名称;它与 .eslintrc 中字符串配置的解析方式有所区别。

如果所用 ESLint 不支持这里的 extends 写法,应先检查其 API 和依赖兼容范围。

6. languageOptions:代码采用什么语言语义

常见子字段如下:

字段作用示例
ecmaVersion配置支持的 ECMAScript 语法版本及相关语言语义'latest'、2022
sourceType区分模块、传统脚本和 CommonJS'module'、'script'、'commonjs'
globals告诉检查器哪些标识符由环境提供,以及是否可写window: 'readonly'
parser指定解析器对象tseslint.parser
parserOptions向解析器传递选项JSX、TS 项目、Vue 内层解析器配置

ecmaVersion: 'latest' 不会把代码编译成兼容旧浏览器的版本,也不提供 polyfill。实际运行兼容性仍由编译链与目标环境决定。

languageOptions: {
  sourceType: 'module',
  globals: {
    ...globals.browser,
    __APP_VERSION__: 'readonly',
  },
}

声明 __APP_VERSION__ 只代表它可被代码引用,不会真的创建这个变量;运行时仍要由构建工具或宿主注入。语言选项说明

7. plugins:注册插件命名空间

import examplePlugin from 'eslint-plugin-example';

// 示意代码:需要先安装真实存在且兼容的插件
const config = {
  plugins: { example: examplePlugin },
  rules: { 'example/some-rule': 'error' },
};

example 是命名空间,example/some-rule 是规则名。Flat Config 注册的是导入的插件对象,而不是旧版插件名称字符串数组。

上面仅用于解释结构,不是可以直接安装运行的业务配置。实际规则名以插件文档为准。

8. rules:规则、等级和选项

rules: {
  'no-debugger': 'error',
  'no-console': ['warn', { allow: ['warn', 'error'] }],
  eqeqeq: ['error', 'always'],
}
等级数字写法含义
off0不执行该规则
warn1报告警告
error2报告错误,通常使检查失败

数组第一个元素是等级,其余元素由具体规则定义。不能把一个规则的配置选项套到另一个规则上。

继承与覆盖还存在一个细节:只改规则等级时,可以保留已有选项。因此调试规则结果时,应查看最终配置,而不是只看最后写下的一行。规则配置说明

9. settings:传给规则的共享数据

settings: {
  'example/project-kind': 'frontend',
}

这个字段提供共享数据,但必须有规则或插件读取这些数据才会产生作用。随意写 settings.alias 不会自动让所有导入检查插件理解 Vite 或 TypeScript 的路径别名。

10. linterOptions:控制检查器行为

linterOptions: {
  noInlineConfig: false,
  reportUnusedDisableDirectives: 'error',
}

noInlineConfig 控制源代码中的 ESLint 配置注释是否可以修改配置。

reportUnusedDisableDirectives 用于发现不再需要的 eslint-disable 注释,避免代码已经修好,关闭规则的注释却一直遗留。

11. processor:先处理非普通 JS 文件

某些插件会从 Markdown 等文件中提取代码块交给 ESLint,再映射回原文件位置。处理器既可能由插件预设配置,也可以显式设置。

不要为了处理 Vue 单文件组件就盲目填写 processor;常见 Vue 配置主要依赖 vue-eslint-parser,应遵循 Vue 插件文档。

12. language:选择插件提供的语言

一些插件支持 Markdown、JSON 等语言,可以使用 插件命名空间/语言名称 指定语言。它与 JavaScript 的 sourceType 不同,也不是通过填一个字符串就能让 ESLint 理解任意格式;必须安装并注册提供该语言的插件。

七、JavaScript 项目的完整配置

浏览器代码和 Node.js 工具脚本应分别设置全局变量:

// eslint.config.mjs
import js from '@eslint/js';
import globals from 'globals';
import { defineConfig, globalIgnores } from 'eslint/config';

export default defineConfig([
  globalIgnores(['**/dist/**', '**/coverage/**']),
  {
    name: 'app/browser',
    files: ['src/**/*.{js,mjs}'],
    extends: [js.configs.recommended],
    languageOptions: {
      sourceType: 'module',
      globals: globals.browser,
    },
    linterOptions: {
      reportUnusedDisableDirectives: 'error',
    },
    rules: {
      eqeqeq: ['error', 'always'],
      'prefer-const': 'error',
      'no-console': ['warn', { allow: ['warn', 'error'] }],
    },
  },
  {
    name: 'app/node-esm',
    files: ['scripts/**/*.{js,mjs}', '*.config.mjs', 'eslint.config.mjs'],
    extends: [js.configs.recommended],
    languageOptions: {
      sourceType: 'module',
      globals: globals.nodeBuiltin,
    },
  },
  {
    name: 'app/node-cjs',
    files: ['scripts/**/*.cjs'],
    extends: [js.configs.recommended],
    languageOptions: {
      sourceType: 'commonjs',
      globals: globals.node,
    },
  },
]);

不要为了消除报错给所有文件同时加入 browser 和 node 全局变量。这样可能让浏览器代码误用 process 等环境标识符而不被发现。需要构建时注入的值,应按实际机制单独配置。

八、TypeScript 基础检查

安装依赖:

pnpm add -D eslint @eslint/js typescript typescript-eslint globals

基础配置:

// eslint.config.mjs
import js from '@eslint/js';
import tseslint from 'typescript-eslint';
import globals from 'globals';
import { defineConfig, globalIgnores } from 'eslint/config';

export default defineConfig([
  globalIgnores(['**/dist/**', '**/coverage/**']),
  {
    files: ['src/**/*.{ts,tsx}'],
    extends: [
      js.configs.recommended,
      tseslint.configs.recommended,
    ],
    languageOptions: {
      globals: globals.browser,
    },
    rules: {
      '@typescript-eslint/no-unused-vars': [
        'error',
        {
          args: 'all',
          argsIgnorePattern: '^_',
          varsIgnorePattern: '^_',
        },
      ],
    },
  },
]);

typescript-eslint 的 recommended 预设会提供解析器及必要的规则配置,并处理部分与 TypeScript 不兼容或重复的核心规则。

这份配置支持 TS/TSX 语法检查,但没有开启类型感知,也没有自动加入 React 组件或 Hooks 规则。.tsx 能解析,与是否符合框架使用约定,是两件事。TypeScript 快速入门

核心规则与 TypeScript 扩展规则

例如下面两个名字属于不同规则:

no-unused-vars
@typescript-eslint/no-unused-vars

TypeScript 版本理解类型语法和 TS 代码结构。手动开启扩展规则时,通常需要关闭对应核心规则,避免冲突或重复报告;使用推荐配置时,先检查预设已经做了哪些处理,不要重复拼装。

九、TypeScript 类型感知检查

基础语法检查不一定能判断某个函数的返回值是不是 Promise。要使用这类规则,需要为规则提供类型信息。

// eslint.config.mjs
import js from '@eslint/js';
import tseslint from 'typescript-eslint';
import globals from 'globals';
import { fileURLToPath } from 'node:url';
import { defineConfig, globalIgnores } from 'eslint/config';

const rootDir = fileURLToPath(new URL('.', import.meta.url));

export default defineConfig([
  globalIgnores(['**/dist/**', '**/coverage/**']),
  {
    files: ['src/**/*.{ts,tsx}'],
    extends: [
      js.configs.recommended,
      tseslint.configs.recommendedTypeChecked,
    ],
    languageOptions: {
      globals: globals.browser,
      parserOptions: {
        projectService: true,
        tsconfigRootDir: rootDir,
      },
    },
    rules: {
      '@typescript-eslint/no-floating-promises': 'error',
    },
  },
]);
字段作用
recommendedTypeChecked启用推荐规则,并包含需要类型信息的检查
projectService使用 TypeScript 项目服务为文件提供类型信息
tsconfigRootDir指明 TS 项目相关路径处理的根目录

类型感知检查需要额外工作,通常比纯语法检查更慢。应确保被检查文件属于正确的 TypeScript 项目,并与编辑器采用的 TypeScript 配置一致。类型感知配置

如果出现“文件未被 project service 包含”的错误,不要直接关闭全部类型规则:先确认文件是否应该加入对应 tsconfig.json。对于构建脚本,可以单独使用不需要类型信息的配置;少量项目外文件才考虑有针对性的默认项目设置。排错说明

projectService 与 parserOptions.project 不需要同时开启;使用项目服务时,应移除不必要的 project 设置。解析器选项

十、Vue 3 + TypeScript 配置

Vue 单文件组件同时包含 template、script、style,不能简单把 .vue 文件当普通 TypeScript 文件交给 TS 解析器。

安装依赖:

pnpm add -D eslint @eslint/js typescript typescript-eslint \
  eslint-plugin-vue vue-eslint-parser globals

以下是不启用类型感知的基础配置:

// eslint.config.mjs
import js from '@eslint/js';
import tseslint from 'typescript-eslint';
import vue from 'eslint-plugin-vue';
import vueParser from 'vue-eslint-parser';
import globals from 'globals';
import { defineConfig, globalIgnores } from 'eslint/config';

export default defineConfig([
  globalIgnores(['**/dist/**', '**/coverage/**']),
  {
    files: ['src/**/*.{ts,tsx,vue}'],
    extends: [
      js.configs.recommended,
      tseslint.configs.recommended,
    ],
    languageOptions: {
      globals: globals.browser,
    },
  },
  ...vue.configs['flat/recommended'],
  {
    files: ['src/**/*.vue'],
    languageOptions: {
      parser: vueParser,
      parserOptions: {
        parser: tseslint.parser,
        extraFileExtensions: ['.vue'],
      },
    },
  },
]);

关键是解析器有两层:

.vue 文件
  → vue-eslint-parser 理解组件结构和模板
    → typescript-eslint parser 理解 script 中的 TypeScript

最外层 languageOptions.parser 不能被 TS 解析器覆盖,否则 Vue 模板可能解析失败。内层解析器配置在 languageOptions.parserOptions.parser。Vue 配置说明

开启 Vue 类型感知检查时,还要考虑项目服务、.vue 文件支持与框架配置的协作,不能简单把上一节所有参数复制进来。复杂项目可从框架官方提供的配置开始,再按项目调整。Vue TypeScript 配置项目

recommended 可能包含模板排版规则。若项目使用 Prettier,继续配置下一节的冲突处理。

十一、与 Prettier 配合

安装:

pnpm add -D prettier eslint-config-prettier

在已有配置的末尾加入:

import eslintConfigPrettier from 'eslint-config-prettier/flat';

export default defineConfig([
  // 项目规则、TS 或 Vue 预设……
  eslintConfigPrettier,
]);

eslint-config-prettier 关闭其已知的冲突规则,本身不负责格式化,也不会自动运行 Prettier。后面若再次开启冲突规则,仍然可能出现反复修改。

将格式检查单独执行:

pnpm exec prettier . --check
pnpm exec prettier . --write

eslint-plugin-prettier 则是另一种集成方式:把 Prettier 结果作为 ESLint 问题报告。并非必须同时安装两种方案;对大多数项目,单独执行 lint 和 format 更容易看清各自职责。配置说明

十二、接入 package.json 和 CI

在 package.json 中合并以下 scripts,不要覆盖项目其他字段:

{
  "scripts": {
    "lint": "eslint .",
    "lint:fix": "eslint . --fix",
    "lint:ci": "eslint . --max-warnings 0",
    "format": "prettier . --write",
    "format:check": "prettier . --check",
    "typecheck": "tsc --noEmit"
  }
}

Vue 项目的类型检查通常使用项目已配置的 vue-tsc 等工具,不能把 tsc --noEmit 示例当成所有框架的统一命令。

CI 中通常检查而不静默修复:

pnpm install --frozen-lockfile
pnpm lint:ci
pnpm format:check
pnpm typecheck

warn 默认不会像规则错误那样使检查失败;--max-warnings 0 可以把任何警告视为未通过,因此“只是 warning”不代表 CI 一定成功。

大项目可考虑 --cache,但缓存不是跳过完整验收的理由。--fix-dry-run 可用于不写入文件的修复预演,不过是否展示修复后的文本取决于输出格式,不能把它当成默认生成补丁的命令。CLI 文档

十三、编辑器保存时修复

以安装了 ESLint 扩展的 VS Code 为例,可以配置:

{
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": "explicit"
  },
  "eslint.validate": [
    "javascript",
    "javascriptreact",
    "typescript",
    "typescriptreact",
    "vue"
  ]
}

这只是编辑器接入,不会替代项目规则文件。显式保存时尝试修复的具体行为,还受扩展版本、工作区信任、语言模式和配置匹配影响。

不要同时让多个格式化器处理同一文件,却不明确哪个负责哪类修改。命令行能运行、编辑器不工作时,应看 ESLint 扩展输出和实际工作目录。VS Code ESLint 扩展说明

十四、旧版 .eslintrc 如何对应

下面用于理解 .eslintrc 与 Flat Config 的对应关系,不需要同时维护两份配置。

旧式配置Flat Config 中通常的处理方式
.eslintrc.*eslint.config.mjs 等 Flat Config 文件
env.browser / env.nodelanguageOptions.globals 中添加环境全局变量
顶层 parser 字符串languageOptions.parser 导入解析器对象
顶层 parserOptions放入 languageOptions.parserOptions,部分 JS 语言选项直接位于 languageOptions
plugins: ['vue']导入插件对象并注册命名空间,或使用插件预设
overrides拆分为多个带 files 的配置块
.eslintignore / ignorePatterns显式迁移到全局或局部 ignores
root: trueFlat Config 不使用这个旧字段
extends: ['eslint:recommended']导入 @eslint/js 后复用推荐配置

迁移时需要重新检查忽略路径,不能认为 .eslintignore 会被 Flat Config 自动沿用。.eslintrc 项目中的 temp.js 等模式,也应按 Flat Config 的匹配语义决定是否写成 **/temp.js。官方迁移指南

十五、怎样排查“配置没有生效”

优先针对一个确切文件排查:

pnpm exec eslint src/main.ts
pnpm exec eslint --print-config src/main.ts
pnpm exec eslint --debug src/main.ts
pnpm exec eslint --inspect-config

--print-config 用于看一个文件最终合并后的配置,--debug 帮助追踪加载与执行,配置检查器适合查看配置匹配关系。检查器相关组件可能需要额外安装或下载,按实际工具提示处理。调试文档

症状优先检查
.ts 或 .vue 不检查是否存在对应 files、解析器与预设
window 或 document 未声明浏览器文件是否配置 globals.browser
ESM 中出现 require 使用问题是否混用了 ESM 与 CommonJS 配置
插件规则找不到插件是否安装、注册名是否与规则前缀一致
未使用变量重复报错是否同时启用了核心和 TS 扩展规则
TS 类型规则缺少信息是否启用正确项目服务,文件是否属于项目
Vue template 解析失败最外层解析器是否仍是 vue-eslint-parser
某规则总被覆盖匹配到哪些配置块,后面是否重写了该规则
构建产物仍被检查是否错误地把局部 ignores 当成全局忽略
本地正常、CI 失败版本、锁文件、执行目录、warning 门槛是否一致

十六、合理使用禁用注释

临时例外可以写明规则和原因:

// eslint-disable-next-line no-console -- 此命令行入口需要输出结果
console.log(result);

尽量不要不带规则名地关闭整个文件所有检查。先判断是规则配置不适合这个目录、可以修正的代码问题,还是需要有理由保留的例外。

对工具脚本统一允许日志,可以通过 files 配置表达,比在几十个文件中重复 disable 更容易维护。

十七、团队落地时的配置顺序

对已有项目,可以先从官方 recommended 起步,明确 JS、TS、Vue 的文件范围,解决解析和环境问题,再逐步增加项目规则。

每增加一组规则,都应该知道它解决什么问题、误报如何处理、是否需要类型信息,以及在 CI 中采用什么门槛。不要一开始启用所有规则,再通过大量关闭规则让项目“变绿”。

配置变更应和依赖锁文件一起审阅;升级预设可能改变启用的规则,即使自己的 rules 没有变化,也要重新检查差异和执行结果。

ESLint 的配置最终应能回答四个问题:检查哪些文件、如何理解这些文件、执行哪些规则,以及在哪些环节阻止问题进入代码库。

0

评论 (0)

取消