背景

在一个 Vue3 + Vite 项目中,同时使用 ESLint(代码质量检查)和 Prettier(代码格式化)时,两者的规则可能冲突。最典型的就是 ESLint 要求最大行长 80 字符,而 Prettier 默认也是 80——看起来一致,但一旦自定义其中一个就可能打架。

解决方案:用 ESLint 驱动 Prettier。通过 eslint-plugin-prettier 将 Prettier 的格式化规则作为 ESLint 的一条规则运行,再通过 eslint-config-prettier 关闭所有与 Prettier 冲突的 ESLint 原生风格规则。

本文以 qiyun_admin 项目的实际配置为例,完整说明集成方式和关键细节。


依赖安装

1
npm install -D prettier eslint-plugin-prettier eslint-config-prettier

三个包的分工:

作用
prettier 格式化引擎
eslint-plugin-prettier 将 Prettier 作为 ESLint 的一条 rule 运行
eslint-config-prettier 关闭 ESLint 中与 Prettier 冲突的风格规则

如果项目中还没有 ESLint 和 Vue ESLint 支持,还需安装:

1
npm install -D eslint @eslint/js eslint-plugin-vue globals

1. Prettier 配置 — .prettierrc.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 一行最多字符数
printWidth: 120
# 每个缩进级别的空格数
tabWidth: 2
# 使用制表符(Tab)缩进
useTabs: true
# 行尾不使用分号
semi: false
# 使用单引号
singleQuote: true
# 对象属性引号仅在必要时添加
quoteProps: "as-needed"
# JSX 中使用双引号
jsxSingleQuote: false
# 是否添加多余逗号 none|es5|all
trailingComma: "es5"
# 对象大括号内部首尾保留空格
bracketSpacing: true
# JSX 右尖括号与最后一行属性同行
bracketSameLine: true
# 箭头函数参数始终加括号
arrowParens: "always"
# HTML 空白敏感度遵循 CSS 默认行为
htmlWhitespaceSensitivity: "css"
# 换行符使用 LF (Unix/Linux/macOS 风格)
endOfLine: "lf"
# Vue 文件中 script 和 style 标签内的代码不进行缩进
vueIndentScriptAndStyle: false

关键点:

  • printWidth: 120 — 项目使用的行宽是 120,比 Prettier 默认的 80 更宽松,适合 Vue 模板中较长的属性链。
  • useTabs: true — 使用 Tab 缩进,配合 tabWidth: 2 让 Tab 显示为 2 个空格宽度。
  • semi: false — 无分号风格。
  • trailingComma: 'none' — 不添加尾逗号,与 ES2017 以来允许尾逗号的趋势不同,这里是团队风格选择。

2. ESLint 配置 — eslint.config.mjs(Flat Config)

项目使用 ESLint 的 Flat Configeslint.config.mjs,ESLint v9+ 默认格式),而非传统的 .eslintrc.*

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
import js from "@eslint/js";
import prettierConfig from "eslint-config-prettier";
import prettierPlugin from "eslint-plugin-prettier";
import pluginVue from "eslint-plugin-vue";
import globals from "globals";
import vueParser from "vue-eslint-parser";

export default [
// 基础推荐规则
js.configs.recommended,
// Vue 推荐规则
...pluginVue.configs["flat/essential"],
// 注意:必须放在其他配置之后,以确保它能覆盖前面的风格规则
prettierConfig,
{
plugins: {
// 注册 prettier 插件
prettier: prettierPlugin,
},
languageOptions: {
ecmaVersion: 2020,
sourceType: "module",
// 对应 env: { browser: true, node: true, es6: true }
globals: {
...globals.browser,
...globals.node,
...globals.es2021,
// 对应 env: { 'vue/setup-compiler-macros': true }
defineProps: "readonly",
defineEmits: "readonly",
defineExpose: "readonly",
withDefaults: "readonly",
},
parser: vueParser,
},
rules: {
// --- 业务逻辑规则---
"no-unused-vars": "off",
"no-useless-assignment": "off",
"no-console": "off",
"no-debugger": "warn",

// --- Vue 特定规则---
"vue/no-unused-vars": "warn",
"vue/custom-event-name-casing": "off",
"vue/attributes-order": "off",
"vue/one-component-per-file": "off",
"vue/require-default-prop": "off",
"vue/multi-word-component-names": "off",

// --- Prettier 集成规则 ---
// 将 Prettier 格式问题作为 ESLint 错误抛出,统一编辑器反馈
"prettier/prettier": "error",
},
},
{
// 忽略文件 (对应 .eslintignore)
ignores: ["dist/**", "static/**", "build/**"],
},
];

集成原理

  1. prettierConfig(即 eslint-config-prettier — 必须放在 js.configs.recommendedpluginVue 配置之后。它会把所有与 Prettier 冲突的 ESLint 原生风格规则全部关闭。如果放反了顺序,样式规则会被后面的配置重新打开,等于没关。

  2. prettierPlugin(即 eslint-plugin-prettier — 通过 'prettier/prettier': 'error' 将 Prettier 的格式化结果作为 ESLint 的一条规则执行。不再额外传入 endOfLine 选项,直接信任 .prettierrc.yaml 中的配置。如果团队跨平台遇到换行符冲突(Delete '␍' 错误),统一通过 .gitattributes 解决,见 FAQ。

  3. 为什么很多 Vue 风格规则设为 'off' — 这些规则(如 vue/max-attributes-per-linevue/html-closing-bracket-newline 等)都与 Prettier 的 HTML/Vue 格式化能力重叠。Prettier 对 Vue template 的格式化已经处理了缩进、换行、属性排列,所以关闭 ESLint 侧的同功能规则,避免冲突。

Flat Config 与传统 .eslintrc 的差异注意点

如果你从传统配置迁移到 Flat Config:

项目 传统 .eslintrc.js Flat Config eslint.config.mjs
导出 module.exports = { ... } export default [ ... ](数组)
插件注册 plugins: ['prettier'] plugins: { prettier: prettierPlugin }
配置继承 extends: ['prettier'] 直接导入并展开到数组
Parser 配置 parser: 'vue-eslint-parser' parser: pluginVue.parsers.vue
忽略文件 .eslintignore ignores: ['dist/**']

3. 忽略规则 — .prettierignore

1
2
3
4
5
6
7
8
9
dist/
node_modules/
build/
static/
public/
*.min.js
*.min.css
*.map
.gitignore

与 ESLint 的 ignores 保持同步,确保构建产物和第三方文件不被格式化。


4. VS Code 配置 — 保存时自动格式化

在项目根目录创建 .vscode/settings.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
},
"[vue]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}

为什么既设 prettier 为默认格式化器,又开 eslintfixAll

  • Prettier 格式化器处理所有文件的格式
  • ESLint 的 source.fixAll.eslint 同时也会触发 prettier/prettier 规则的修复,双重保险
  • 两者的修复结果一致(因为 ESLint 内部调用的就是 Prettier)

需要安装 VS Code 扩展:


5. NPM Scripts

package.json 中添加:

1
2
3
4
5
6
7
{
"scripts": {
"format": "prettier --write \"src/**/*.{js,ts,vue,css,scss,json,md}\"",
"lint": "eslint src/ --fix",
"lint:check": "eslint src/"
}
}
  • npm run format — 手动格式化所有源码
  • npm run lint — ESLint 检查并修复(同时也修复 Prettier 问题)
  • npm run lint:check — 仅检查,不修复(用于 CI)

6. Husky + lint-staged(可选)— 提交前自动检查

仅有编辑器保存格式化还不够——总有人忘记安装扩展、或者直接用其他编辑器修改。Husky + lint-staged 可以在 git commit 时自动对暂存文件运行格式化与 ESLint 检查,把住最后一道关。

安装

1
2
npm install -D husky lint-staged
npx husky init

husky init 会在项目根目录创建 .husky/ 文件夹和 pre-commit hook。

配置

.husky/pre-commit

1
npx lint-staged

这是 Husky v9 默认生成的 hook 文件,内容只有这一行。

Husky v9 注意:旧版本(v4/v5)需要在 package.json 中配置 "husky": { "hooks": {...} } 或使用 .huskyrc.js。v9 统一使用 .husky/ 目录下的 shell 文件,更直观、可版本控制。

package.json 添加 lint-staged 配置

1
2
3
4
5
6
{
"lint-staged": {
"*.{js,ts,vue}": ["prettier --write", "eslint --fix"],
"*.{css,scss,json,md}": ["prettier --write"]
}
}

工作流程:

1
2
3
4
5
6
7
git add src/components/Button.vue
→ git commit -m "feat: add button"
→ Husky 触发 pre-commit hook
→ lint-staged 读取暂存文件
→ 匹配 *.vue → 运行 prettier --write → 运行 eslint --fix
→ 格式化结果自动加入暂存区
→ commit 成功(或失败并提示修复)

为什么不直接把 eslint --fix 作为唯一的 hook?

因为 Prettier 格式化在某些场景下会修改 ESLint 不会触碰的部分(比如 Markdown 换行、YAML 缩进)。先跑 prettier 再跑 eslint,确保两者都通过。

跳过 hook

1
git commit --no-verify -m "紧急修复:跳过 lint"

只在万不得已时使用(如修复生产环境紧急 bug)。


7. 工作流程总结

1
2
3
4
5
6
7
编辑代码
→ Ctrl+S(或 Cmd+S)
→ VS Code Prettier 格式化
→ ESLint 运行 prettier/prettier 校验
→ ESLint 报告/修复其他代码质量问题
→ git commit
→ Husky / lint-staged 再次校验(可选)

在编辑器层面,Prettier 负责"好不好看",ESLint 负责"对不对"。通过 eslint-plugin-prettier,两者在保存时一次性完成,互不打架。


常见问题

Q: Prettier 格式化的结果和 ESLint 的 fix 结果不一致?

A: 检查 eslint-config-prettier 是否放在了数组的最后(或至少在所有规则配置之后)。Flat Config 中数组顺序决定规则优先级,后面的覆盖前面的。

Q: prettier/prettier 报错 Delete '␍' (prettier/prettier)?

A: 这是换行符冲突。统一团队的 .gitattributes 来解决:* text=auto eol=lf。不再建议在 prettier/prettier 规则中加 endOfLine: 'auto' 覆盖,因为这样会隐藏项目本身的换行符配置问题,而 .gitattributes 是更根本的解决方案。

Q: 配置了项目 .vscode/settings.json,但保存时 Prettier 不生效?

A: 最常见原因:VS Code 用户设置(User Settings)的优先级高于工作区设置(Workspace Settings)。如果你之前在用户设置中强制指定了其他格式化器,会覆盖项目配置。

检查路径:

  • macOS:~/Library/Application Support/Code/User/settings.json
  • Linux:~/.config/Code/User/settings.json
  • Windows:%APPDATA%\Code\User\settings.json

打开用户设置 JSON,检查是否有以下配置覆盖了项目设置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
// ❌ 全局指定了其他格式化器,会优先生效
"editor.defaultFormatter": "dbaeumer.vscode-eslint",

// ❌ 全局关闭了保存格式化
"editor.formatOnSave": false,

// ❌ 按语言覆盖了格式化器
"[javascript]": {
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
},
"[vue]": {
"editor.defaultFormatter": "some-other-formatter"
}
}

VS Code 设置优先级(从低到高)

1
默认设置 < 用户设置 < 工作区设置 < 文件夹设置

逻辑上工作区设置(.vscode/settings.json)应该覆盖用户设置,但有两点例外:

  1. editor.defaultFormatter 的行为:如果用户设置的 [language] 作用域下指定了格式化器,而工作区设置只在顶层指定了 editor.defaultFormatter,前者会胜出。所以项目配置需要同时在语言作用域下设置,如上面第 4 节所示。
  2. 显式排除:用户设置中有 "settingsSync.ignoredSettings": [...] 不会同步某些设置,但本地仍生效。

排查步骤

  1. 打开命令面板(Cmd+Shift+P),输入 >Developer: Inspect Editor Tokens and Scopes
  2. 右键点击编辑器状态栏的 Prettier 图标,查看当前文件正在使用哪个格式化器
  3. 或者查看 VS Code 输出面板(Output),选择 “Log (Window)”,搜索 Formatter 相关日志

推荐做法:不要在用户设置中设置 editor.defaultFormatter,让每个项目通过 .vscode/settings.json 自行声明。用户设置只放个人偏好(主题、字体、键绑定等)。

Q: 不想让 ESLint 报 Prettier 错误,只想让 Prettier 独立工作?

A: 那就不要用 eslint-plugin-prettier,只保留 eslint-config-prettier(关掉冲突规则)。Prettier 通过编辑器格式化器独立运行。但这样格式化错误不会显示在 ESLint 的问题面板中。

Q: Flat Config 中插件导入报错 “Module not found”?

A: 确保所有 ESLint 相关依赖都已安装(eslinteslint-plugin-vueglobals@eslint/jseslint-plugin-prettiereslint-config-prettier)。Flat Config 使用 ESM 的 import 语法,不要用 require()