尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

ESLint 与 Prettier 自动格式化,前端代码风格统一指南

ESLint 与 Prettier 自动格式化,前端代码风格统一指南 不知道你有没有遇到过这种现场团队里5个前端代码风格至少能凑出3套。有人双引号有人单引号有人缩进两格有人四格有人坚定加分号有人坚持裸奔。每次提代码评审评论区永远有一半篇幅在争论格式问题核心代码反而没人认真看。我后来花了一周时间把ESLint、Prettier和编辑器里的CtrlS自动保存格式化彻底打通这类争论基本归零。新同事拉下仓库npm install完随便打开一个文件改两行保存的瞬间代码风格自动统一。这篇就把整个链路拆开讲清楚从工具分工、VS Code配置、WebStorm和Vim的替代方案到husky lint-staged的提交兜底以及我实际踩过的各种坑全部摊开说。1. 保存那一下格式化为什么值得专门做一件事1.1 人工统一格式团队前三个月还能忍后面全在吵架大部分前端项目刚起步时代码风格问题是靠“约定”解决的。团队拉个群发一份《前端代码规范》里面有缩进、引号、分号、命名规则。听起来挺像回事但人类的记忆和执行力都靠不住。我见过一个挺典型的中型项目代码量到十万行级别之后git blame里密密麻麻都是“style format”这种提交。功能逻辑没人动今天这个人格式化了一部分文件明天那个人又格式化了一部分。结果代码评审时老同事看到一堆无关的空白字符变化根本分不清哪行才是真正的改动。有人会说“我自己注意点不就行了”问题在于注意成本太高。人每次写完代码都要想“这里该不该换行、这个函数参数要不要拆行”这些决策本质上跟业务逻辑没有任何关系纯粹消耗脑力。机器能干的活没必要让人用纪律去扛。把格式化交给工具反而是在给团队减负。真正该花的是配置时间不是每天反复纠结风格的时间。1.2 ESLint 不是格式化的唯一主角它和 Prettier 的分工很微妙很多刚入门的前端一提代码规范就想到ESLint以为装完ESLint就有了一切。这个理解不算错但不够完整。ESLint的核心能力是静态检查。它扫描代码里的逻辑问题和潜在隐患比如声明了没用的变量、意外使用全局变量、条件判断里漏了括号、React组件里缺了依赖项等等。它也能修复一部分问题eslint --fix可以自动处理很多可修复规则比如自动加空格、去掉多余分号。但ESLint不是专门做排版的它的规则重点在“代码哪里写错了”而不是“代码长得是否整齐划一”。Prettier则完全不同。它不关心你的业务逻辑不关心变量命名只看输出文本本身。你把一段乱七八糟的代码丢给它它按照统一配置重新排版缩进几格、单引号双引号、行宽多少、尾逗号怎么处理、文件末尾要不要换行全部由它说了算。用一个不精确但好理解的类比ESLint像机场安检负责检查行李箱里有没有违禁品Prettier像流水线上的打包机保证每个箱子外观都一模一样。安检和打包是两件事但一个完整的交付流程两者都需要。2. 你以为 ESLint 能直接帮你排版责任边界先掰扯清楚2.1 检查错误和统一排版是两件事拆开才能少掉头发ESLint本身确实带不少风格类规则比如缩进、引号、函数签名空格这些规则也能通过--fix自动修复。但问题在于ESLint的风格规则和Prettier的风格输出经常不是同一套标准两边都开着很容易出现“ESLint改完Prettier又改回去”的鬼畜循环。我来整理一个实用的对比表维度ESLintPrettier核心目标发现代码错误和反模式统一代码排版风格关注内容未使用变量、全局变量、代码复杂度、可维护性引号、缩进、分号、行宽、换行、尾逗号自动修复能力只修复规则中标记为fixable的问题全量重排没有“部分修复”概念配置风格规则极多可自定义每个开关和参数配置项很少故意限制自定义空间适合场景保证代码质量底线保证代码观感一致把两者拆开之后团队规则会清晰很多业务代码写得烂不烂、有没有低级错误由ESLint把关代码排版丑不丑、风格统不统一由Prettier负责。不要把Prettier挂在ESLint里面当成规则去跑后面我会细说。2.2 规则打架的典型现场以及行业通用的两条出路直接说结论只要你在项目里同时启用ESLint和Prettier就一定会遇到规则冲突。最常见的几个打架点行宽ESLint的max-len设了120Prettier默认printWidth是80两边较劲。引号ESLint的quotes要求双引号Prettier配置又设了singleQuote: true保存时来回顶牛。缩进ESLint的indent规则和Prettier的tabWidth偏好不一致。运算符换行ESLint的operator-linebreak和Prettier的排版策略不同。行业里目前有两条主流处理路线第一条使用eslint-config-prettier关掉ESLint里所有与格式化冲突的规则。这是我最推荐的做法。先把检查工具和排版工具的职责彻底分开ESLint专注逻辑排版交给Prettier。第二条使用eslint-plugin-prettier把Prettier当成ESLint的一条规则来跑。这样确实能在ESLint输出里看到格式化报错但代价是保存时多跑一道完整排版速度会变慢而且报错信息里混杂着“该加分号”和“这里有个未使用变量”两种噪音。我个人的经验是小项目无所谓中大型项目不建议这么干。如果你问社区里的主流方案大概率也是第一条ESLint Prettier eslint-config-prettierPrettier负责打印ESLint负责挑错各干各的。3. VS Code 下从零配置让 CtrlS 真正触发一次“整容”3.1 安装依赖eslint、prettier、eslint-config-prettier 各自的作用先创建项目然后在根目录安装依赖npm install -D eslint prettier eslint-config-prettier三个包各司其职eslint提供代码检查核心能力负责跑规则、报告错误、执行--fix。prettier提供代码格式化能力生成统一排版的文本。eslint-config-prettier官方提供的配置包专门用来关闭ESLint中与Prettier冲突的规则。如果你是Vue项目还需要装eslint-plugin-vue和vue-eslint-parserReact项目一般需要eslint-plugin-react和eslint-plugin-react-hooks。TypeScript项目要加typescript-eslint/parser和typescript-eslint/eslint-plugin。这些根据技术栈灵活补核心思路一样。这里特别提醒一句不要全局安装Prettier也不要让团队里的人各自用自己电脑上的Prettier版本。格式化工具版本不统一今天你格式化一个文件、明天同事格式化同一个文件结果可能完全不同。Prettier和ESLint必须装成项目本地依赖统一锁定版本。3.2 settings.json 每一项的配置逻辑别抄完就完事VS Code端要改工作区设置。打开项目的.vscode/settings.json写入{ editor.defaultFormatter: esbenp.prettier-vscode, editor.formatOnSave: true, editor.codeActionsOnSave: { source.fixAll.eslint: explicit }, editor.tabSize: 2, editor.detectIndentation: false, files.eol: \n }一字一句拆开讲为什么这么配。editor.defaultFormatter指定默认格式化器是Prettier扩展。如果不指定VS Code可能用一个内置的格式化器或者别的插件风格不受Prettier控制等于白搭。editor.formatOnSave是全局开启保存时格式化。但光有这个还不行它只触发格式化器也就是Prettier不会自动修复ESLint里的逻辑类问题。editor.codeActionsOnSave里那行source.fixAll.eslint才是真正让ESLint在保存时自动修复问题的地方。注意我写的是explicit不是true。在新版VS Code里用explicit表示只有当用户明确开启这个动作时才执行避免有些代码动作在后台被静默触发。即使你用的是旧版VS Code写true效果也是让ESLint在保存时跑一遍fix把可自动修复的问题处理掉。editor.tabSize和editor.detectIndentation是为了避免VS Code根据历史内容自动推断缩进。很多老项目混着2空格和4空格开着自动检测会导致你每次保存都只能格式化当前缩进推倒重来。关掉检测固定2空格让Prettier统一接管。files.eol设置默认换行符为LF避免Windows环境下把整个项目文件变成CRLF产生无意义的全文件diff。有几个点值得补充。Prettier扩展和ESLint扩展都需要在VS Code里安装如果企业内网环境安装不了扩展最好走命令行npx prettier --write或者借助CI兜底。另外每台新电脑打开项目时VS Code会弹“是否信任此工作区”需要允许工作区设置生效否则.vscode/settings.json里的配置不会加载。3.3 根目录配置文件eslint.config.js 和 .prettierrc 最小可用版先写.prettierrc一个最小但够用的配置{ singleQuote: true, semi: true, printWidth: 100, tabWidth: 2, trailingComma: es5, endOfLine: lf }这几个选项的含义singleQuote统一用单引号semi在语句末尾加分号printWidth控制一行代码最大宽度超过100字符Prettier会尝试换行tabWidth指定缩进为2空格trailingComma控制在ES5允许的语法结构里使用尾逗号endOfLine固定换行符为LF。再写ESLint配置。现在新项目基本都走Flat Config也就是根目录下创建eslint.config.jsimport js from eslint/js; import prettierConfig from eslint-config-prettier; export default [ js.configs.recommended, prettierConfig, { files: [**/*.{js,ts,vue}], rules: { no-console: warn, no-unused-vars: warn } } ];注意这里eslint-config-prettier要放在js.configs.recommended后面。Flat Config是顺序敏感的后面的配置会覆盖前面的同名规则。Prettier配置放最后才能确保ESLint推荐配置里那些与排版冲突的规则被关掉。这只是最小可用版实际项目里经常还要加languageOptions、plugins、ignores。但先跑通这个闭环最重要保存文件ESLint的自动修复先执行Prettier再把排版统一覆盖一遍输出结果干净一致。4. 换了 WebStorm 或 VimCtrlS 自动格式化还能不能玩4.1 WebStorm 的保存时格式化藏在“动作”里VS Code是前端主力但团队里保不齐有人用WebStorm。WebStorm内置ESLint和Prettier支持不需要装扩展不过要手动打开几个开关。设置路径一般在Settings→Languages Frameworks→JavaScript→Prettier勾选“On save”触发保存时格式化。同时在Settings→Languages Frameworks→JavaScript→Code Quality Tools→ESLint里找到“Run eslint --fix on save”或者“on code save”之类的选项打开。需要注意WebStorm默认的格式化器其实不是Prettier必须把Prettier设为默认的“Formatter”才行否则保存时是WebStorm自己的格式化和ESLint在打架。社区里matrix用户不少统一配置时把这些细节写进README比一个个口头解释靠谱得多。4.2 Vim/Neovim 用户别慌一条 autocmd 解决问题用Vim的人一般都不想装一堆IDE但格式化需求同样存在。最简单粗暴的方案是直接用Prettier命令行配合Vim的自动命令autocmd BufWritePre *.js,*.ts,*.tsx,*.vue,*.json silent! :!npx prettier --write %这段的意思是在文件写入磁盘前先对当前文件执行npx prettier --write。BufWritePre事件发生在文件写之前格式化完成后再保存效果和编辑器的“保存时格式化”一模一样。如果你用的是Neovim且配置了LSP更现代的做法是用conform.nvim或者null-ls这类格式化工具按文件类型配置Prettier作为formatter。它们同样工作在BufWritePre阶段但比裸调命令更可控也不会有每次npx启动的额外延迟。4.3 真正的跨编辑器一致性靠的是项目配置文件而不是IDE跨编辑器的关键并不是让每个IDE都保持一模一样的配置界面而是让所有人都基于同一套项目级配置运行。Prettier的读取顺序是项目根目录的.prettierrc或prettier.config.js找不到就用用户级配置。ESLint同样Flat Config的eslint.config.js在项目根目录。把这些文件提交到仓库任何编辑器只要支持对应工具保存时就会自动读取项目配置。为了更稳固还可以加一层.editorconfig它定义基本的缩进、字符集、换行符VS Code、WebStorm、Vim都原生支持。Prettier很多选项可以直接通过.editorconfig读取这样不同IDE之间哪怕格式化器实现有差异基础规则也能对齐。如果一个同事的VS Code没有加载项目设置或者他的编辑器扩展版本太老保存出来的代码可能还是跟团队不一致。这时候就需要提交阶段兜底我在第5章会讲。5. 保存格式化不背锅提交前的自动化才是团队闭环5.1 为什么光有 CtrlS 不够总有代码没被打开过CtrlS自动格式化解决了“编辑器里打开文件并修改”的场景但还留了几个洞有人从终端里用脚本批量改代码生成的临时文件根本没有经过编辑器有人用在线编辑或者低代码平台维护部分页面保存后的文件直接推到仓库还有人拉了远程分支之后没做任何操作但远程分支里的历史代码本来就没被格式化过。这些情况靠人肉检查是堵不住的。真正稳妥的做法是把格式化命令挂在提交钩子上。只要有人想git commit就先跑一遍检查没通过就不让提交。这样即使有人绕过了编辑器、改了配置文件到了提交这一步都会被拦下来。5.2 husky lint-staged只处理即将提交的代码最常用的一套组合是husky加lint-staged。husky负责注册Git钩子lint-staged负责只对暂存区的文件执行命令。先安装npm install -D husky lint-staged npx husky initnpx husky init会在项目根目录生成.husky文件夹里面有个pre-commit钩子文件默认内容一般是npm test。改成npx lint-staged然后在package.json里加lint-staged配置{ lint-staged: { *.{js,ts,tsx,vue,json,css,scss,md}: [ prettier --write, eslint --fix --max-warnings0 ] } }这里的逻辑是当用户执行git commit时husky触发lint-stagedlint-staged把暂存区里匹配到的文件分别跑一遍prettier --write和eslint --fix。格式化结束如果文件变了lint-staged会自动把这些改动重新加入暂存区一起提交。--max-warnings0是为了让ESLint处理任何warning都直接失败要求提交前必须清零。有些人觉得太严根据团队情况可以去掉但我个人建议保留因为一旦放松警告只会越堆越多最后提交钩子就形同虚设。值得注意的是prettier --write和eslint --fix写在一起顺序有一定讲究。我习惯把Prettier放前面ESLint放后面。理由是Prettier负责统一的排版基线ESLint再去做逻辑类修复这样不会出现两个工具互相覆盖的情况。只要配了eslint-config-prettierESLint不会来做排版类修改两者就不会顶牛。5.3 历史老项目接入时如何避免一次提交几千个文件很多人照着教程配完这套流程信心满满地提交然后发现lint-staged根本没触发或者没拦下问题因为老项目的历史代码本身就有成千上万个格式问题。如果直接在老项目里跑一遍全量prettier --write结果就是一个PR里多出几千个文件的diff代码评审完全没法做。我的建议是分三步走第一步先加.prettierignore把历史遗留目录和第三方代码暂时忽略掉让格式化只作用于新代码和修改到的文件。第二步用lint-staged只对本次暂存区的文件执行格式化和检查。老文件你没动它它就不会被格式化提交时的diff还是干净的。第三步等团队有精力了单独开一个“chore format all”的分支挑一个改动少的版本窗口一次性把所有存量代码格式化完毕然后尽快合入主干。这个分支建议用机器生成不带任何业务逻辑减少评审负担。这三步走下来老项目也能平滑接上自动格式化而不是推倒重来。6. 配置完成之后的真实踩坑现场和一条条对应的解法6.1 diff 污染格式化不该混在功能改动里项目接入了自动格式化后最让团队头疼的往往不是格式化不生效而是格式化把整个文件都改乱了。比如你在一个3000行的大文件里改一行逻辑保存时Prettier顺手把全文件重排了一遍diff里瞬间多出几百行变化。代码评审的人看半天不知道你到底改了什么逻辑只知道你动了半条命。解法很朴素历史问题文件不要急着格式化先让它们保持原样等你哪天集中重构或大改时再一并处理。提交钩子里的lint-staged只针对暂存区但它不会阻止大文件全量格式化——只要你保存了它就会格式化整个文件。所以编辑器的“格式化保存”和“提交前的增量处理”是两套心智要跟团队成员讲清楚保存时格式化只改你正在编辑的文件要是看到有不相干的大文件被改了先想想是不是自己不小心保存了。6.2 .vue、.ts、json 同时存在的项目格式化顺序会坑你现代前端项目很少只有纯JavaScript很可能是Vue组件配TypeScript再加一堆JSON配置还有vitest的单测文件。Prettier对这些文件类型基本都能处理但有一个隐藏坑eslint.config.js里的files匹配规则写错了会导致ESLint没跑或者Prettier对某些文件不生效。比如配了files [**/*.{js,ts,vue}]那.vue文件里的script setup langts需要vue-eslint-parser去解析记得在ESLint配置里指定parser。如果项目有vitest测试文件最好针对**/*.test.ts单独开规则避免测试文件里的断言风格被业务代码的规则误伤。还有项目里的JSON文件比如package.json、tsconfig.jsonPrettier默认也能格式化但有些特殊文件比如.eslintrc、.prettierrc如果没在格式化范围里就会被漏掉。把这些通通写进lint-staged的glob匹配里能省很多事。6.3 全局安装 prettier 还是项目安装答案比你想的绝对很多人图省事在自己电脑上全局装了一个Prettier然后VS Code里直接调用全局版本。项目本地没装Prettier刚开始也没发现问题直到某天Prettier升级了大版本格式化规则变了一下子全项目的文件都变得“不一致”。正确的做法只有一个项目本地安装并且锁版本。prettier和eslint都应该出现在package.json的devDependencies里团队所有人通过npm install拿到同一版本IDE扩展也优先读取项目本地的可执行文件。迫不得已的情况下你可以在settings.json里把Prettier的路径指到一个固定的全局版本但前提是你能保证团队所有人的全局版本一模一样。这几乎不可能所以别走这条路。6.4 给第三方目录开免死金牌.prettierignore 的用法Prettier会尝试格式化它认为属于前端的任何文件包括node_modules里的文件、打包产物、锁文件。这些文件一旦被格式化轻则产生巨大diff重则可能破坏生成逻辑。在项目根目录创建.prettierignore内容和.gitignore高度重合node_modules dist build coverage package-lock.json yarn.lock pnpm-lock.yaml *.min.js *.min.css这三个文件的作用边界很容易混淆.gitignore管的是文件要不要进Git版本库.prettierignore管的是Prettier格式化时跳过哪些文件ESLint对应的忽略配置则写在eslint.config.js的ignores数组里。三者不要混用各管各的。还有一个坑是.prettierignore里写的路径如果没匹配对可能会出现“明明配了忽略结果Prettier还是在提交钩子里格式化了某个目录”的诡异现象。这种时候建议直接跑一下npx prettier --check .看它到底把哪些文件视为待格式化一目了然。这套流程调通以后我再也没在代码评审里说过“麻烦统一一下引号”之类的话。工具干工具的事人干人的事团队代码风格终于不再靠脾气和记忆维持。如果你正在搭建或者重构前端的工程化规范可以先从这次最小闭环开始ESLint抓逻辑错误Prettier管排版CtrlS触发自动保存格式化提交钩子兜底。跑顺了再慢慢加更多规则代码质量自然会被拦在合入之前而不是合入之后。
返回列表