ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Prettier 代码格式化实战:原理、配置、编辑器集成与团队规范

Prettier 代码格式化实战:原理、配置、编辑器集成与团队规范 接手过有一定历史的项目就知道代码评审里最烦的不是逻辑bug而是diff里躺着一堆跟你改动无关的格式变动。这些格式噪音大多来自开发者本地的编辑器差异有人习惯单引号有人坚持双引号有人缩进两个空格有人用四个有人写JS坚决不加分号有人每行都加。Prettier 这类代码格式化工具核心价值就是把这些争议用一套确定性规则彻底自动化——人不再为格式吵架机器负责统一输出。这篇文章我打算把 Prettier 从工作原理、常用配置项到 VSCode 和 IDEA 两端的集成排查、团队格式化模板搭建完整梳理一遍适合正在为格式问题头疼的前端开发者参考。1. 为什么格式统一必须靠工具而不是靠人自觉1.1 格式化战争的隐藏成本先还原一个典型场景一个五人的前端小组有人用 VSCode有人用 IDEA有人还用着 Sublime。团队带头人开了个会强调大家写代码的时候注意统一风格所有人点头。但项目一旦进入赶版本节奏没人还有精力去手动对齐每一处引号、空格和分号。两周之后PR 里开始出现一种诡异的现象功能代码可能只改了三四行但 diff 里多出了几十行空白和引号变动reviewer 必须在格式噪音里像考古一样找真正的逻辑变更。这种时间成本非常隐蔽但它是实打实的。一次 review 多花十分钟团队十个人每天十几次 review一年下来浪费的工作量相当客观。更麻烦的是当一次大范围格式化提交混进业务 commitgit blame 会被整体污染——后面想查某行代码是谁在哪个需求里写的跳出来的全是格式化提交。这类沉默成本不会在周报里出现但长期拖累的可维护性很多团队直到做历史代码考古时才追悔莫及。1.2 Prettier 的核心机制先解析成 AST再重新排版Prettier 与传统格式化工具最大的差异在于工作方式。它并不是做简单的文本替换而是先把代码解析成一棵抽象语法树AST把你写的 JavaScript、TypeScript、CSS、JSON、Markdown 都先提炼成语义结构然后再根据配置项把这棵树重新序列化成文本输出。这个机制带来一个决定性的好处只要代码语义一致不管你的原始写法多随意——一行能写成三行、对象缩进歪七扭八、字符串引号来回混用——经过 Prettier 之后输出结果完全相同。格式化结果是确定性的没有我觉得这里该换行的人工判断空间。可以这么理解Prettier 就像一套标准排版引擎你的输入是内容的意思输出永远是固定的板式任何人在任何机器上跑产物都是同一份。1.3 与 ESLint 的分工质量归质量排版归排版很多人把 Prettier 和 ESLint 放在一起对比但这两个工具解决的问题根本不在一个层级。ESLint 的核心职责是代码质量检查关注的是变量声明了没使用隐式类型转换是否危险函数复杂度是否过高这类逻辑问题Prettier 则纯粹关注排版管的是缩进、引号、分号、换行位置、尾逗号样式。简单说ESLint 管逻辑对不对Prettier 管代码齐不齐。所以正规做法是两者配合使用ESLint 作为质量门禁Prettier 作为格式门禁。实践中最常见的坑就是把格式类规则一股脑写进 ESLint 配置比如强制单引号、强制尾逗号结果 ESLint 的规则和 Prettier 的输出互相冲突开发者保存一遍被改一遍格式还未必符合预期。我的建议是ESLint 中尽量关闭与排版相关的规则格式的事情全部交给 Prettier这样两边的职责都清晰配置也不会冗余。2. Prettier 核心配置项逐项拆解每个选项背后的取舍逻辑2.1 配置查找优先级先搞清楚 Prettier 到底听谁的Prettier 查配置遵循就近原则。处理某个文件时它会从该文件所在目录开始逐级向上查找找到最近的配置文件就停止如果一路找到用户主目录都没有就用内置默认值。说白了离文件最近的配置最有话语权一个嵌套子目录里的 .prettierrc 可以覆盖根目录的规则。常见的配置载体包括 package.json 中的 prettier 字段、.prettierrc 文件支持 JSON/YAML/TOML、.prettierrc.js 或 prettier.config.js可导出对象或函数。在 Monorepo 结构里这个机制特别好用根目录放一份全仓统一的 .prettierrc.json某个子包如果有特殊需求比如模板文件不希望限制行宽在子包内放一份自己的配置覆盖即可。团队协作时只要约定好配置放哪、谁负责维护格式基线就能长期稳定不会因为某个人改了本地配置而影响到别人。2.2 高频配置项与推荐值配置项不算多但每一项都直接影响日常手感。我先给一份多数前端项目都能直接落地的推荐值再逐个解释决策理由。配置项默认值我的推荐备注printWidth80100单行最大字符数80 偏窄120 又嫌太长tabWidth22缩进宽度前端项目主流是 2useTabsfalsefalse统一用空格缩进避免 tab 宽度在不同编辑器里打架semitruetrue语句末尾自动加分号singleQuotefalsetrue字符串优先使用单引号quotePropsas-neededas-needed对象属性名能不加引号就不加jsxSingleQuotefalsefalseJSX 属性中不使用单引号trailingCommaallall多行场景补尾逗号减少未来的行变更 diffbracketSpacingtruetrue对象字面量花括号内侧保留空格bracketSameLinefalsefalseJSX 的是否放到最后一行末尾arrowParensalwaysalways箭头函数单个参数也加括号endOfLinelflf统一使用 LF 换行符重点说几个争议比较大的选项。printWidth 为什么从 80 提到 100因为现在屏幕普遍宽80 的限制会让很多单行逻辑被强行拆成三四行阅读反而割裂。semi 默认 true很多人喜欢无分号风格但依赖 JS 的自动分号插入ASI需要考虑边界情况比如行首是[、(、时可能出现意外的语法解析显式加分号可以规避这类心智负担。trailingComma 推荐 all不只是风格偏好——多行对象新增属性时每行本来都有逗号不会因为加了一条数据而让上一行额外多出或去掉逗号git diff 会更干净。2.3 overrides给特定类型文件开小灶全局一套配置当然方便但实际项目中总有例外。比如 Markdown 文档里的长链接printWidth 强制换行会让链接断成两截体验很差再比如 package.json 里密密麻麻的依赖列表用 printWidth 100 会被拆得很碎反而不利于阅读。Prettier 提供的 overrides 配置就是为这种场景准备的它可以按文件路径或通配符匹配给特定文件单独覆盖任意选项。{ printWidth: 100, singleQuote: true, overrides: [ { files: *.md, options: { proseWrap: preserve } }, { files: [package.json, *.json], options: { printWidth: 200 } }, { files: *.vue, options: { htmlWhitespaceSensitivity: ignore } } ] }这种做法比全局一个配置走天下灵活得多。我实际维护的项目里几乎都会给 .md 文件开 proseWrap: preserve避免长文本被强制换行给 JSON 类文件放宽行宽保证依赖列表的可读性。overrides 的存在让团队不必为了几个边缘文件妥协全局规范该统一的地方统一该特殊的地方特殊。3. VSCode 里装好 Prettier 后为什么保存文件还是不格式化3.1 完整安装与配置链路VSCode 中使用 Prettier看起来只是装个扩展实际上有几步不能跳过。先从扩展市场安装 esbenp.prettier-vscode然后要在设置里做三件事指定默认格式化器、开启保存自动格式化、按需配置 requireConfig。如果只装扩展不做设置VSCode 很可能仍然走内置的格式化逻辑或者明明格式混乱却没有任何反应。推荐在工作区层面的 .vscode/settings.json 中直接固定配置而不是让每个开发者去自己改用户设置。这样团队成员打开项目就自动生效不用互相提醒你格式化器选对了吗。基础配置长这样{ editor.defaultFormatter: esbenp.prettier-vscode, editor.formatOnSave: true, editor.formatOnPaste: false, editor.formatOnType: false }formatOnSave 建议打开formatOnPaste 和 formatOnType 我一般关掉因为粘贴时自动格式化容易产生意外改动写代码时实时格式化也会打断思路统一保存时处理最稳妥。3.2 常见踩坑格式化器冲突和 requireConfig 埋雷VSCode 里 Prettier 不生效绝大多数逃不出三种情况。第一种是多个格式化器竞争。如果项目里同时装了 ESLint 扩展、Vetur 或 Beautify 这类也带格式化能力的扩展而 defaultFormatter 又没有明确指定VSCode 会感到困惑甚至会弹窗让你选。即使选了不同扩展的执行结果也可能互相覆盖最终保存出来的格式根本不是 Prettier 风格。解决办法就是全局把默认格式化器锁死为 esbenp.prettier-vscode。第二种是 prettier.requireConfig 配置导致的不生效。这个参数默认是 false也就是说即使项目里没有 .prettierrcPrettier 也会用默认规则格式化。但如果有人把它改成了 true而项目根目录恰好没有配置文件Prettier 会直接罢工——很多突然格式化没反应的案例根源就在这里。排查看似无从下手其实检查一下这个开关和项目里有没有配置文件就够了。第三种是执行右键格式化文档时VSCode 实际调用的是内置格式化器而不是 Prettier。这种情况可以在弹出菜单底部看到配置默认格式化器的入口把它指到 Prettier 即可。记住一个原则所有格式化入口都走同一套默认格式化器就不会出现保存一次一个样的诡异场景。3.3 与 ESLint 并存的保存动作配置项目里同时用 ESLint 和 Prettier 时保存动作需要协调。推荐的配置是让 formatOnSave 负责 Prettier 排版同时用 codeActionsOnSave 里的 source.fixAll.eslint 让 ESLint 做逻辑层面的快速修复{ editor.defaultFormatter: esbenp.prettier-vscode, editor.formatOnSave: true, editor.codeActionsOnSave: { source.fixAll.eslint: explicit } }这里有个先后顺序问题如果 ESLint 里还留着格式类规则保存时可能出现ESLint 改一遍、Prettier 又改一遍的来回跳动甚至报错。所以我一直强调ESLint 里格式相关的规则要关干净让 Prettier 成为唯一的排版来源。两个工具各管一摊保存动作才能顺滑。4. IDEA / WebStorm 里 Prettier 格式化失效的完整排查链路4.1 失效场景还原配置填了格式化没动静JetBrains 系 IDEIDEA、WebStorm、PyCharm里接 Prettier和 VSCode 完全是两条路。VSCode 是装一个扩展让编辑器直接调用 Prettier而 JetBrains 的 Prettier 插件更像一个桥接器它需要指定 Node.js 解释器路径、Prettier 包路径然后把格式化动作翻译成 node 调用 prettier 的命令行操作。最典型的问题是插件启用了、配置也填了按 Ctrl Alt L 格式化结果代码纹丝不动或者变出来的风格跟预期差得远。很多人第一反应是插件坏了或.prettierrc 没生效但大部分情况下IDE 压根就没把 Prettier 当成格式化器来用。下面按步骤排查照着做基本能找到问题。4.2 排查第一步检查 Prettier 包路径是否指到了项目 node_modules打开 SettingsmacOS 上是 Preferences- Languages Frameworks - Prettier这里有四个关键选项。选项作用建议Node interpreter选择 Node.js 解释器路径默认即可但要确保 node 可用Prettier package选择 prettier 包路径必须指向项目 node_modules/prettier/index.jsOn Reformat Code action是否在快捷键格式化时使用 PrettiertrueOn save是否在保存时自动运行 Prettier按团队习惯开启On import导入代码时是否格式化false这里有个特别容易踩的坑Prettier package 如果选了全局安装的 prettier而不是项目 node_modules 里的那份版本差异会导致行为漂移。比如项目的 devDependencies 锁在 Prettier 2.x而全局装的是 3.x部分配置项的默认行为或者说解析逻辑已经变了格式化结果自然不一样。务必选择项目内的包不要贪图省事用全局路径。4.3 排查第二步确认文件类型和选中代码被 IDE 正确识别插件没生效还有一个常见原因IDE 的 Prettier 插件默认只处理它认识的关联文件类型。.js、.ts、.json 这些默认没问题但 .vue、.md、.css 等文件能不能被识别取决于 IDE 安装的语言插件和 Prettier 插件的匹配规则。如果你格式化的是一个 .vue 文件里的script块或者一个 .md 文件里的内联代码IDE 可能压根不认为这块内容归 Prettier 管按快捷键时就会走其他格式化器或者直接无操作。具体到选中的代码还有一个细节当代码嵌在 HTML 标签之间比如 JSP 里的内联 JS、Vue 模板里的表达式、或者模板字符串里的伪代码Ctrl Alt L 实际触发的是 HTML 语言的格式化器而不是 Prettier。这种情况不用纠结直接用右键菜单里的 Format With... 手动将当前选中内容交给 Prettier 执行。如果要长期处理这类文件考虑调整 IDE 对该文件类型的 Language Injection 识别让内联的 JS 段被正确当成 JavaScript 处理。4.4 排查第三步保存时自动格式化开关其实分两个从 VSCode 切过来的开发者很容易有一个惯性思维装好插件保存就自动格式化。但 JetBrains 插件里这个行为被拆成了两个独立开关——On Reformat Code action管快捷键操作On save管保存操作。只勾选了前者保存时就不会有任何反应反之如果只勾了 On save手动按快捷键时用的可能还是 IDE 内置格式化器。实用建议日常开发以保存触发为主把 On save 打开快捷键留给主动整理一下代码的场景。同时勾上 On Reformat Code action这样无论走哪条路结果都是 Prettier 的输出不会出现保存后格式和格式化后的格式不一致的诡异体验。4.5 排查第四步配置文件解析失败和 editorconfig 冲突再讨论一个相对隐蔽的问题Prettier 配置文件本身解析失败时IDE 插件可能静默降级。比如 .prettierrc 是 JSON 文件但里面带了尾逗号或注释命令行工具能容错处理IDE 插件的解析器却可能直接抛错结果就是你目睹格式化好像什么都没发生。遇到这种情况去 IDE 底部 Tool Window 打开 Prettier 插件的日志输出看有没有报错信息比盲猜有效得多。另外JetBrains 的 Prettier 插件在读取缩进配置时会优先参考 .editorconfig 的 indent_style 和 indent_size。如果项目里同时存在 .editorconfig 和 .prettierrc且两边的缩进设置不一致最终效果很可能不符合 .prettierrc 的预期。团队里这两份配置务必对齐或者干脆明确 Prettier 全权接管格式.editorconfig 只负责字符集、换行符这类基础属性。5. 团队格式化模板怎么搭从单机配置升级到仓库级规范5.1 先用 .editorconfig 打底把所有编辑器拉回同一起跑线如果团队要从零搭建一套开箱即用的格式化模板第一步不是急着写 .prettierrc而是先放一个 .editorconfig。这个文件的价值在于即使有人没装任何格式化插件他的编辑器也会因为 .editorconfig 的存在而采用相同的基础行为比如编码、缩进、换行符。它是格式统一的最底线。一个常见的 .editorconfig 长这样root true [*] charset utf-8 end_of_line lf insert_final_newline true indent_style space indent_size 2 trim_trailing_whitespace true注意 Prettier 自己也会读取 .editorconfig 中的缩进和换行设置作为部分选项的兜底来源。这个特性方便但也容易造成隐式依赖。我的建议是把关键格式项在 .prettierrc 里显式写全不要暗示editorconfig 配好了 Prettier 就会按那个走两份配置各司其职还能互相兜底团队换人时也不会因为少看一个文件而产生理解偏差。5.2 可以直接抄的团队推荐配置模板下面是一套我认为适合大多数前端团队的 Prettier 配置已经在多个项目中实测过可直接复制使用{ printWidth: 100, tabWidth: 2, useTabs: false, semi: true, singleQuote: true, quoteProps: as-needed, jsxSingleQuote: false, trailingComma: all, bracketSpacing: true, bracketSameLine: false, arrowParens: always, proseWrap: preserve, htmlWhitespaceSensitivity: css, vueIndentScriptAndStyle: false, endOfLine: lf, singleAttributePerLine: false, overrides: [ { files: *.md, options: { printWidth: 80, proseWrap: preserve } }, { files: [package.json, *.json], options: { printWidth: 200 } } ] }挑选原则还是那句每个选项都值得在评审时过一遍。printWidth 100 是很多团队的折中方案既不挤也不散singleQuote true 减少转义和视觉噪音trailingComma all 配合代码评审工具能看到更干净的 diff。这套模板本身不复杂难的是让每个成员理解它而不是盲目复制——有人不理解 semi 为什么要 true下次就可能为了满足个人偏好改回 false造成配置漂移。5.3 用 husky lint-staged 把格式化变成提交门禁配置写得再好不落地执行就是废纸。最可靠的强制手段不是 IDE而是 Git 提交钩子。在提交前对暂存区文件执行 Prettier形成了格式不过关根本进不了仓库的自动化门禁。团队里任何一个人提交代码都会先被格式化一遍这比 review 时提醒你格式化一下要便宜太多。按这个思路安装依赖并初始化钩子npm install --save-dev prettier lint-staged husky npx husky init然后在 package.json 中添加脚本和 lint-staged 配置{ scripts: { format: prettier --write ., format:check: prettier --check . }, lint-staged: { *.{js,ts,jsx,tsx,vue,json,css,scss,md}: [ prettier --write ] } }最后在 .husky/pre-commit 文件里写入npx lint-staged这样每次 commit 只处理暂存区里受影响的文件不会把全量格式化强塞进某个提交里。CI 里还可以加一道npm run format:check防线防止有人绕过本地钩子把未格式化的代码推到远端。三条线一拉格式问题基本进不了主分支。5.4 存量项目的迁移节奏先增量不返工再慢慢统一历史文件最后聊一个团队最容易纠结的问题老项目已经写了好几年格式五花八门直接全量格式化一次会怎样产生的 diff 大到 review 没人看merge 冲突多到让人崩溃而且会彻底刷掉所有 git blame 历史。这种迁移不能一步到位我见过相对稳的办法是增量约束法。第一步把工具链和配置先定下来PR 合入后立刻在 CI 里加prettier --check让所有新提交都过新格式。第二步对存量文件采取谁改动谁格式化的策略——不管谁碰了某个文件顺手对那个文件执行一次prettier --write格式化跟着功能改动一起进仓库。第三历史文件不用着急一次整理等它被频繁改动时再处理反正那些冷文件也不参与协作。这个节奏下三个月左右热文件的格式就统一了团队不会经历任何一次被格式化浪潮淹没的阵痛。如果历史文件实在重要配合 Git 的.git-blame-ignore-revs机制把一次性格式化提交标记为忽略git blame 立刻恢复可用这个细节很多团队都会漏掉。最后分享一点个人体会格式化工具的收益从来不是在接入第一天看到的而是在三个月后当你发现 review 时间明显变短、git blame 恢复干净、新人两天就能跟上风格的时候才真正体现出来。别急着一步到位把规范和门禁打好剩下的交给时间。
返回列表