接手一个Vite新项目,第一件事不是写业务,而是把代码规范立起来。前些天我刚好给团队的Vite项目集成了一套ESLint和Prettier,从安装依赖到VSCode保存自动格式化,再到git提交前拦截,整个过程踩了不少坑。这篇就把完整链路和关键细节拆开讲清楚,照着配就能从零得到一个“代码风格统一、明显错误早起被揪出”的开发环境。适合刚用Vite、想规范代码但不知道怎么下手的同学,也适合已经配过但总被各种配置折腾到头疼的老手。
1. 为什么Vite项目要单独集成ESLint和Prettier
1.1 代码规范不是“强迫症”,是效率工具
刚开始写前端的时候,我也觉得ESLint报一堆warning很烦人,Prettier把双引号改成单引号也像没事找事。直到参与多人协作的项目,看到同一个文件里有人用4空格、有人用Tab,有人结尾写分号、有人不写,git diff一打开全是格式改动,真正逻辑变更被淹没在几十行的空白变更里,才意识到规范的价值。
代码规范的核心作用有两个维度:一个是可读性,团队里任何人打开代码都能快速理解结构;另一个是可维护性,减少无意义的格式差异,让评审者一眼看到实质改动。而Vite作为目前主流的构建工具,只负责起服务和打包,它本身不包含任何lint能力,所以项目创建之后,ESLint和Prettier需要自己动手接上去。
1.2 ESLint管“对不对”,Prettier管“好不好看”
很多人把这两个工具混为一谈,其实分工非常清晰:
- ESLint关注代码质量和潜在错误,比如变量定义了没使用、引用了一个不存在的依赖、强制使用无副作用表达式等。
- Prettier关注代码格式,比如字符串用单引号还是双引号、语句末尾是否加分号、缩进多少空格、换行宽度是多少。
打个比方:ESLint是审查员,检查有没有违章;Prettier是装修师,统一墙面颜色和瓷砖规格。审查员不管墙刷什么颜色,装修师不管承重墙有没有裂缝,两者配合才能交出一套安全又美观的房子。
1.3 为什么不在Vite里直接全员统一
有些框架会在脚手架里内置lint规则,Vite刻意保持轻量,让开发者自己选择。这带来的好处是灵活性极高,React、Vue或原生TS项目都能自定义规则。坏处是好多人不知道从哪儿接入,或者装上之后ESLint和Prettier互相打架,反而更乱。这篇文章就是把这条路走通,让后来者少踩我踩过的坑。
2. 集成前的准备工作与依赖安装
2.1 环境版本确认
开始之前,先确认你的Node环境。当前Vite 5、Vite 6都要求Node.js 18+,建议使用长期支持版本(20或22)。ESLint 9以后配置文件格式变成了“扁平配置”(Flat Config),而社区里很多老文章还在用.eslintrc.js,版本不同会导致配置格式对不上,这是新手最容易迷茫的地方。
我的实操环境是:
| 工具 | 版本 |
|---|---|
| Node.js | 20.x |
| Vite | 5.x |
| Vue | 3.x |
| ESLint | 9.x |
| Prettier | 3.x |
2.2 安装依赖有哪些以及为什么
在项目根目录执行:
npm install -D eslint prettier eslint-plugin-vue eslint-config-prettier这里逐项拆一下:
- eslint:核心lint引擎。
- prettier:代码格式化核心库。
- eslint-plugin-vue:用于在Vue单文件组件(SFC)里识别
<template>、<script>、<style>块,并应用规则。如果项目是纯React,可换成eslint-plugin-react;如果是原生JS/TS,这个插件不需要装。 - eslint-config-prettier:关掉ESLint中那些与Prettier冲突的格式类规则,比如
indent、quotes等。如果不装这个,你会发现ESLint和Prettier同时管格式化,当两人要求不一致时就会报一堆矛盾错误。
注意:很多人提到需要
@vue/eslint-config-prettier,那是在走Vue官方lint封装时的搭配。这里我们走的是直接组合插件的方式,所以装标准版eslint-config-prettier即可。你也可以统一用@eslint/js和typescript-eslint,后面会讲TS的补充方案。
3. 一步一步配置ESLint(以Vue 3项目为例)
3.1 创建扁平配置文件eslint.config.js
ESLint 9默认识别eslint.config.js作为配置文件。在项目根目录创建该文件,我们一步步拆开写,便于理解。
// eslint.config.js import js from '@eslint/js' import pluginVue from 'eslint-plugin-vue' import prettierConfig from 'eslint-config-prettier' export default [ // 继承js官方推荐规则 js.configs.recommended, // Vue3 推荐的规则集 ...pluginVue.configs['flat/recommended'], // 自定义规则 { files: ['**/*.{js,mjs,cjs,vue}'], languageOptions: { ecmaVersion: 'latest', sourceType: 'module', globals: { // 浏览器全局对象 window: 'readonly', document: 'readonly', console: 'readonly', // Vite暴露的import.meta.env importMeta: 'readonly' } }, rules: { 'no-unused-vars': 'warn', 'no-console': 'warn', 'vue/multi-word-component-names': 'off' } }, // 关闭与Prettier冲突的ESLint规则 prettierConfig, ]如果你没有安装@eslint/js,js.configs.recommended会找不到包。通常安装eslint时自带了@eslint/js,但为了明确还是在项目里装上:
npm install -D @eslint/js3.2 几个关键点的说明
为什么用pluginVue.configs['flat/recommended']?
eslint-plugin-vue从9.x版本开始提供扁平配置入口,里面已经包含解析器vue-eslint-parser,能正确处理.vue文件。如果你用老的extends: ['plugin:vue/vue3-recommended'],在Flat Config下是不识别的,这就是很多人配完发现ESLint根本不检查Vue文件的原因。
globals里为什么要写window和document?
扁平配置默认不会给ESLint注入环境全局变量。在浏览器端代码中使用window、document,如果不声明,ESLint会报“未定义”错误。这里我直接列为只读全局变量。对于import.meta.env,需要用importMeta这一项来声明,否则import.meta.env也会报错。
关于vue/multi-word-component-names
Vue官方推荐组件名使用多个单词,避免和原生HTML元素冲突,比如HomePage而不是Home。但对内部业务组件来说,经常会有Header.vue、Footer.vue这种单名单文件,严格模式下会报错。我选择关闭这个规则,你也可以保留推荐规则,看团队取舍。
3.3 补充TypeScript支持(可选)
如果Vite项目用TS,只需再加两步:
npm install -D typescript typescript-eslint然后在eslint.config.js里:
import tseslint from 'typescript-eslint' // 在export default数组中加入 tseslint.configs.recommended这样ESLint就能正确解析.ts文件,并给出TS相关的类型相关建议规则。特别注意:Vue文件里的<script lang="ts">,也需要typescript-eslint提供解析能力,否则会出现“无法解析类型”的异常报告。
3.4 在package.json里加入lint脚本
{ "scripts": { "lint": "eslint . --max-warnings=0", "lint:fix": "eslint . --fix" } }eslint .检查当前目录下所有符合.eslintignore规则以外的文件。--max-warnings=0表示只要有一个warning就直接以非0退出码结束,适合CI环境强制通过。开发时也可以去掉这个参数,只留warning不阻塞。--fix让ESLint自动修复能修复的问题,比如删除未使用的导入、补分号等。
执行npm run lint,如果之前没跑过,Vue项目大概率会报出一堆“组件名应为多单词”、“存在未使用变量”等问题。按规则修完即可。
3.5 配置忽略项.eslintignore
在项目根目录创建.eslintignore:
dist node_modules public *.min.jsESLint默认忽略node_modules,但dist和public下如果有第三方压缩代码,最好也明确忽略,避免做无意义的检查。对于扁平配置,也可以直接在eslint.config.js里加一个ignores配置块:
{ ignores: ['dist/**', 'node_modules/**', 'public/**'] }两种方式等价,二选一即可。我更喜欢用独立文件,简单直观,团队里非前端也能看懂你要忽略什么。
4. Prettier配置与ESLint冲突处理
4.1 创建.prettierrc.json并解释核心配置项
Prettier通过配置文件读取格式化选项,常见的.prettierrc.json长这样:
{ "printWidth": 80, "tabWidth": 2, "semi": false, "singleQuote": true, "trailingComma": "none", "endOfLine": "auto", "arrowParens": "always", "htmlWhitespaceSensitivity": "ignore" }逐项拆解这些配置,你就知道为什么这些是最常用的一组:
- printWidth:每行代码的最大宽度,超过后Prettier会尝试换行。80是社区最保守的选择,适配大多数屏幕并减少横向滚动。也可以设100,看个人习惯。
- tabWidth:每个缩进级别对应几个空格。Vite默认2空格缩进,所以这里设2。
- semi:是否在语句末尾加分号。
false表示不加分号。我偏向不加,因为JavaScript具有自动分号插入机制,现代开发中分号更多是装饰。 - singleQuote:是否使用单引号。
true表示优先单引号,避免在字符串中需要转义双引号的情况。 - trailingComma:多行结构是否添加尾逗号。
none表示不加,比如对象字面量最后一行不写逗号。有些团队会用es5,即只在数组、对象等合法位置添加。注意:在函数参数列表加尾逗号需要浏览器支持ES2017,如果不确定目标环境,用none最安全。 - endOfLine:行尾结束符。
auto让Prettier跟随当前操作系统,Windows上是CRLF,macOS/Linux上是LF,避免跨平台把每行都标记为修改。但如果你使用git并且团队都在macOS上,可以固定为lf。 - arrowParens:箭头函数参数的括号。
always表示单个参数也加括号,avoid表示单个参数不加括号。Vue和React官方风格不同,我习惯always,和TypeScript配合更好。 - htmlWhitespaceSensitivity:影响Vue模板中HTML的空白敏感度。
ignore让Prettier对模板里的多个空格更宽容,避免出现“明明没改内容,一格式化整段模板就变更”的情况。
4.2 配置format脚本
package.json里加入:
{ "scripts": { "format": "prettier --write .", "format:check": "prettier --check ." } }prettier --write .:递归格式化当前目录下所有Prettier可识别的文件,自动覆盖写入。prettier --check .:只检查格式是否符合配置,不做修改。这个命令可以放到CI里,用来检查格式。
4.3 用eslint-config-prettier消除冲突
ESLint和Prettier都通过规则去检查代码风格,比如ESLint的quotes规则指定字符串使用单引号或双引号,Prettier的singleQuote也指定要使用哪种引号。如果两者设置了不同的偏好,ESLint会在lint阶段认为Prettier格式化后的代码违反了规则。
我之前遇到过最典型的场景:配置了ESLint强制“必须使用双引号”,同时Prettier配置为singleQuote: true,编辑器保存时Prettier把双引号改成单引号,紧接着ESLint的红色波浪线就亮起来,提示应使用双引号。这就是典型的“两个工具在打架”。
解决办法是在eslint.config.js数组最后加入一个eslint-config-prettier模块。它做的事情非常纯粹:把ESLint里所有与“格式”相关的规则全部关闭,只保留错误检测类规则。这样ESLint不再关心引号、缩进、分号,这些交给Prettier统一治理。
需要注意的一个点是,如果还用了eslint-plugin-prettier,那是另一种集成方式——让Prettier作为ESLint的规则插件运行,可以做到eslint --fix时顺便格式化。不过这种方案会让ESLint变慢,因为在ESLint内部又跑了一遍完整的Prettier。我更推荐“职责分离”的方式,ESLint管错误,Prettier管格式,各自独立执行,性能更好,也更容易排查问题。
4.4 配置.prettierignore
和ESLint一样,Prettier需要忽略一些不需要格式化的文件:
dist node_modules package-lock.json pnpm-lock.yaml yarn.lock public特别是package-lock.json,每次依赖安装后它会自动产生大量变化,你永远不应该用手动格式化它。
5. 与VSCode联动:保存即格式化
5.1 安装两个必要的VSCode扩展
编辑器侧需要两个插件:
- ESLint:让编辑器实时显示linter错误,并支持
F1 -> Fix all auto-fixable Problems快速修复。 - Prettier - Code formatter:提供代码格式化能力,可以在保存、粘贴或主动执行格式化时使用。
安装完成后,打开一个Vue文件,右键——如果项目里有多个格式化器时会提示选择,此时一定要选择Prettier作为默认格式化器。
5.2 修改用户设置或工作区设置
在项目根目录下创建.vscode/settings.json,和团队成员共享配置:
{ "editor.defaultFormatter": "esbenp.prettier-vscode", "editor.formatOnSave": true, "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit" }, "eslint.validate": [ "javascript", "typescript", "vue", "html" ], "prettier.printWidth": 100, "prettier.semi": false, "prettier.singleQuote": true }逐项说明:
- editor.defaultFormatter:编辑器默认格式化器设为Prettier,避免双格式化器弹窗或自动用了错误Formatter。
- editor.formatOnSave:保存时自动调用默认格式化器(即Prettier)。
- editor.codeActionsOnSave:保存时自动执行ESLint的自动修复动作,比如导入排序、未使用变量移除等。注意旧版本的VSCode用
true,新版VSCode建议用explicit,两者都能触发。如果发现保存时ESLint没有自动修复,检查这里的值和VSCode版本是否匹配。 - eslint.validate:告诉ESLint插件针对哪些语言类型进行实时校验。Vue和HTML需要显式列出。新版ESLint插件会自动根据配置文件识别,但加上更稳妥。
- 底下几个
prettier.*配置项:因为项目里有.prettierrc.json,VSCode的Prettier插件会优先读取项目配置,所以settings.json里的这些其实会被覆盖。写出来是给一些没读项目配置的情况兜底。
重要提醒:
editor.formatOnSave对Vue单文件组件中整个文件生效。如果你只想格式化代码块,不想动模板里的缩进,需要额外配置prettier.documentSelectors或按文件类型区分,但大多数情况下全文件格式化是符合预期的。
5.3 解决保存时“格式化器冲突”弹窗
很多同学第一次安装两个插件后,保存时会弹出“There are multiple formatters for this file type”或者右下角提示“Press F1 to resolve (currently: Prettier)”。这是因为除了Prettier外,VSCode内置的TypeScript/JavaScript language features也会提供格式化器。在settings.json里明确了editor.defaultFormatter为Prettier后,这个弹窗基本就消失了。
另一种常见冲突是Vue老项目里装了Vetur插件,它也会接管.vue文件的格式化。Vetur和Prettier同时启用时,格式化结果会乱套。我的建议是Vue 3 + Vite环境下直接禁用Vetur,改用官方推荐的Volar(Vue - Official)。Volar对Vue 3的语法解析更准确,而且不会争抢默认格式化器。
5.4 实际操作验证
配置完成后,把一个文件写乱,比如不写分号、用双引号、故意多缩进几格,然后保存。肉眼可见的变化是:引号变成单引号,缩进统一到2空格,语句后面不会自动加分号(如果semi: false)。同时,如果代码中有ESLint能自动修复的问题,比如import { foo } from 'bar',其中foo没被使用,保存后ESLint会自动把它从import列表里删掉,这就是source.fixAll.eslint的效果。
整个过程最爽的一点是:几乎不用手动处理风格问题,写代码时大脑权重放在逻辑上,风格交给机器。
6. 常见问题与排查技巧实录
6.1 问题和排查速查表
我在实际集成过程中遇到过以下高频问题,整理成表格,方便你对症下药。
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
eslint命令报“TypeError: this.libOptions.parse is not a function” | ESLint 9的扁平配置中使用了旧版解析器 | 卸载@typescript-eslint/parser旧版,升级到typescript-eslint新版统一管理 |
| Vue文件没有被ESLint检查 | 没有配置eslint-plugin-vue,或仍使用.eslintrc老格式 | 在eslint.config.js中加入pluginVue.configs['flat/recommended'] |
| 保存文件后所有引号变成双引号,但我想用单引号 | 项目缺少.prettierrc.json,VSCode默认使用双引号 | 在项目根目录创建.prettierrc.json并设置"singleQuote": true |
| 保存时会同时触发Prettier和ESLint修复,且互相冲突 | 没装eslint-config-prettier | 安装并把它加入ESLint配置数组 |
import.meta.env提示未定义 | 扁平配置中没有声明importMeta全局变量 | 在languageOptions.globals中加入importMeta: 'readonly' |
window、document提示未定义 | 没有声明浏览器全局变量 | 在languageOptions.globals中加入window: 'readonly'等 |
| Prettier格式化之后ESLint还是提示缩进错误 | ESLint和Prettier重复管理缩进 | 确保eslint-config-prettier是配置数组最后一个元素,才能覆盖所有冲突规则 |
打开.vue文件后编辑器很卡 | 可能装了Vetur和其他Vue插件冲突 | 禁用Vetur,只保留Volar |
| git提交时格式违规的代码溜进了仓库 | 未做提交前拦截 | 引入husky和lint-staged,下面讲 |
6.2 强烈建议补充:husky + lint-staged
既然已经集成了ESLint和Prettier,只靠编辑器“保存时格式化”还不够,因为不是每个队友都会正确设置VSCode。为了守住仓库的最后一道门,一定要在git提交前跑一遍lint和format检查。
安装:
npm install -D husky lint-staged初始化husky:
npx husky init这个命令会在.husky/目录下创建pre-commit文件,然后修改它:
npx lint-staged在package.json里配置lint-staged的作用对象和命令:
{ "lint-staged": { "*.{js,mjs,cjs,ts,vue}": [ "eslint --fix", "prettier --write" ], "*.{json,md,css,scss,html}": [ "prettier --write" ] } }这样每次提交时,只会对暂存区内的文件运行检查,改谁查谁,而不是全项目扫描,速度快很多。执行顺序上,先让ESLint修复能修复的问题(它还会把一些不能用Prettier处理的问题报出来),然后Prettier美化格式。如果有无法自动修复的错误,eslint --fix会以非0退出,提交直接失败,你把错误修掉再提。
6.3 一个容易忽略的坑:git行尾符导致Prettier检查失败
团队中如果有Windows和macOS/Linux混用,endOfLine配置不当会导致每行都被视为已修改。我们团队曾遇到一个经典场景:A用Windows提交了CRLF行尾,B用macOS检出后git diff显示整个文件被改。虽然没让提交失败,但很烦人。
推荐在项目根目录添加.gitattributes:
* text=auto *.js text eol=lf *.ts text eol=lf *.vue text eol=lf *.json text eol=lf同时.prettierrc.json里设置endOfLine: "lf",这样所有开发者都用LF换行符,彻底消除行尾噪音。如果你已经有一堆CRLF文件,提交前先跑一次prettier --write .统一转换。
6.4 配置文件到底该选.eslintrc还是eslint.config.js
现在网络上搜ESLint配置,新旧两种格式并存,非常容易绕晕。我的态度是:新项目一律使用Flat Config(eslint.config.js),老项目可以继续用.eslintrc,但要意识到ESLint 9默认用扁平配置,用--eslintrc标志才能兼容旧配置。
如果你从旧配置迁移到新配置,不要只是把module.exports改成数组,需要理解结构变化:旧配置的env、extends、plugins、rules分散在不同层级,新配置里则是通过配置对象数组叠加。迁移过程中最常遇到的问题是“解析器重复定义”,比如既在parser里写了vue-eslint-parser,又在languageOptions.parserOptions里配置了别的解析器,导致Vue模板解析失败。
7. 实操中我总结的几个关键心得
配置ESLint和Prettier的时候,有一条原则我屡试不爽:先让ESLint稳定,再让Prettier接手格式。也就是先保证npm run lint不报error,再设置Prettier格式化,这样即使两者发生冲突,也容易判断是谁导致的问题。
还有一个体会是,不要一上来就引入几十条自定义规则。ESLint的推荐规则集 + Vue官方推荐规则集已经覆盖了大多数场景。你先跑通自动修复和格式化,然后在日常开发中遇到确实不合理、确实需要个性化的地方,再一条条加进rules。我用过很多配置很重的老项目,规则超过200条,看着严谨,实际上很多规则同事根本不理解,报错之后大家只会手动// eslint-disable一行,反而失去监控意义。
最后再分享一个我自己一直沿用的习惯:把lint和format:check都加入CI的检查清单中,与单元测试并行运行。代码规范不是上线前的一锤子买卖,而是每次合并请求都要过的关卡。这样过一个月再看项目仓库,代码风格会意外地统一干净,逻辑评审也会愉悦很多。