尧图精选

Vue3+TS 项目 VS Code ESLint 自动修正配置指南

🕒 发布时间:2026/10/1 18:36:03 📁 来源:尧图网络
在 VS Code 里配 ESLint 自动修正这件事说简单也简单——装个插件、加两行配置、保存即修复。但真到团队里、真到 Vue3 TypeScript 的工程里你会发现每个人的编辑器行为不一致有人保存能修有人保存报一堆红还有人干脆把 lint 关了。我自己在好几个项目里反复折腾过这套东西从早期的.eslintrc时代一路做到现在的 Flat Config踩过的坑包括插件版本不匹配、Prettier 和 ESLint 规则打架、monorepo 下插件找不到配置文件、保存时全量 fix 导致大文件卡死等等。这篇文章就把这套VsCode 配置 EsLint 自动修正的完整链路拆开讲一遍从为什么要配、插件和依赖怎么选、配置文件怎么写、settings.json每个开关的取舍到实测流程和问题排查速查表。不管你是刚装完 VS Code 的新手还是在带团队做工程规范的老手都能从里面拿到可以直接抄的配置和一部分只有实际跑过才会知道的细节。1. 先把这件事的边界理清楚自动修正到底在修什么很多人对保存自动修正有一个误解以为它是个万能的代码美化开关按下 CtrlS 代码就会变得整整齐齐。实际情况比这个复杂ESLint 的自动修正只覆盖它能确定且安全改写的那部分规则比如多余的分号、未使用的变量导入、引号风格、和这类。而像这个函数是不是该拆成两个这种需要理解语义的判断ESLint 给不了也不该给。所以第一步得先把各个工具的分工讲清楚不然配到最后会陷入为什么我保存了代码还是乱的困惑。1.1 ESLint、Prettier、编辑器三者各自管什么我用一个比较好记的类比来区分把写代码当成写一篇文章。ESLint 是审稿编辑它关心的是这么写会不会有逻辑隐患比如变量声明了没用、switch忘了break、数组回调里用了异步却没处理。Prettier 是排版美工它只在乎看起来齐不齐比如缩进是两格还是四格、一行超过多少字符要换行、函数参数太长要不要逐个换行。VS Code 是执行现场它自己不产生规则只负责在你按下保存的时候把上面两位编辑叫过来干活。理解这个分工很关键因为它直接决定了配置文件该怎么放。我见过有人把所有格式化规则都塞进 ESLint也见过有人只用 Prettier 而完全不跑 lint两种做法都会出问题。前者会让 ESLint 变得臃肿缓慢后者会漏掉真正的代码质量问题。合理的边界是代码质量类规则交给 ESLint纯格式类规则交给 Prettier然后想办法让两者不打架。顺带说一句ESLint 从 v9 开始默认使用 Flat Config也就是eslint.config.js旧的.eslintrc.json会逐步被替代。这不是一个纯技术细节——它直接影响你在 VS Code 里配插件时用哪个开关。新项目我建议直接上 Flat Config老项目迁移时再权衡。1.2 为什么自动修正要分成三层来做我给自动修正这件事定了三层结构缺一层都会出问题。第一层是编辑器内保存时修正也就是这篇文章的主线。它的价值是给出最快的反馈循环——你写完一个函数保存违规的红线立刻被修掉或者提示出来写代码的过程中规范就不停被拉回来。这一层比拼的是体验。第二层是提交前修正靠 Git 钩子在pre-commit阶段对暂存文件跑一次eslint --fix。它的价值是兜底因为你不可能保证每个人编辑器都配对了。我一直用lint-staged做这件事只对暂存的文件跑速度可以接受。第三层是集成流程里做检查在构建或合并请求阶段跑一次不带--fix的eslint .任何违规直接让流程失败。这一层的作用不是修是防止有人本地没配好就把坏代码合进去。注意三层里只有第一层是修正后两层一个是补救、一个是拦截。千万别把--fix放在集成流程里那样等于让机器替人做代码决策出了问题是很难追责的。先把这三层的定位记在心里后面的配置才知道每一行是为了哪一层服务的。很多配了没生效的问题本质上都是层级混淆——比如把本该在项目根目录生效的配置写到了用户级设置里。2. 环境准备与插件选型别在第一步就埋雷环境这块看起来最没技术含量但它是最容易出问题的地方。我统计过自己处理过的自动修正失效案例超过一半最后追到根源都是版本不匹配或者装错了插件。这一章节把依赖顺序、插件取舍和团队协作这三件事讲透。2.1 Node、包管理器与项目级依赖的安装要点ESLint 是跑在 Node 上的所以第一个前提是你的 Node 版本要够新。ESLint v9 要求 Node 18.18 以上v8 要求 12 以上。现在一般新项目直接上 Node 20 LTS 或者 22 LTS问题不大。但如果你是接手一个老项目本机 Node 版本和 CI 上不一致会出现本地能过、流程上失败的诡异现象。我的建议是用.nvmrc或者package.json里的engines字段把版本锁住。然后是包管理器。npm、pnpm、yarn 都能用但在 monorepo 里要特别注意依赖提升的问题。pnpm 的严格依赖结构会让某些 ESLint 插件找不到自己的依赖表现为插件解析失败。遇到这种报错先检查是不是需要shamefully-hoist或者把插件显式装到子包的依赖里。依赖的顺序也有讲究。所有 ESLint 相关的东西都装成项目级开发依赖别装全局。全局装 ESLint 是历史遗留习惯现在不仅没必要还会造成版本混乱。具体命令是这样# 初始化一个 lint 配置交互式问答新手推荐 npm init eslint/configlatest # 或者手动装适合想控制每一个包的情况 npm i -D eslint eslint/js typescript-eslint eslint-plugin-vue globals prettier eslint-config-prettier # 如果要接 Git 钩子 npm i -D husky lint-staged顺序上我习惯先装 ESLint 本体再装语言相关的 plugin 和 parser最后接 Prettier。每装一层就验证一次别一口气装完再排查不然出错了根本不知道是哪一个引入的。2.2 VS Code 插件怎么选ESLint、Prettier、EditorConfig 的分工VS Code 插件市场里名字带 lint 的一抓一大把装错的情况太常见了。我只用三个ESLint 官方扩展发布者显示为 Microsoft、Prettier 官方扩展Prettier 团队、EditorConfig for VS Code。注意 ESLint 扩展有好几个同名或近名的认准发布者和下载量再装。三个插件的职责再明确一遍插件负责的事不需要它做的事ESLint调用项目里的 ESLint 跑规则、展示红线、执行 fix不要指望它做排版Prettier缩进、换行、引号此类纯格式化不负责代码质量判断EditorConfig统一缩进、换行符、文件末尾空行这类基础约定不做规则检查这里有个很常见的冲突点如果你同时让 ESLint 的格式规则和 Prettier 生效保存时会出现改了又改回去的反复横跳。解决方式在 3.3 节详细说简单讲就是用eslint-config-prettier把和 Prettier 冲突的 ESLint 规则关掉让格式这事只有一个话事人。还有一个细节ESLint 扩展从 v3.x 开始需要显式声明eslint.useFlatConfig新版本会自动探测。如果你用的是 Flat Config 但插件还按老模式找.eslintrc就会提示找不到配置这个坑我踩过排查时容易误以为是自己配置写错了。2.3 团队协作下编辑器设置和项目配置的取舍这是我最想强调的一节。规则应该放在项目里编辑器行为可以随个人偏好。什么意思呢像是否禁止用var未使用变量是报错还是警告这种规则属于团队约定必须写进仓库里的eslint.config.js让所有人一致。而保存的时候要不要顺手格式化用不用自动保存这类属于个人习惯放在你自己的用户级settings.json里就行不用强行要求同事。但现实里往往是反过来的新人把source.fixAll.eslint: true塞进项目级的.vscode/settings.json结果所有人的保存行为都被改了有人写代码依赖自动保存、有人手动保存体验完全乱套。我的处理方式是只把和规则强相关的编辑器设置放进.vscode/settings.json比如指定用哪个配置来源、要校验哪些文件类型把保存时做什么留给用户自己决定。提示.vscode/settings.json要提交到仓库.vscode/extensions.json建议也提交这样同事打开项目时 VS Code 会提示安装推荐插件减少我这边没装插件的扯皮。3. ESLint 配置文件从零写起以 Flat Config 为主线到了核心部分。配置文件是整个自动修正链路的规则来源配置写错编辑器再对也没用。这一章我用 Flat Config 为主线因为它是当前和未来的默认形态同时会在必要处标注老版本.eslintrc的差异。3.1 eslint.config.js 的结构与关键字段逐条拆解Flat Config 的核心是一个可以导出对象数组的文件。数组里每个对象是一层配置后面的覆盖前面的这一点和白名单式的老配置差别很大理解了这个覆盖顺序调试时就容易多了。下面是我在一个 Vue3 TypeScript 项目里常用的起点逐段说明// eslint.config.js import js from eslint/js; import tseslint from typescript-eslint; import pluginVue from eslint-plugin-vue; import globals from globals; import prettierConfig from eslint-config-prettier; export default tseslint.config( // 第一层忽略哪些目录别让 lint 去扫产物 { ignores: [dist/**, node_modules/**, coverage/**, *.min.js] }, // 第二层JS 官方推荐规则 js.configs.recommended, // 第三层TypeScript 推荐规则 ...tseslint.configs.recommended, // 第四层Vue 推荐规则 ...pluginVue.configs[flat/recommended], // 第五层自己的定制规则 { files: [**/*.{js,mjs,cjs,ts,vue}], languageOptions: { ecmaVersion: latest, sourceType: module, globals: { ...globals.browser, ...globals.node } }, rules: { no-unused-vars: off, typescript-eslint/no-unused-vars: [ warn, { argsIgnorePattern: ^_, varsIgnorePattern: ^_ } ], no-console: [warn, { allow: [warn, error] }] } }, // 第六层Prettier 放最后关闭所有与格式化冲突的规则 prettierConfig );几个字段值得单独说ignores一定要写。我第一次配的时候没写结果整个dist目录被扫几万个报错编辑器直接卡死。忽略目录要跟你的构建输出保持一致Vue 项目一般是distNext.js 项目是.next。files的写法在 Flat Config 里是 glob**/*.{js,ts,vue}这种花括号写法是支持的。注意老版本.eslintrc里用overrides数组做的事在 Flat Config 里就是并列多个配置对象用files区分作用范围思路是平铺而不是嵌套。languageOptions.globals决定了哪些全局变量不被当成未定义。在浏览器项目里window、document要放进去Node 项目里process、__dirname要放进去。新手最常遇到的no-undef报错八成就是 globals 没配全。3.2 TypeScript、Vue 的适配要点与版本搭配TypeScript 这边typescript-eslint是目前的事实标准它把 parser 和一堆规则打包在一起。启用带类型信息的规则需要额外配projectService或者parserOptions.project好处是能检查出这个 await 用在了非 Promise 上这种类型相关的逻辑问题代价是慢一些。我个人的取舍是中小项目开启超大项目只在关键目录开启。{ files: [**/*.ts, **/*.vue], languageOptions: { parserOptions: { // 让 ts 规则能拿到类型信息 projectService: true, tsconfigRootDir: import.meta.dirname } } }Vue 这边有一个坑要特别提醒.vue文件里既有模板又有脚本脚本部分可能是 TS所以需要两层 parser——外层vue-eslint-parser负责解析.vue内层parserOptions.parser指定脚本部分由typescript-eslint解析。用官方推荐的pluginVue.configs[flat/recommended]会自动配好外层但内层如果你要检查 TS 语法还得自己补一句{ files: [**/*.vue], languageOptions: { parserOptions: { parser: tseslint.parser // 关键让 vue 文件里的 script langts 能被解析 } } }漏掉这句的表现是.vue文件里写 TS 类型标注直接报解析错误而且这类报错不做修正只能手工改。我第一次遇到的时候以为是插件坏了排查了半天。3.3 把 Prettier 接进来避免两个工具互相打架前面说过ESLint 和 Prettier 在格式规则上会重叠。处理方式有两种主流路线路线 AESLint 只管质量Prettier 只管格式。用eslint-config-prettier关掉所有和 Prettier 冲突的 ESLint 规则格式完全交给 Prettier。这是我现在用的方案清晰、省心。路线 B让 ESLint 跑 Prettier 规则。用eslint-plugin-prettier把 Prettier 当成一条 ESLint 规则跑好处是只跑一次工具坏处是报错信息变成 Prettier 的、而且慢。如果两个工具都各跑各的还都往保存时挂一定会打架。我选 A 的理由很实际格式差异对代码审查基本无意义而 Prettier 的零配置哲学让团队不用争论两个空格还是四个空格。ESLint 只需要专注它擅长的逻辑问题。接入方式就是在配置数组最后加一层eslint-config-prettier它本质是一堆off规则负责把冲突项关掉。这里有个版本细节eslint-config-prettier从 v10 开始提供了 flat 版本直接import默认导出即可老版本可能要用prettierConfig.rules按你的版本调整。4. VS Code 侧的关键配置settings.json 怎么写规则写好了接下来是编辑器怎么调用它。VS Code 的设置分用户级和项目级改错了作用范围会出现我这边好使同事那边不好使的经典问题。4.1 保存自动修正的核心开关与作用域最小可用配置是这样几条{ editor.codeActionsOnSave: { source.fixAll.eslint: explicit }, eslint.validate: [javascript, typescript, vue], eslint.useFlatConfig: true }逐条解释editor.codeActionsOnSave里的source.fixAll.eslint是 ESLint 扩展注册的一个代码操作来源。把它挂在这里保存时扩展就会调eslint --fix能修的部分。值的写法经历了变化老版本写true新版支持explicit只在手动保存时修和always连自动保存也修。我推荐explicit因为always配合自动保存files.autoSave: afterDelay会变成你每敲一会儿它就修一次光标位置会跳写代码体验很差。eslint.validate决定扩展对哪些语言激活。默认可能不含vue所以在 Vue 项目里你不显式加上它.vue文件保存就不会被修。这就是为什么我的 JS 能自动修、Vue 不行的答案。eslint.useFlatConfig是给 Flat Config 用的开关较新版本能自动探测但显式写上更稳。4.2 codeActionsOnSave 的精细化控制该修什么不该修什么source.fixAll.eslint是把能修的全修。但有些场景你会想更细地控制比如只想修 import 顺序、或者不想要某些激进改写。这时可以用其它来源或者干脆在 ESLint 规则层面把对应的fixable规则关掉。还有个容易忽略的点保存时的 fix 是有范围限制的。默认扩展只会修当前文件不会去动你没打开的文件。这其实是好事避免你按一次保存修改了整个仓库。另一个实战技巧是区分修和提示。有些规则不提供 fix只会在问题面板里标红。对于这类规则编辑器能做的就是展示处理还得靠人。所以不要把保存自动修正想成清空所有警告它只能清掉带扳手图标的那部分。我个人的设置习惯是{ editor.formatOnSave: true, editor.defaultFormatter: esbenp.prettier-vscode, editor.codeActionsOnSave: { source.fixAll.eslint: explicit, source.organizeImports: never } }source.organizeImports设成never是有意为之——VS Code 自带的 import 整理和 ESLint 的 import 规则经常打架两个人抢着排同一个列表。我选择让其中一个闭嘴规则统一交给一边。4.3 多根工作区、monorepo 下的目录级覆盖monorepo 是自动修正最容易失效的场景。原因是 ESLint 扩展默认在工作区根目录找配置文件如果你的子包配置在packages/web/eslint.config.js扩展根本不会去那里看。解决办法是告诉扩展去哪里找{ eslint.workingDirectories: [ { pattern: packages/*/ }, { pattern: apps/*/ } ] }workingDirectories支持 glob扩展会在匹配到的每个目录分别启动一个 ESLint 进程。代价是内存占用上升包特别多的时候要留意。还有个更隐蔽的问题多个子包共用 workspace 根目录的node_modules插件解析路径可能出错。表现为某个子包报告找不到插件 xxx。这时候优先检查该子包是不是把插件声明在自己的package.json里——严格依赖结构的包管理器会要求显式声明。提示在 monorepo 里调试 ESLint 时先跑命令行的npx eslint --print-config path/to/file.vue它会打印出这个文件最终生效的完整配置。比对命令行和编辑器里的行为差异能快速定位到底是配置问题还是扩展问题。5. 实测流程从新项目到保存即修正前面讲的是各模块的原理和配置这一章把完整流程串起来包含我实际跑通的命令、参数取舍和验证方式。你可以当成一份操作清单照着走。5.1 完整落地步骤含可直接运行的命令按顺序做这几步第一步确认 Node 版本并锁定。node -v # 需要 18.18建议 20 LTS 以上 echo 20 .nvmrc第二步装依赖。npm i -D eslint eslint/js typescript-eslint eslint-plugin-vue globals npm i -D prettier eslint-config-prettier npm i -D husky lint-staged第三步生成 ESLint 配置骨架。npm init eslint/configlatest交互式问答会问你的项目类型JS/TS、框架、跑在浏览器还是 Node、要不要装依赖。选完之后它会生成一个eslint.config.js你再按第 3 章的内容把 Vue、Prettier 那几层补上。第四步加脚本。{ scripts: { lint: eslint ., lint:fix: eslint . --fix } }这里我特意分成两个脚本lint不带--fix用于流程里做检查lint:fix带--fix用于本地批量修。第五步配 VS Code。项目级.vscode/settings.json放规则相关设置个人settings.json放保存行为内容见第 4 章。第六步接 Git 钩子。npx husky init然后在.husky/pre-commit里写npx lint-stagedpackage.json里配{ lint-staged: { *.{js,ts,vue}: [eslint --fix, prettier --write] } }注意这里顺序是先 ESLint 后 Prettier符合质量在前、格式在后的逻辑。5.2 规则取舍与参数选择背后的计算过程配置里有几个不是拍脑袋定的数字我把取舍过程写出来方便你按自己项目调整。关于规则严格度。我一般把no-unused-vars设成warn而不是error。原因是写代码时中途经常有暂时没用的变量设成error会让整条流程红掉体验上不划算warn既能提示又不阻断等提交前再让它变error。可以用 ESLint 的--max-warnings 0在命令行阶段把警告也当失败处理实现本地宽松、流程严格。关于忽略参数。argsIgnorePattern: ^_意味着用下划线开头的参数不报未使用。这个约定在事件回调、接口占位参数里非常实用比如(req, _res, next) {}。关于带类型规则的开销。我做过简单测试一个 500 文件左右的中型项目开启带类型检查的规则后单次全量 lint 时间大概从 8 秒涨到 25 秒左右。这个量级在保存时是可接受的但如果你的项目有几千个文件就得考虑把带类型的规则限定在关键目录或者依赖编辑器的增量处理。关于files的范围。我见过有人把files写成**/*结果.css、.md甚至.json都被拉进来报一堆莫名其妙的错误。范围要精准只包含真正有代码的文件类型。5.3 实操现场记录怎么验证它真的生效了配完必须验证不能只看编辑器没红线就以为好了。我通常这样验证写一个故意违规的文件// test-lint.js var a 1; var b 2; if (a b) { console.log(相等); } const unused 我不会被用到;保存之后预期的结果是var被替换成const或let取决于你的规则、变成、多余的分号被处理、未使用的变量出现警告。如果保存后什么都没发生回到第 6 章排查。命令行验证npx eslint test-lint.js # 看规则报什么 npx eslint test-lint.js --fix # 看能修成什么 npx eslint --print-config test-lint.js | head -40 # 看最终生效配置一个我常用的对比技巧命令行--fix的结果和编辑器保存的结果应该一致。如果命令行能修而编辑器不能问题在扩展如果两边都不修问题在规则本身这条规则可能本来就不提供 fix。用这个二分法能省掉一半排查时间。6. 常见问题与排查技巧实录这一章是我踩坑的汇总。自动修正失效的原因其实就那么几类按顺序查基本都能定位。6.1 保存没反应按这个固定顺序排查我总结了一个从外到内的排查顺序别乱跳第一看状态栏的 ESLint 标识。编辑器右下角有个 ESLint 的小图标点开看它有没有报错。如果它显示未找到配置说明扩展根本没找到你的eslint.config.js问题在路径或useFlatConfig。第二看输出面板。输出面板里选ESLint频道扩展的报错、版本、解析到的配置文件路径都在这。这比猜快得多。第三确认文件类型在 validate 范围内。前面说过.vue不写进eslint.validate就不会被处理。第四确认source.fixAll.eslint的值。false、never都是关的explicit和always才是开的。第五检查是不是被忽略了。你的文件可能命中ignores或者所在目录被.eslintignore排除了Flat Config 时代一般用ignores字段但老配置可能还在用.eslintignore。第六确认没有和 Prettier 或其它格式化插件打架。如果保存后代码变了又变回去就是两个工具在抢着改同一个地方。6.2 常见问题速查表把高频问题整理成一张表遇到时对照着看现象常见原因处理方式保存完全没反应插件未启用 / 未找到配置看输出面板 ESLint 频道确认配置文件路径.js 能修.vue 不能未把 vue 加入 validateeslint.validate加vue报找不到插件 xxxmonorepo 依赖未提升在子包显式声明该插件保存后格式反复变化ESLint 与 Prettier 规则冲突配置eslint-config-prettier收尾TS 语法在 .vue 里报解析错误内层 parser 未指定parserOptions.parser tseslint.parserno-undef到处报globals 未配置languageOptions.globals补全环境变量保存时编辑器卡顿规则过多或全量 fix精简规则或改为 key 触发修改老代码时报一堆历史问题全量扫描无忽略配ignores或仅对变更文件检查命令行能修、编辑器不能扩展版本或工作目录问题检查eslint.workingDirectories装了插件但状态栏没图标该文件类型未被激活打开一个.js文件再看状态栏6.3 大项目里的性能与体验优化心得最后说几个让这套东西跑得舒服的技巧这些是配置文档里不会写、但实际用起来差别很大的。第一把保存时的修复拆成两档。我习惯用editor.codeActionsOnSave做保存修复同时给一个快捷键单独触发全量修复比如绑定eslint.executeAutofix大文件保存时如果觉得慢就临时关掉codeActionsOnSave里的 ESLint 项需要时手动触发。第二控制 lint 的触发时机。扩展有个eslint.run设置可以设onType或onSave。onType输入时就检查反馈快但耗电onSave只在保存时检查省资源但慢一拍。我在笔记本上用onSave台式机上用onType按场景切。第三别让 lint 去扫产物和第三方代码。ignores一定要写全node_modules、dist、coverage、*.min.js全部排除。这条能解决一半的卡死投诉。第四规则要渐进严格。接手一个历史项目时不要把推荐规则一次性全开那样会瞬间冒出几千条错误人会崩溃然后关掉 lint。正确做法是先用一个宽松的起始配置定一条新增代码必须过、存量代码逐步改的规矩每个迭代消化一部分。第五团队里统一插件版本。在.vscode/extensions.json里推荐插件再在文档里说明最低版本要求。编辑器扩展更新频繁新旧行为偶尔会有差异统一版本能省掉很多你那边行我这边不行的沟通成本。我自己在这些项目里有个体会自动修正的价值不在于省了多少手工改格式的时间而在于它把规范从一个需要人记住的规则变成了一个不需要记住的默认行为。新人根本不用读规范文档写代码的过程中就被拉着往对的格式走。前提是配置别写得太激进不然反弹比不配还严重——见过太多团队因为保存时全量报错最后集体把 lint 关了。所以每次我落地这套东西第一步永远是把规则收敛到确定没问题的那一小撮先让人尝到甜头再慢慢加码。另外分享一个我常用的小技巧如果你不确定某条规则到底该不该修先在命令行对单个文件跑一次--fix用git diff看一眼它到底改了什么觉得合理再放进保存流程。这个习惯帮我避开了好几次顺手修了不该修的代码的翻车。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →