Vite项目集成ESLint与Prettier:从配置到流程的完整实践
发布时间:2026/10/9 8:06:19 锦皓数字建站

接手一个 Vite 项目我第一件事不是看业务代码而是先翻有没有.eslintrc或者eslint.config.js再看有没有.prettierrc。代码能跑不代表能维护尤其是多人协作的项目单引号双引号混用、缩进 2 格 4 格并存、半行分号半行没分号这种东西一天能引发一百次无意义的 diff 冲突。去年我整理团队前端脚手架的时候把 ESLint 和 Prettier 完整接进了 Vite 流程从本地编辑器到 CI 构建一路卡规范省下的沟通成本远超预期。这篇文章就把这套集成方案从头到尾讲清楚适合刚用 Vite 建项目、想在第一天就把规范立起来的开发者也适合想理清 ESLint、Prettier 和 Vite 三者关系的朋友。1. 为什么要在 Vite 项目里集成 ESLint 和 Prettier1.1 先分清两个工具的分工很多人把 ESLint 和 Prettier 当成一回事其实这俩东西分工完全不同。ESLint 管的是代码对不对、有没有坏味道比如声明了没用到的变量、不小心用了、把console.log留在生产代码里它管。Prettier 管的是代码长得整不整齐比如用单引号还是双引号、缩进两格还是四格、一行写多少个字符就该换行它管。举个例子你就明白了ESLint 会因为if (a b)里用了宽松相等而报警告Prettier 却完全不关心还是它只会把a b按自己的风格整理成带空格的写法。反过来ESLint 默认不检查行宽是 80 还是 100Prettier 却会强行换行。所以这俩是互补关系谁也不能替代谁只装其中一个规范都是不完整的。1.2 集成以后的直接收益把这套组合装进 Vite 项目最明显的好处有三个。第一是风格统一。团队里有人习惯双引号有人习惯单引号有人喜欢写分号有人不喜欢。有了 Prettier格式化这件事完全交给机器谁写出来的代码保存之后都是一个样review 的时候不会再出现你这里为什么加分号这种毫无价值的讨论。第二是错误前置。ESLint 能在开发阶段就把未使用变量、隐式 any、重复声明这类问题揪出来而不是等代码合并之后出了问题再去查。Vite 本身启动快、热更新快配合编辑器保存即检查等于每条错误都在写出来的瞬间被提示修复成本最低。第三是自动化兜底。规范写在配置里而不是写在团队文档里。新同事入职拉下代码装好依赖编辑器配置一同步写出来的代码天然合规不需要师傅带徒弟式地口口相传。1.3 接入方案怎么选集成方式其实有三条路适用场景不太一样。第一种是插件方式把 ESLint 挂进 Vite 的插件体系里比如vite-plugin-eslint开发服务器启动时和文件热更新时自动跑 lint。这种方式对强制很有效但缺点也明显项目大了以后每次热更新都跑 lint 会有明显延迟而且它和 ESLint 9 的 flat config 适配一直有点别扭我后面会细说。第二种是命令方式在package.json里配lint和format脚本本地手动跑、CI 里跑、提交前用 hooks 跑。这种方式最灵活也是我目前的主力推荐因为 lint 不阻塞开发但在关键节点一个都逃不掉。第三种是编辑器方式靠 VS Code 的 ESLint 和 Prettier 扩展在保存文件时自动格式化、自动修复。这是日常开发体验的核心也是绝大多数团队成员真正感知到规范存在的地方。真实项目里这三条路不是三选一而是叠加的。我的标准组合是编辑器保存时处理 提交前 lint-staged 兜底 CI 里全量 lint 巡检。至于 vite-plugin-eslint小项目尝鲜可以大项目我劝你慎重。2. 环境准备与依赖安装2.1 版本坑ESLint 9 的配置体系变了先提醒一个最容易翻车的点。ESLint 从 9.0 开始把配置体系从传统的.eslintrc换成了 flat config也就是eslint.config.js。如果你在网上搜教程看到教你在项目里建.eslintrc.cjs然后extends一堆配置的那大概率是 ESLint 8 时代的老写法在 ESLint 9 下直接跑不起来会报类似ESLint couldnt find an eslint.config.(js|mjs|cjs) file的错误。现在用npm create vite建出来的项目模板如果勾选了 ESLint 选项生成的就是 flat config 风格。所以我建议新项目统一走eslint.config.js。老项目还没升到 ESLint 9 的可以暂时用.eslintrc但心里要清楚这只是过渡状态。另外版本兼容性也要注意Vite 5 要求 Node 18Vite 7 要求 Node 20.19ESLint 9 要求 Node 18.18装依赖之前先用node -v看一眼免得装完一堆依赖跑起来全是版本报错。2.2 依赖安装一步步来我这里以 Vue 3 TypeScript 的 Vite 项目为例这也是目前最多人用的组合。先建项目npm create vitelatest my-project -- --template vue-ts cd my-project npm install然后安装规范相关的开发依赖npm install -D eslint prettier eslint-config-prettier globals npm install -D typescript-eslint eslint-plugin-vue这些包干什么用的我逐个说清楚。eslint和prettier是本体没得说。typescript-eslint这个包很有意思它把typescript-eslint/parser和typescript-eslint/eslint-plugin打包在一起还提供了ts.config()之类的好用工具函数TS 项目直接装它一个就够了。eslint-plugin-vue是 Vue 官方推荐的 ESLint 插件提供template的解析和各种 Vue 特定规则。eslint-config-prettier是冲突消解器作用是把 ESLint 里所有和 Prettier 冲突的格式类规则全部关掉这个必须装不装你后面会很痛苦。globals用来声明浏览器环境里的全局变量flat config 下没有现成的env.browser了这个包的用途后面会提到。如果你不写 Vue把eslint-plugin-vue去掉就行如果项目是纯 JavaScripttypescript-eslint也可以去掉。React 项目的话把eslint-plugin-vue换成eslint-plugin-react和eslint-plugin-react-hooks思路完全一样。2.3 在 package.json 里加 scripts装完依赖先把package.json里的 scripts 配好。我习惯这样配{ scripts: { lint: eslint ., lint:fix: eslint . --fix, format: prettier --write ., format:check: prettier --check . } }lint负责检查lint:fix会自动修复所有可以自动修复的问题比如去掉未使用的导入、自动补上分号如果规则要求。format是批量格式化所有文件format:check只检查不修改这个适合放到 CI 里当门禁。注意eslint .后面的点号意思是检查当前目录下所有文件具体哪些文件被排除靠配置文件里的ignores控制。3. ESLint 实战配置3.1 两种配置体系建议选新不选旧ESLint 9 的 flat config 和传统.eslintrc最大的区别是你不再需要依赖extends字符串去隐式加载配置而是把所有配置写成一个数组数组里每个元素就是一个配置块用files声明这个配置块作用于哪些文件用ignores声明忽略哪些文件。好处是整个配置完全显式、可读、好追溯坏处是网上大量老教程直接失效你搜到的东西要自己分辨新旧。我给一个最简单的判断方法看到.eslintrc、extends、env、parserOptions、plugins这些字段是老写法看到eslint.config.js、defineConfig、files、languageOptions、ignores是新写法。3.2 Vue3 TS Vite 的完整配置示例下面这份配置是我目前在用的模板直接复制到项目根目录命名eslint.config.jsimport js from eslint/js import ts from typescript-eslint import vue from eslint-plugin-vue import globals from globals import prettier from eslint-config-prettier export default ts.config( { ignores: [dist, node_modules, public, coverage, *.d.ts] }, js.configs.recommended, ...ts.configs.recommended, ...vue.configs[flat/recommended], { files: [**/*.{js,mjs,cjs,ts,vue}], languageOptions: { ecmaVersion: latest, sourceType: module, globals: { ...globals.browser }, parserOptions: { parser: ts.parser } }, rules: { no-console: warn, no-debugger: warn, vue/multi-word-component-names: off, typescript-eslint/no-explicit-any: off } }, prettier )逐个解释。ts.config()是 typescript-eslint 包提供的辅助函数比裸写数组更省心它会自动帮你处理好 TypeScript 相关的配置合并。js.configs.recommended是 ESLint 官方推荐的 JavaScript 规则集相当于老写法里eslint:recommended。...ts.configs.recommended是 TypeScript 推荐规则集展开成多个配置块。...vue.configs[flat/recommended]是 Vue 插件对 flat config 提供的推荐规则注意它和传统写法里的plugin:vue/vue3-recommended不是一个名字别搞混。最后那个prettier就是eslint-config-prettier的默认导出把它放在数组最后一位作用是把所有和 Prettier 冲突的 ESLint 格式规则关掉。这个顺序很重要如果放早了后面可能又有规则把它覆盖回去。3.3 三条最值得调的规则配置里我做了几个实际项目里最常见的调整。vue/multi-word-component-names这个规则是 Vue 插件推荐规则集里比较烦人的一条它要求组件文件名必须是多个单词比如UserCard.vue可以Home.vue就不行。新项目倒是可以遵守但老项目改起来成本太高我选择直接关掉。typescript-eslint/no-explicit-any默认是 warn 还是 error 取决于推荐规则集的配置很多团队会直接禁掉任何显式any。我的态度是any本身不是罪滥用才是。前期用any保证开发速度后期逐步收紧比一开始就逼着所有人写复杂泛型要实际得多。所以我把它关掉团队里如果出现明显滥用靠 review 解决而不是靠规则一刀切。no-console和no-debugger我配成了warn而不是error。开发阶段 console 太常用了如果设成 error 你会被逼着一直注释代码反而影响效率。设成 warn 能在不阻断开发的情况下保持提醒等 CI 阶段再加严格环境变量来放行或拦截这才是合理的分层。还有一个很多新手会踩的坑flat config 里没有env了浏览器全局变量window、document、localStorage这些不会自动声明而js.configs.recommended里恰好有一条no-undef规则结果就是你明明在使用浏览器 APIESLint 却报变量未定义。解决办法就是上面配置里的globals: { ...globals.browser }这也是我为什么让你装globals这个包。4. Prettier 配置与冲突化解4.1 配置项逐条解读Prettier 的配置文件叫.prettierrc或者.prettierrc.json放在项目根目录。我用的配置长这样{ semi: false, singleQuote: true, printWidth: 100, tabWidth: 2, trailingComma: es5, arrowParens: always, endOfLine: lf, bracketSameLine: true, vueIndentScriptAndStyle: false }每个选项什么意思我给你讲清楚不然你没法根据团队习惯去改。semi控制是否加分号。false就是不加这是现在前端社区的主流审美但如果你团队都是 Java 背景可能更习惯true。singleQuote控制字符串用单引号还是双引号true用单引号。这两个是最容易引起团队争论的选项定下来就别三天两头改改一次整个仓库的 git 历史就要翻一次天。printWidth是行宽默认 80我调成 100。80 是给 GitHub 网页比较宽裕的阅读宽度但现代开发基本都是大显示器配分栏编辑器100 到 120 能显著减少自动换行代码读起来更连贯。trailingComma默认是es5意思是在对象和数组的末尾加逗号但函数参数不加。es5是个比较平衡的选择全加的话在某些老旧环境会有问题none不加的话加了行又会有多余 diff。arrowParens控制箭头函数参数是否加括号。always就是哪怕只有一个参数也加括号x x会变成(x) x。我建议保持always因为以后加参数时 git diff 更干净不会出现本来加括号又拆括号的噪音。endOfLine设成lf这是跨平台最关键的一个配置后面专门讲。4.2 eslint-config-prettier 为什么必须放最后现在讲冲突问题。ESLint 里有一批规则天生和 Prettier 打架比如quotes、semi、indent、comma-dangle它们都在管格式。你如果同时开着 ESLint 的这些格式规则和 Prettier编辑器自动修复时就会上演ESLint 把缩进改成 4 格Prettier 又改回 2 格的拉锯战保存一次文件两边的自动修复各执行一次结果还是报错非常折磨。eslint-config-prettier做的事情就是把这批冲突规则全部关掉让你在使用 React/Vue/TS 推荐规则集的时候格式部分以 Prettier 为准。所以它的位置必须在配置数组的最后或者老写法的extends数组的最后一位确保前面所有规则集里格式类规则都被它覆盖掉。还有一个相关选择要不要用eslint-plugin-prettier。这个插件会把 Prettier 当成一条 ESLint 规则来跑好处是 lint 和 format 统一成一条命令坏处是性能明显下降因为 ESLint 会对每个文件额外做一次格式化计算而且会把格式不对当成 lint error在 CI 里很吵。我个人的建议是新项目不用它保持ESLint 管逻辑、Prettier 管排版的清晰分工格式化交给编辑器或prettier --writeESLint 负责真正有意义的错误检查。4.3 忽略文件与 endOfLine 的 Windows 坑.prettierignore和.eslintignore同样重要。Prettier 的忽略文件我一般这样写dist node_modules public coverage package-lock.json pnpm-lock.yaml *.min.js忽略掉生成文件、锁文件和压缩文件一方面减少格式化消耗另一方面这些文件本来就不该被格式化否则每次构建出来格式不同git 会一直标记改动。ESLint 9 的扁平配置里不再单独要求.eslintignore直接在eslint.config.js里写ignores就行我前面配置里已经写了。接下来是 Windows 用户的经典痛点。有一天你会在 ESLint 或者 Prettier 的输出里看到Delete ␍或者Expected linebreaks to be LF but found CRLF这样的报错。原因是 Windows 下编辑器默认用 CRLF 换行而统一标准是 LF。解决办法就是我配置里的endOfLine: lf同时建议在项目根目录加一份.editorconfigroot true [*] charset utf-8 indent_style space indent_size 2 end_of_line lf insert_final_newline true trim_trailing_whitespace true.editorconfig的好处是它不依赖任何人装了 Prettier 插件只要 VS Code 装了 EditorConfig for VS Code 扩展打开文件就直接按这个规范走。配合 Prettier 的endOfLine: lf基本能根治换行符问题。5. 把检查接入日常流程5.1 在 Vite 里跑 lintvite-plugin-eslint 与替代方案说到集成很多人第一反应是把 lint 塞进 Vite 的插件机制里也就是用vite-plugin-eslint。它的用法很简单先装依赖然后在vite.config.ts里这样配import { defineConfig } from vite import vue from vitejs/plugin-vue import eslint from vite-plugin-eslint export default defineConfig({ plugins: [ vue(), eslint({ include: [src/**/*.{js,ts,vue}], lintOnStart: true, cache: true }) ] })但我要给你一个负责任的大实话这个小插件我实际用下来问题不少。一个是性能项目文件一多每次热更新都要触发一次 lint开发的时候能明显感觉到卡顿另一个是兼容性它有一段时间没好好更新了和 ESLint 9 的 flat config 适配很不稳我遇到过配置了eslint.config.js但插件完全不生效的情况。所以现在给我的话我不会在新项目里默认上这个插件更推荐下面这套组合。日常开发靠编辑器扩展即时提示这是主力体验提交代码前靠 lint-staged 只检查暂存区文件这是第二道闸门CI 里跑npm run lint做全量检查这是最终兜底。这套方案不依赖任何 Vite 插件不受 ESLint 版本升级影响而且开发时 lint 完全不占用构建性能。如果你确实想在浏览器里看到 lint 报错的浮层提示可以关注一下vite-plugin-checker它把 ESLint 和 vue-tsc 的检查整合进了 Vite体验接近编辑器叠加提示不过它的配置复杂度要高一些适合确实有团队强制需求的场景。5.2 VS Code 保存时自动格式化与自动修复编辑器配置这块是团队成员无感合规的关键。先装两个扩展ESLintdbaeumer.vscode-eslint和 Prettier - Code formatteresbenp.prettier-vscode。然后在项目根目录建一个.vscode/settings.json这样配置会跟着仓库走不用每个同事手动改编辑器设置{ editor.formatOnSave: true, editor.defaultFormatter: esbenp.prettier-vscode, editor.codeActionsOnSave: { source.fixAll.eslint: explicit }, [javascript]: { editor.defaultFormatter: esbenp.prettier-vscode }, [typescript]: { editor.defaultFormatter: esbenp.prettier-vscode }, [vue]: { editor.defaultFormatter: esbenp.prettier-vscode }, eslint.validate: [javascript, typescript, vue, html] }这里有个细节值得说一下。source.fixAll.eslint老版本写法是true但新版本 VS Code 推荐用explicit意思是只有显式触发时才执行避免和 formatOnSave 打架。如果你发现保存时 ESLint 和 Prettier 互相覆盖修改先把codeActionsOnSave改成explicit试试大多数情况都是因为true的隐式触发导致两边同时运行。配置好之后写代码时看到黄色波浪线就知道有 lint 问题保存文件自动格式化团队所有成员体验一致。这才是集成得润物细无声的状态。5.3 提交前兜底lint-staged最后一道闸门是提交前检查。直接在pre-commit钩子里跑全量npm run lint的话项目大了之后每次提交都要等几十秒很容易被同事吐槽然后偷偷跳过。正确做法是只检查本次提交暂存区里的文件这就是lint-staged的用途。装两个包npm install -D lint-staged husky配好lint-staged我这里给出一个package.json里的配置片段{ lint-staged: { *.{js,ts,vue}: [eslint --fix, prettier --write] } }再初始化 husky 的 pre-commit 钩子npx husky init echo npx lint-staged .husky/pre-commit这样提交的时候暂存区里的 JS/TS/Vue 文件会先被 ESLint 修复一遍再被 Prettier 格式化一遍如果还有修不了的错误提交会被打断。注意这里顺序不能反先 ESLint 后 Prettier因为 ESLint 修复可能会移动代码Prettier 再统一排版最终落盘的是稳定版本。这套做下来规范的强制力基本就闭环了编辑器保存时解决 80% 问题提交钩子挡住 15%CI 巡检兜住剩下 5%。6. 常见问题与排查技巧实录6.1 高频问题速查表这些是我在各类项目里反复遇到、也帮同事排查过多次的问题先给个速查表。现象大概率原因解决办法报错ESLint couldnt find an eslint.config.(js|mjs|cjs) fileESLint 9 找不到 flat config项目根目录创建eslint.config.js.vue文件报 Parsing error没有接入 eslint-plugin-vue 或 parser 没配好安装并配置eslint-plugin-vue确认flat/recommended已引入window、document报 no-undefflat config 下没有声明浏览器全局变量安装globals在languageOptions.globals里展开globals.browserESLint 和 Prettier 无限互相覆盖没装eslint-config-prettier或没放在最后安装并确保它在配置数组最后一位Windows 下报Delete ␍换行符 CRLF 与 LF 冲突Prettier 配endOfLine: lf补.editorconfig保存时 lint 修复不触发codeActionsOnSave配置写法不对把source.fixAll.eslint配成explicitvite-plugin-eslint 不生效和 ESLint 9 flat config 兼容问题改用 script 编辑器 lint-staged 方案6.2 三个印象深刻的排查案例第一个案例是no-undef的坑。有个同事的纯 JS 项目所有文件里window、localStorage全部画红线他以为是规则太严格准备全局屏蔽 no-undef。实际上问题很简单flat config 没有env.browser那样的环境声明了必须显式补全局变量。我帮他把globals.browser展开进去红线瞬间消失。这个问题的隐蔽性在于它对 TypeScript 项目影响没那么大因为 TS 自己会做类型检查很多 TS 项目没配浏览器 globals 也能跑但纯 JS 项目必炸。第二个案例是格式化分歧。之前有个项目同时装了eslint-config-prettier和eslint-plugin-prettier结果 ESLint 输出里全是prettier/prettier报错而编辑器的 Prettier 扩展又觉得代码是对的两边说法不一致搞得同事很崩溃。排查下来的结论是这个项目把 prettier 规则重复挂在了 ESLint 里同时编辑器又在格式化等于两套 Prettier 在抢同一个文件。后来我们把eslint-plugin-prettier从配置里卸掉只保留eslint-config-prettier做规则冲突消解编辑器负责格式化世界清净了。这个案例给我的教训是集成不是越多越好工具职责边界要清晰。第三个案例是 lint-staged 的路径问题。有一次配完 husky 钩子提交时报错说找不到要检查的文件。我检查了半天发现是 lint-staged 配置里 glob 模式写成了src/**/*.{js,ts,vue}而同事当时改的是vite.config.ts文件不在src下所以匹配不到任何文件钩子相当于空跑。后来我把模式改成*.{js,ts,vue}并额外加了一条*.{json,md}: [prettier --write]把配置文件也纳入格式化范围才真正把入口都守住。配置 lint-staged 的时候一定要确认 glob 模式覆盖到了你项目里所有需要检查的文件位置。还有一个前端工程化里相对隐蔽的问题ESLint 9 的 flat config 不支持直接使用传统plugin:xxx/recommended这种字符串插件引用必须显式导入插件对象。我见过有人把 ESLint 8 项目的配置原封不动搬到 ESLint 9结果各种Failed to load plugin报错。新版配置虽然更啰嗦但每一步都是显式的出了问题反而好排查。如果你看到网上教程里extends后面跟一堆字符串先确认教程时间再决定要不要照抄。我个人在实际操作里的体会是工具链越早接入成本越低。项目刚初始化时配一次规范后面所有代码自动合规等项目写了几万行再回头补规范改格式的 diff 能把 git 历史搅得没法看。所以如果你正在搭一个新的 Vite 项目哪怕业务还没写一行先把 ESLint 和 Prettier 这套地基打好后面省的事远比你现在多花的半小时多得多。配置细节上我给的建议是规则集用推荐配置起步只做少数几个符合团队习惯的调整别一上来就自定义几十条规则规范是慢慢沉淀出来的不是一步到位的。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。