记一次ESLint失效--(附原理分析)

0 阅读5分钟

生成ESLint标题图片 (1).png 近日突然发现 ESLint 在 VSCode 中无法自动保存了。

问题现象

保存文件时,ESLint 不再自动修复代码格式(如缩进、引号、分号等)。

当时的配置

settings.json 配置如下:

{
  "workbench.colorTheme": "Default Dark+",
  "typescript.locale": "zh-CN",
  "gitlens.defaultDateLocale": "",
  "editor.fontSize": 14,
  "eslint.format.enable": true,
  "eslint.enable": true,
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": "explicit"
  },
  "eslint.codeActionsOnSave.rules": null,
  "editor.indentSize": "tabSize",
  "eslint.validate": ["javascript", "javascriptreact", "vue", "html"],
  "commentTranslate.hover.string": true,
  "commentTranslate.hover.variable": true
}

根目录配置了 .eslintrc.js,package.json 中的 ESLint 相关依赖:

"@vue/cli-plugin-eslint": "~4.5.15",  // Vue CLI 对 ESLint 的集成,提供 vue-cli-service lint 命令和开发时的 lint-on-save
"eslint": "^6.7.2",                    // ESLint 核心引擎,负责解析规则并执行检查和修复
"eslint-plugin-prettier": "^3.1.4",    // 将 Prettier 的格式化规则作为 ESLint 规则运行,使两者统一
"eslint-plugin-vue": "^6.2.2",         // 提供 Vue 文件(.vue)的 lint 规则,如模板语法检查、组件命名规范等
// 另外 @babel/eslint-parser: 7.15.8  — JS 解析器,让 ESLint 能识别 Babel 转译的语法(如可选链 ?.、空值合并 ?? 等)

尝试修改配置后想到可能是版本问题导致的。于是安装了 ESLint 的历史版本,重启搞定。

image.png

猜测 package.json 安装版本与 VSCode 插件版本冲突。


深入分析:ESLint 自动保存涉及的三层配置

要搞清楚 ESLint 自动保存为什么会失效,首先需要理解整个机制涉及三层配置,它们各司其职:

第 1 层:VSCode/Cursor 全局 User Settings

文件路径: %APPDATA%/Code/User/settings.json(VSCode)或 %APPDATA%/Cursor/User/settings.json(Cursor)

作用: 所有项目的默认编辑器配置。

管什么:

  • ESLint 插件是否启用(eslint.enable
  • 保存时是否触发 ESLint 修复(source.fixAll.eslint
  • 校验哪些文件类型(eslint.validate
  • 各语言的默认格式化器(editor.defaultFormatter

第 2 层:项目 .vscode/settings.json

文件路径: 项目根目录下 .vscode/settings.json

作用: 仅对当前项目生效,会覆盖全局配置中的同名项

管什么: 与全局配置相同的字段,但优先级更高。适合团队统一配置,跟随项目代码提交到 Git。

第 3 层:.eslintrc.js

文件路径: 项目根目录下 .eslintrc.js

作用: 定义具体的代码检查和格式化规则。

管什么:

  • 用单引号还是双引号(quotes
  • 要不要分号(semi
  • 缩进几个空格(indent
  • Vue 模板规范(vue/max-attributes-per-line 等)

补充:.eslintignore — 忽略文件配置

项目根目录下的 .eslintignore 用于指定 ESLint 不需要检查的文件和目录,语法与 .gitignore 一致。当前项目配置如下:

build/*.js                          # 构建脚本,自动生成的代码无需检查
src/assets                          # 静态资源目录(图片、字体等),不含需要 lint 的代码
public                              # 公共静态文件,直接拷贝到输出目录,不经过编译
dist                                # 打包产物,自动生成无需检查
src/views/index/styles/mixins.scss  # SCSS mixin 文件,ESLint 不处理样式文件
node_modules/                       # 第三方依赖,永远不应该被检查

如果不配置 .eslintignore,ESLint 会尝试检查项目中所有匹配 eslint.validate 的文件,包括 node_modulesdist 中的文件,导致检查变慢甚至报大量无关错误。

三者的关系图

┌──────────────────────────────────────────────────┐
│  全局 User Settings                               │
│  决定:ESLint 插件开不开、保存时触不触发              │
│  优先级:最低(被项目配置覆盖)                       │
├──────────────────────────────────────────────────┤
│  项目 .vscode/settings.json                       │
│  决定:当前项目的编辑器行为,覆盖全局同名配置           │
│  优先级:中                                        │
├──────────────────────────────────────────────────┤
│  .eslintrc.js                                     │
│  决定:具体用什么规则检查和修复代码                    │
│  优先级:规则层面最终决定                             │
└──────────────────────────────────────────────────┘

简单记忆:settings.json 管"开关和触发时机",.eslintrc.js 管"具体规则"。

优先级规则

对于编辑器配置(settings.json 中的字段):

项目 .vscode/settings.json  >  全局 User settings.json

同名配置存在于两处时,项目级优先。例如:

配置项全局配置项目配置最终生效
[vue] defaultFormatterVue.volarnullnull
[javascript] defaultFormatterPrettiernullnull
source.fixAll.eslintexplicitexplicitexplicit

保存时的完整执行链路

按下 Ctrl+S 保存文件
  
  
VSCode/Cursor 检查 settings.json 配置
  ├─ editor.formatOnSave?   true: 触发默认格式化器
  ├─ source.fixAll.eslint?  explicit: 触发 ESLint 自动修复
  
  
ESLint 插件(dbaeumer.vscode-eslint)开始工作
  
  ├─ 1. 在项目 node_modules/ 中查找 eslint 引擎
  ├─ 2. 读取 .eslintrc.js 中定义的规则
  └─ 3. 按规则执行自动修复
       ├─ quotes: 'single'      双引号改单引号
       ├─ semi: 'never'         删除分号
       ├─ indent: 2             改为 2 格缩进
       └─ ...其他可自动修复的规则

关键点:ESLint 插件是遥控器,项目 node_modules 中的 eslint 包是电视机,.eslintrc.js 是频道列表。三者缺一不可。


失效的常见原因

1. ESLint 插件版本与项目 eslint 包版本不兼容

这是本次遇到的问题。VSCode ESLint 插件会调用项目 node_modules/eslint 中的引擎,如果插件升级后不再支持旧版 eslint API,就会静默失败。

解决方案: 降级/升级项目中的 eslint 版本,使其与插件兼容。

2. 格式化器冲突

如果同时配置了 editor.formatOnSave: truesource.fixAll.eslint,且默认格式化器(如 Prettier、Volar)的规则和 .eslintrc.js 不一致,就会出现"保存后格式反复跳动"的问题。

典型冲突:

规则Prettier 默认.eslintrc.js 配置
引号双引号 "单引号 '
分号;
尾逗号

解决方案: 关闭 editor.formatOnSave,或者将格式化器设为 null,只保留 ESLint 修复:

{
  "editor.formatOnSave": false,
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": "explicit"
  },
  "[vue]": {
    "editor.defaultFormatter": null
  },
  "[javascript]": {
    "editor.defaultFormatter": null
  }
}

3. ESLint 插件未安装或被禁用

没有安装 dbaeumer.vscode-eslint 插件,或者插件被工作区禁用了。

检查方式: VSCode 左下角状态栏查看是否有 ESLint 图标,点击可查看插件状态和输出日志。

4. node_modules 未安装

项目没有执行 npm install,导致 node_modules/eslint 不存在,插件找不到引擎。

5. .eslintrc.js 配置错误

配置文件中有语法错误或引用了未安装的插件/解析器,导致 ESLint 初始化失败。

检查方式: 打开 VSCode 输出面板(Ctrl+Shift+U),选择 ESLint 通道查看错误日志。


推荐的项目级配置

在项目根目录创建 .vscode/settings.json,跟随代码提交到 Git,确保团队统一:

{
  "editor.formatOnSave": false,
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": "explicit"
  },
  "eslint.validate": [
    "javascript",
    "javascriptreact",
    "vue"
  ],
  "eslint.options": {
    "extensions": [".js", ".vue"]
  },
  "[vue]": {
    "editor.defaultFormatter": null
  },
  "[javascript]": {
    "editor.defaultFormatter": null
  }
}

这样保存时只使用项目 .eslintrc.js 中定义的规则进行自动修复,不会被全局配置或其他格式化器干扰。