Prettier 能做的和做不到的——7 类问题格式化救不了

· 约 4 分钟 🪄 代码格式化

“代码格式化了还报 bug?“——Prettier 不是万能的。它只调整字符间距,不懂你代码在做什么。下面 7 类问题格式化工具救不了,得靠别的手段。

Prettier 能解决的

先说能的,对比着看才清楚边界:

  • 引号:'abc'"abc"
  • 缩进:tab 和空格、2 空格和 4 空格
  • 分号:JS 语句末尾加不加
  • 尾逗号:数组 / 对象 / 函数参数
  • 换行:行超长自动折行
  • 空格:if(x)if (x)a:1a: 1
  • 对齐:多行对象键值对齐
  • 属性顺序(部分语言)

所有这些都是纯排版问题,不涉及语义。

1. 重复的 object key

const config = {
  port: 3000,
  host: 'localhost',
  port: 4000,         ← 重名,后面覆盖前面
};

Prettier 按原样输出,因为语法合法。ESLint 规则 no-dupe-keys 会报错。

JSON 更危险——很多 parser 默认后者覆盖前者,但部分 schema validator 把它当错误。手写配置文件时最容易踩。

2. 未使用的变量 / import

import { foo, bar } from './lib';  ← bar 从没用

function calc(x, y) {              ← y 从没用
  return x * 2;
}

Prettier 原样留着。ESLint 的 no-unused-vars 和 TypeScript 的 noUnusedLocals 管这个。IDE 里通常显示灰色。

注意:下划线开头的变量名 (_unused) 按惯例表示”故意不用”,不同 lint 规则对它的处理不同。

3. 语义重复的条件

if (x > 0 && x > 0) { ... }        ← 条件重复
if (a === b || a === b) { ... }    ← 条件重复
if (x) { ... } else if (x) { ... } ← else if 永不触发

Prettier 老老实实输出。ESLint 的 no-dupe-else-ifno-dupe-boolean-conditions 能抓出来。

4. 永远触发 / 永不触发

if (true) { ... }           ← 永真
if (false) { ... }          ← 永假,dead code
while (1) break;            ← 结构没错但意图可疑
const x = 5;
if (x < 0) { ... }          ← const 已知为正数

Prettier 原样输出,TypeScript 的 narrowing 能识别部分,ESLint 的 no-constant-condition 能抓字面量。

5. JSX 属性顺序 / 重复

<input
  type="text"
  value={v}
  type="email"重复
  onClick={f}
  onClick={g}     ← 重复
/>

Prettier 不报错——JSX 规范允许重复属性(后者覆盖前者)。ESLint 的 react/jsx-no-duplicate-props 必备。

6. CSS 变量 / 选择器错误

:root {
  --brand: #336699;
}

.btn {
  color: var(--brandd);      ← 变量名拼错,无默认值=无效
  background: #gggggg;       ← 非法颜色值
  padding: 10;               ← 缺单位(合法但通常是 bug
}

Prettier 只管缩进和空格,拼写错误完全不管。Stylelint 的 declaration-property-value-allowed-listcolor-no-invalid-hex 能查。

padding: 10 在 SVG 或某些 CSS 属性里合法(无单位数字),在普通布局里往往是 bug。

7. TypeScript 类型问题

const age: string = 30;           ← 类型不匹配
function greet(name: string) {
  return `Hello ${nane}`;         ← 拼错变量名
}
interface User {
  id: number;
  id: string;                      ← 接口重复字段
}

Prettier 完全无视——它在排版层,不做类型检查。这是 tsc --noEmit / astro check 的职责。

工具链分工

大项目的典型配置:

工具职责触发时机
Prettier排版保存时自动 / pre-commit
ESLintJS / TS 质量pre-commit / CI
StylelintCSS 质量pre-commit / CI
TypeScript类型检查IDE 实时 / CI
Biome / oxc一体化(替代)新项目可选
husky + lint-staged触发器git commit 时

冲突处理:Prettier 和 ESLint 在”引号 / 分号 / 缩进”上会打架。装 eslint-config-prettier,它会关掉 ESLint 里所有与 Prettier 冲突的规则,避免来回格式化。

一次 lint 搭建顺序

  1. 先装 Prettier:统一排版,消除”好看不好看”的争吵
  2. 再装 ESLint:抓 bug,别管排版
  3. eslint-config-prettier:两者和平共处
  4. 装 husky + lint-staged:commit 前自动跑,坏代码进不了仓库
  5. CI 加 prettier --checkeslint:PR 必须过

顺序反过来(先 ESLint 后 Prettier)会长期打架。

压缩模式:何时用

Prettier 的”压缩/压扁”本质是去空白,不是混淆。生产环境真正的 minify 要用:

  • JS/TS:esbuild / terser / swc
  • CSS:cssnano / lightningcss
  • HTML:html-minifier-terser

Prettier 的”压缩”只是开发过程中偶尔”一行化”用的,别当生产工具。

本地跑一下

粘入任意 JS/TS/JSON/CSS/HTML/Vue/MD/YAML/SQL/XML,一键格式化或压缩,多文件批量 ZIP 导出。本地运行不上传——公司代码、私有 schema 都能安全处理。

❓ 常见问题

Prettier 和 ESLint 有什么区别?

一句话——Prettier 管排版(空格、换行、引号风格),ESLint 管代码质量(未使用变量、潜在 bug、API 误用)。两者互补:Prettier 解决"长得难看",ESLint 解决"写得不对"。现代项目两个都要装,用 eslint-config-prettier 关掉 ESLint 里与 Prettier 冲突的规则。

Prettier 可以禁用某个文件吗?

可以。.prettierignore 文件(和 .gitignore 语法一样)列出要跳过的路径;单个文件里加 // prettier-ignore 注释(上一行)可跳过下一行;JSX 用 {/* prettier-ignore */};CSS 用 /* prettier-ignore */。全局关闭不推荐——项目风格不一致反而更乱。

为什么 Prettier 不让配置更多规则?

设计哲学。Prettier 创始人 James Long 刻意限制配置项数量(大概 20 个左右),避免团队陷入"空格 2 还是 4"的口水战。目标是"决策交给工具"而不是"讨论风格"。想定制太多风格,用 dprint 或自己写 eslint 规则,但团队效率一般会下降。

Prettier 会不会改坏代码语义?

绝大多数情况下不会。Prettier 基于 AST 处理,只调整 token 间的空白。少数边界情况:模板字符串里的 tabs、正则字面量内部、含特殊分号的 CSS(IE hack)。生产环境建议配 CI 检查 prettier --check,而不是 pre-commit 直接写——万一格式化坏了还能 diff 追溯。

🪄 打开 代码格式化 Prettier 驱动·JS/TS/JSON/CSS/HTML/Vue/MD/YAML/SQL/XML·美化/压缩·本地运行