☰
Vue 3 项目代码格式化配置指南:Prettier + ESLint + VS Code 一次配好
2026/10/8 12:41:50 网站建设 项目流程

1. Vue 3 新项目为什么需要 Prettier + ESLint + VS Code 三件套

刚拉起来的 Vue 3 项目,最容易出现的问题不是功能写不出来,而是三个人提交的代码像三种风格:有人用双引号,有人用单引号;有人每行 80 字符就换行,有人一行写 200 字符;有人保存时自动格式化,有人手动改缩进。等到 code review 的时候,diff 里一半是空格和引号的变化,真正的逻辑改动反而被淹没。

Prettier 解决的是「风格」问题,它不关心你的代码写得对不对,只关心长什么样:缩进、引号、分号、换行、属性顺序。ESLint 解决的是「质量」问题,它关心你有没有用未定义的变量、有没有漏掉key、v-for里有没有写:key、ref有没有被意外解构。VS Code 负责把这两件事串起来,让你按一次Ctrl + S,格式化自动跑、lint 问题自动修,不用记命令。

这套链路适合谁?适合刚用npm create vue@latest或 Vite 拉起 Vue 3 项目、准备拉人一起写的前端。也适合从 Vue 2 迁过来、发现原来那套.eslintrc.js在新版 ESLint 里报FlatCompat错误的同学。我试过在一个 5 人小组里统一这套配置,第一周就少了大概 70% 的格式类 review 评论。

需要提前说清楚一个坑:ESLint 从 v9 开始默认用扁平配置eslint.config.js,不再默认读.eslintrc.*。很多老教程还在写.eslintrc.js,你照着配会发现 ESLint 根本不生效,或者报ESLint couldn't find an eslint.config.(js|mjs|cjs) file。这篇按新版扁平配置来写,同时给出 Prettier 与 ESLint 不打架的关键设置。

另外,格式化链路和 AI 辅助编码其实可以配合。你在 VS Code 里让模型补全代码时,补出来的片段风格未必和项目一致,但只要保存时 Prettier 接管,风格就会被拉回统一。如果你在用 Claude Code 这类命令行 Agent 写 Vue 组件,也可以让它走统一的模型入口,配置方式我在第 2 节给出,和格式化链路互不干扰。

2. 前置准备:Node 版本、依赖安装与模型入口配置

先把环境对齐。Vue 3 + Vite 项目建议 Node 18 以上,ESLint 9 和 Prettier 3 都要求 Node 18.18+。用node -v确认一下,低于 18 先升级,否则装依赖时会遇到engine不匹配的警告,严重时npm install直接失败。

依赖分三组装。第一组是 Prettier 本体和它与 ESLint 的桥接:

npm install -D prettier eslint-config-prettier

eslint-config-prettier的作用是关掉 ESLint 里所有和格式相关的规则,避免 ESLint 说「这里要加分号」而 Prettier 说「这里不要分号」,两边互相覆盖。第二组是 ESLint 本体和 Vue 插件:

npm install -D eslint eslint-plugin-vue vue-eslint-parser

vue-eslint-parser是解析.vue单文件组件的关键,没有它 ESLint 读不懂<template>和<script setup>。第三组是 TypeScript 项目才需要的:

npm install -D @vue/eslint-config-typescript typescript-eslint

如果你项目里用了@typescript-eslint/parser,注意它和vue-eslint-parser的嵌套关系:外层用vue-eslint-parser解析.vue,内层parserOptions.parser指向 TS 解析器,这样<script lang="ts">里的类型语法才不会报解析错误。

接下来是模型入口。如果你打算在写 Vue 组件时用 Claude Code 或类似命令行 Agent 做补全和重构,可以把它指向统一的 API 地址,这样不用在多个工具里重复填 Key。配置方式是设置环境变量:

export ANTHROPIC_BASE_URL="https://taotoken.net/api" export ANTHROPIC_AUTH_TOKEN="你的 API Key"

Key 在控制台的 API Keys 页面创建:https://taotoken.net/console/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=claude_code_setup

如果你用的是 Codex 这类读auth.json的工具,配置写在~/.codex/auth.json,字段是base_url和api_key,Base URL 同样填https://taotoken.net/api。模型 ID 按你实际要用的填,比如claude-sonnet-4-5这类。三件套(Base URL + Key + Model ID)缺一不可,只填两个最常见的报错就是 401。

想先验证模型通不通,不用写代码,直接在模型对话页面发一句话测试:https://taotoken.net/model-chat?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=model_chat_test

这一步和格式化链路是并行的,你可以先跳过,等第 3 节配置写完再回来补。但如果你团队里有人用 Agent 写代码,建议现在就配好,避免后面风格不统一时找不到原因。

3. 可复制配置:.prettierrc、eslint.config.js、settings.json 与 scripts

这一节是全文的核心,四个文件直接复制到项目根目录对应位置即可。先建.prettierrc,放在项目根目录:

{ "$schema": "https://json.schemastore.org/prettierrc", "printWidth": 120, "tabWidth": 2, "useTabs": false, "semi": false, "singleQuote": true, "quoteProps": "as-needed", "jsxSingleQuote": true, "trailingComma": "none", "bracketSpacing": true, "bracketSameLine": false, "arrowParens": "avoid", "proseWrap": "preserve", "htmlWhitespaceSensitivity": "css", "vueIndentScriptAndStyle": false, "endOfLine": "auto", "embeddedLanguageFormatting": "auto", "singleAttributePerLine": false }

几个容易踩坑的项单独说。endOfLine: "auto"是为了跨平台,Windows 用 CRLF、Mac 用 LF,设成auto后 Prettier 保留文件原有换行符,不会因为一个人提交就把整个文件标成改动。vueIndentScriptAndStyle: false表示<script>和<style>里的内容不额外缩进一层,这是 Vue 社区比较主流的写法。arrowParens: "avoid"让单参数箭头函数不写括号,x => x * 2而不是(x) => x * 2,如果你团队习惯带括号,改成"always"即可。

然后是eslint.config.js,这是 ESLint 9 的扁平配置,放在项目根目录:

import js from '@eslint/js' import pluginVue from 'eslint-plugin-vue' import prettierConfig from 'eslint-config-prettier' export default [ js.configs.recommended, ...pluginVue.configs['flat/recommended'], prettierConfig, { files: ['**/*.{js,mjs,cjs,vue}'], languageOptions: { ecmaVersion: 'latest', sourceType: 'module', globals: { window: 'readonly', document: 'readonly', console: 'readonly' } }, rules: { 'vue/multi-word-component-names': 'off', 'vue/no-unused-vars': 'error', 'no-unused-vars': ['error', { argsIgnorePattern: '^_' }] } }, { ignores: ['dist/**', 'node_modules/**', '*.min.js'] } ]

注意prettierConfig必须放在 Vue 插件配置之后,否则它关不掉 Vue 插件里那些格式规则。vue/multi-word-component-names关掉是因为很多项目里index.vue、Home.vue这种单词组件名很常见,开着会一直报错。argsIgnorePattern: '^_'让你用下划线开头的参数表示「故意不用」,避免 lint 误报。

TypeScript 项目在files里加上'**/*.ts',并在languageOptions.parserOptions里指定parser: tseslint.parser,同时把typescript-eslint的 recommended 配置展开进来。

第三个文件是.vscode/settings.json,放在.vscode目录下:

{ "editor.formatOnSave": true, "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit" }, "editor.defaultFormatter": "esbenp.prettier-vscode", "[vue]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, "[javascript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, "[typescript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, "[json]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, "[jsonc]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, "[html]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, "[css]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, "files.associations": { "*.vue": "vue" }, "emmet.includeLanguages": { "vue": "html" }, "search.exclude": { "**/node_modules": true, "**/dist": true, "**/pnpm-lock.yaml": true } }

editor.codeActionsOnSave里source.fixAll.eslint的值在新版 VS Code 里要写"explicit",写true会提示已废弃。formatOnSave和fixAll.eslint同时开,保存时先跑 ESLint 修复再跑 Prettier 格式化,顺序由 VS Code 内部协调,实测不会冲突。

第四个是.vscode/extensions.json,让队友打开项目时收到插件推荐:

{ "recommendations": [ "Vue.volar", "esbenp.prettier-vscode", "dbaeumer.vscode-eslint" ] }

最后在package.json的scripts里加两条命令,方便 CI 和本地批量检查:

{ "scripts": { "lint": "eslint . --fix", "format": "prettier --write \"src/**/*.{js,ts,vue,json,css}\"" } }

lint带--fix会自动修能修的,format只格式化src下的文件,避免误改dist和锁文件。团队里有人不装 VS Code 插件时,跑这两条命令也能对齐风格。

4. 验证请求:保存触发格式化与 lint 修复的完整过程

配置写完不验证等于没配。这一节用一个真实的.vue文件走一遍,看保存时到底发生了什么。

在src/components下新建DemoCard.vue,故意写成不规范的样子:

<script setup> import { ref } from "vue" const count=ref(0) const unusedVar = 123 function add(){count.value++} </script> <template> <div class="card"> <p>当前计数:{{count}}</p> <button @click="add">加一</button> </div> </template> <style scoped> .card{padding:16px;border:1px solid #ddd} </style>

这段代码有三个问题:const count=ref(0)等号两边没空格、function add(){...}大括号没空格、unusedVar声明了没用。按Ctrl + S保存,观察 VS Code 的行为。

保存后第一件事是 ESLint 的source.fixAll.eslint触发,它会报unusedVar未使用,这个属于逻辑问题,ESLint 不会自动删(删了可能改变你的意图),会在问题面板标红。同时 Prettier 接管格式化,把const count=ref(0)改成const count = ref(0),function add(){改成function add() {,<style>里的.card{padding:16px;...}展开成多行。

保存后的文件变成:

<script setup> import { ref } from 'vue' const count = ref(0) const unusedVar = 123 function add() { count.value++ } </script> <template> <div class="card"> <p>当前计数:{{ count }}</p> <button @click="add">加一</button> </div> </template> <style scoped> .card { padding: 16px; border: 1px solid #ddd; } </style>

注意import { ref } from "vue"的双引号变成了单引号,{{count}}变成了{{ count }},这些都是 Prettier 按.prettierrc里singleQuote: true和 Vue 模板规则做的。unusedVar还在,因为 ESLint 只标记不自动删,你需要手动处理或改成_unusedVar让它被argsIgnorePattern忽略。

再验证一次命令行。在终端跑:

npm run lint

如果unusedVar还在,会看到类似输出:

/path/src/components/DemoCard.vue 4:7 error 'unusedVar' is assigned a value but never used no-unused-vars

把unusedVar删掉或改名_unusedVar,再跑一次,输出为空,说明 lint 通过。接着跑:

npm run format

终端会列出被格式化的文件,比如src/components/DemoCard.vue 30ms。如果文件已经符合规范,Prettier 会跳过不输出,这是正常行为。

到这里,保存即格式化、保存即修 lint 的链路就通了。你可以故意把.prettierrc里的semi改成true,保存后看分号是否加上,再改回false,确认配置真的在生效,而不是 VS Code 用了某个全局默认值。

5. 常见报错排查:401、local proxy failed、reading choices 与 OAuth

配置过程中最容易卡住的不是 Prettier 本身,而是模型入口和 ESLint 版本问题。这一节按真实报错逐条对照。

报错一:401 Unauthorized。出现在你用 Claude Code 或 Codex 调模型时。原因通常是三件套没配全:只填了 Base URL 没填 Key,或者 Key 填错、过期。检查ANTHROPIC_AUTH_TOKEN是否和 API Keys 页面创建的一致,注意不要有多余空格。Codex 用户检查~/.codex/auth.json里api_key字段,Base URL 必须是https://taotoken.net/api,结尾不要多加/v1,加了会 404 而不是 401,但表现类似。

报错二:local proxy failed。这个报错一般出现在工具尝试走本地端口转发时。检查你的环境变量里有没有残留的HTTP_PROXY、HTTPS_PROXY指向一个没启动的本地端口。清掉这些变量再试:

unset HTTP_PROXY HTTPS_PROXY ALL_PROXY

然后重新执行命令。如果你在 CI 里跑,检查 CI 的环境变量配置,很多 CI 默认注入了代理变量。

报错三:reading 'choices' of undefined。这是 OpenAI 兼容接口的典型报错,出现在返回体结构和代码预期不一致时。常见原因是模型 ID 填错,服务端返回了错误对象而不是正常的choices数组。检查你填的 Model ID 是否在可用列表里,比如claude-sonnet-4-5这类,拼写错误会直接导致这个报错。另一个原因是请求体里stream参数和客户端处理逻辑不匹配,关掉流式再试一次能定位。

报错四:OAuth 相关报错。如果你用的工具默认走 OAuth 登录而不是 API Key,会提示需要授权。这类工具要切换到 API Key 模式,在配置里找auth_type或类似字段,改成api_key,然后填 Base URL 和 Key。Claude Code 用环境变量方式就不会触发 OAuth。

报错五:ESLint 报couldn't find eslint.config.js。说明你项目里还是老配置,或者 ESLint 版本低于 9。确认package.json里eslint版本是^9.x,并且根目录有eslint.config.js。如果同时存在.eslintrc.js,删掉它,否则 ESLint 9 会忽略扁平配置。

报错六:Prettier 和 ESLint 互相覆盖。表现是保存后格式对了,但 ESLint 又报格式错误。检查eslint.config.js里prettierConfig是否在数组最后。如果用了eslint-plugin-prettier(把 Prettier 当 ESLint 规则跑),建议去掉,改用eslint-config-prettier分离方案,性能更好也不容易冲突。

排查完这些,你的格式化链路基本就稳了。如果团队里有人用 Cursor,.vscode/settings.json同样生效,因为 Cursor 基于 VS Code 构建,配置目录兼容。Cursor 专属的 AI 规则放.cursor/rules/,和格式化配置互不干扰。

6. 长期编码与团队协作:把格式化链路接进 Agent 工作流

单机配好只是第一步,团队协作里真正省时间的是把格式化链路和 AI 编码工具接起来。当你在 VS Code 里用 Agent 补全一个 Vue 组件,补出来的代码可能用双引号、可能缩进 4 空格,但只要保存时 Prettier 接管,风格立刻统一。ESLint 则负责拦住 Agent 可能引入的未使用变量、缺失:key这类问题。

如果你团队用 Claude Code 做长期编码,建议把模型入口固定下来,避免每个人各配一套。环境变量方式最简单:

export ANTHROPIC_BASE_URL="https://taotoken.net/api" export ANTHROPIC_AUTH_TOKEN="你的 API Key"

需要长期跑 Agent 任务、频繁调用的场景,可以看 Coding Plan 的额度方案:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=coding_plan

接入文档里有各工具的完整配置示例,包括 Claude Code、Codex、Cline 这些:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=doc_integration

一个实用技巧:把npm run lint挂到 Git 的 pre-commit 钩子上,用husky+lint-staged,只检查暂存区的文件。这样即使有人没装 VS Code 插件,提交前也会被拦一道。配置大概是这样:

{ "lint-staged": { "*.{js,ts,vue}": ["eslint --fix", "prettier --write"] } }

顺序是先 ESLint 修逻辑问题,再 Prettier 统一风格。注意lint-staged里不要对dist和锁文件生效,否则提交会变慢。

最后说一个我踩过的坑:.prettierrc里的endOfLine如果设成"lf",Windows 队友保存后整个文件会被标成改动,因为 Git 默认core.autocrlf会把 LF 转 CRLF。设成"auto"能避免这个问题,但更彻底的做法是在项目根目录加.gitattributes:

* text=auto eol=lf

这样无论谁在什么系统上提交,仓库里存的都是 LF,Prettier 的endOfLine: "auto"也不会再产生意外 diff。这两处配合,跨平台团队的格式问题基本就清零了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询