近日突然发现 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 的历史版本,重启搞定。
猜测 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_modules 和 dist 中的文件,导致检查变慢甚至报大量无关错误。
三者的关系图
┌──────────────────────────────────────────────────┐
│ 全局 User Settings │
│ 决定:ESLint 插件开不开、保存时触不触发 │
│ 优先级:最低(被项目配置覆盖) │
├──────────────────────────────────────────────────┤
│ 项目 .vscode/settings.json │
│ 决定:当前项目的编辑器行为,覆盖全局同名配置 │
│ 优先级:中 │
├──────────────────────────────────────────────────┤
│ .eslintrc.js │
│ 决定:具体用什么规则检查和修复代码 │
│ 优先级:规则层面最终决定 │
└──────────────────────────────────────────────────┘
简单记忆:settings.json 管"开关和触发时机",.eslintrc.js 管"具体规则"。
优先级规则
对于编辑器配置(settings.json 中的字段):
项目 .vscode/settings.json > 全局 User settings.json
同名配置存在于两处时,项目级优先。例如:
| 配置项 | 全局配置 | 项目配置 | 最终生效 |
|---|---|---|---|
[vue] defaultFormatter | Vue.volar | null | null |
[javascript] defaultFormatter | Prettier | null | null |
source.fixAll.eslint | explicit | explicit | explicit |
保存时的完整执行链路
按下 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: true 和 source.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 中定义的规则进行自动修复,不会被全局配置或其他格式化器干扰。