news 2026/9/20 23:39:41

Prettier 与 Linter 的分工边界:格式化交给 Prettier,Bug 检测交给 Linter

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Prettier 与 Linter 的分工边界:格式化交给 Prettier,Bug 检测交给 Linter

Prettier 与 Linter 的分工边界:格式化交给 Prettier,Bug 检测交给 Linter

【免费下载链接】prettierPrettier is an opinionated code formatter.项目地址: https://gitcode.com/gh_mirrors/pr/prettier

Prettier 是"有主见"(opinionated)的代码格式化器,它从不试图替代 ESLint、TSLint、stylelint 等 linter,两者分属完全不同的职责。本文基于仓库中 comparison.md(即网站版本化文档 website/versioned_docs/version-stable/comparison.md)展开,先拆解 linter 规则的两大类别,再从 Prettier 源码层面解释"从头重印整个程序"为何能让格式化规则彻底"失业",最后给出--check落地 CI 以及与 ESLint/stylelint 集成的最佳实践。读完你将明确团队中 Prettier 与 linter 各自该管什么、如何配置才不会互相冲突。

Linter 规则的两大类别

文档开篇就给出了一个非常清晰的框架:linter 的规则可以分成两类,而 Prettier 只与其中一类发生关系。

格式化规则(Formatting rules)

这类规则关心的是"代码长得怎么样",例如:

  • max-len:限制单行最大长度;
  • no-mixed-spaces-and-tabs:禁止混用空格与制表符缩进;
  • keyword-spacing:统一关键字(如ifreturn)两侧的空格;
  • comma-style:统一逗号的书写位置(行尾还是行首)。

Prettier 让这一整类规则全部变得不再必要。因为 Prettier 会把整个程序从头重新打印一遍(reprint the entire program from scratch),并以完全一致的方式输出,程序员根本不可能再在这种地方犯错——空格放错位置、换行不一致、逗号风格不统一这类问题,在 Prettier 的模型里压根不会产生。

这里的关键在于 Prettier 的定位:

Prettier is not a rule-based tool: it has no individual formatting rules that you can enable or disable. It reprints your whole program from scratch instead.

Prettier 不是基于规则的工具,它没有一条条可以单独开启或关闭的格式化规则。这也正是它与其他所有"样式检查器"在架构层面最根本的分野。

代码质量规则(Code-quality rules)

这类规则关心的是"代码有没有问题",例如:

  • no-unused-vars:发现声明了却从未使用的变量;
  • no-extra-bind:检测多余的bind调用;
  • no-implicit-globals:防止在全局作用域意外创建隐式全局变量;
  • prefer-promise-reject-errors:要求Promise.reject必须传Error对象。

Prettier 对这类规则毫无帮助,也无意帮助。文档明确指出,这类规则恰恰是 linter 提供的最重要的能力——因为它们很可能真的帮你抓到代码里的 Bug。格式化类规则抓不到逻辑错误,而代码质量规则能。

所以结论可以浓缩成一句话:

用 Prettier 管格式化,用 linter 抓 Bug!

"从头重印"不是口号:看 Prettier 源码里的真实管线

要理解为什么 Prettier 能让格式化规则"集体失业",最好的方式是从源码看它到底是怎么工作的。Prettier 的格式化主流程位于 src/main/core.js 的coreFormat函数,其核心三步恰好就是"从头重印"的实现证据:

  1. 解析parseText(originalText, opts)将源码解析成抽象语法树(AST);
  2. 转 DocprintAstToDoc(ast, opts)遍历 AST,把它转换成一种称为 Doc 的中间表示。Doc 由 src/document/builders/ 目录下的groupindentlineif-break等构建器拼装而成,它描述的是"这段代码在不同宽度下应该怎么断行、怎么缩进";
  3. 打印printDocToStringWithoutNormalizeOptions(doc, opts)由 src/document/printer/printer.js 负责,把 Doc 最终渲染成字符串输出。

注意第三步的输入不再是你写的原始文本,而是由 AST 生成的 Doc。换句话说,你的输入只是被"解析"后当作语义来源,最终输出的样式完全由 Prettier 的打印器决定——这就是文档所说"reprints your whole program from scratch"的落地实现。

由此可以引申出几个推论:

  • 只要 AST 相同,写法千奇百怪的代码都会被重印成同一种样子,因此max-lenkeyword-spacingcomma-style这类规则永远"无案可查";
  • Prettier 没有"规则开关"的概念,因为它根本没有逐条生效的规则表——整个打印策略是一体的,这也决定了你无法只保留某一种格式化行为而关闭另一种;
  • 正因为是整体重印,Prettier 能保证输出再次格式化后保持不变(幂等性)。CLI 的--debug-check正是利用这一性质做自检:它会把结果再格式化一遍并与第一次输出比较,若不一致即报错(见 src/cli/format.js)。

代码质量检测:Linter 不可替代的职责

既然 Prettier 对代码质量规则"什么都不做",那么 lint 仍然是你工程体系里的刚需。no-unused-vars能抓住删除重构后遗留的死变量,no-extra-bind能提示不必要的性能开销,prefer-promise-reject-errors能阻止吞掉错误堆栈的坏习惯——这些都是格式化工具永远无法提供的能力。

文档对此的措辞非常坚定:代码质量规则是 linter 提供的最重要的规则,因为它们"likely to catch real bugs"。所以不要试图用 Prettier 的配置文件去"模拟"这些检查,Prettier 的选项空间里也根本没有它们的位置——这恰好与 option-philosophy.md 中"Prettier 选项已被冻结、不再接受新增选项请求"的原则一致。格式化争议应该被消除,而不是被转移到"选哪个选项"上;而 Bug 检测则始终是 ESLint 这类工具的专属战场。

落地实践:CI 中如何用--check守好格式化这条线

分工清楚了,剩下的问题就是怎么执行。在 CI 里,你不应该让 Prettier"改"代码,而应该让它"检查"代码并让构建失败——这正是prettier --check的用途,配合prettier --write在本地一键修复。

以仓库中的 CLI 实现为例,src/cli/format.js 的listDifferent函数会调用prettier.check(input, options),而 API 层的实现极简,见 src/index.js:

async function check(text, options) { return (await format(text, options)) === text; }

即"格式化后与原文本是否完全相等"。由此可总结出--check的完整行为特征(依据 src/cli/format.js):

场景CLI 输出进程退出码
所有文件都已符合 Prettier 风格All matched files use Prettier code style!0
存在未格式化的文件逐行列出文件名,并提示Code style issues found in X files. Run Prettier with --write to fix.1
存在解析错误 / 无法识别的文件输出错误信息2

基于此,一个典型的 CI 检查命令是:

npx prettier --check .

而本地修复则运行:

npx prettier --write .

配合.prettierignore(见 ignore.md)排除不需要格式化的目录,以及 configuration.md 中的配置文件(如.prettierrcprettier.config.js),Prettier 就能在你打开 PR 之前把格式问题全部拦截在本地。更完整的 CLI 参数说明可参考 cli.md。

与 Linter 的三种集成姿势

文档在 integrating-with-linters.md 中系统梳理了让 Prettier 与 linter 共存的三种方案,按推荐程度排序:

1. 官方推荐:eslint-config-prettier(关闭冲突规则)

linter 里绝大多数风格类规则在使用 Prettier 后不仅多余,更糟糕的是它们可能与 Prettier 冲突——Prettier 刚把代码重印成自己的风格,linter 的样式规则可能又判它违规,形成互相打架的死循环。eslint-config-prettier的作用就是一次性关闭所有与 Prettier 冲突或已无必要的规则。这是官方推荐路线,配置一次即可长期省心。

2. 不推荐但存在:eslint-plugin-prettier / stylelint-prettier(把 Prettier 当 lint 规则跑)

这类插件让你在 linter 内部"以 lint 规则的形式"运行 Prettier。在 Prettier 诞生初期,这种方案可以复用已有的 linter 编辑器集成、不用搭新基础设施,确实有用;但如今可以直接运行prettier --check .,且大多数编辑器已原生支持 Prettier,这类插件的价值已经大打折扣。文档明确指出它们的缺点:

  • 编辑器里会出现大量红色波浪线,非常烦人——Prettier 的本意是让你忘记格式化的存在,而不是成天提醒你;
  • 比直接运行 Prettier 慢;
  • 多了一层间接层,多了一个可能出错的地方。

3. 兜底方案:prettier-eslint / prettier-stylelint(先格式化再 lint)

这类工具先跑prettier,紧接着对输出再跑eslint --fix。如果你对 Prettier 输出的某些方面实在无法接受,可以用eslint --fix再修一轮。缺点是这类工具比单纯运行 Prettier慢得多,应视为特殊情况下的兜底而非常规路线。

总结:一条清晰的分工边界

回到 comparison.md 的核心结论:Prettier 负责格式化,linter 负责抓 Bug

  • 格式化规则(max-lenno-mixed-spaces-and-tabskeyword-spacingcomma-style等):交给 Prettier,从源码管线看,它会解析成 AST、生成 Doc 并整体重印输出,让你无从犯错;
  • 代码质量规则(no-unused-varsno-extra-bindno-implicit-globalsprefer-promise-reject-errors等):交给 linter,因为它们能抓到真实 Bug;
  • 落地时用prettier --check进 CI、prettier --write修本地,用eslint-config-prettier关闭冲突的风格规则,避免两套工具互相打架。

这条边界背后,是 Prettier "选项越少越好、格式化战争越少越好"的 option-philosophy.md 设计哲学,以及"为团队终结无休止的风格争论"的初心(见 why-prettier.md)。理解了它,你就理解了 Prettier 在整套工程质量体系中的准确位置——它不抢 linter 的饭碗,它只是让 linter 更专注地去做真正重要的事。

【免费下载链接】prettierPrettier is an opinionated code formatter.项目地址: https://gitcode.com/gh_mirrors/pr/prettier

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!