代码格式化实战中Prettier与ESLint协同配置的关键细节

Tr° 俊涵 工具 阅读 2,633
赞 8 收藏
二维码
手机扫码查看
反馈

我的写法,亲测靠谱

我用 Prettier + ESLint + Husky 这套组合,在三个中大型 React 项目里跑了两年多,基本没再手动格式化过代码。不是因为懒,是实在被人工对齐、分号争议、缩进混乱搞怕了。

代码格式化实战中Prettier与ESLint协同配置的关键细节

核心配置就这三行,放在 .prettierrc 里:

{
  "semi": true,
  "singleQuote": true,
  "tabWidth": 2,
  "trailingComma": "es5",
  "printWidth": 80,
  "endOfLine": "lf"
}

注意:我坚持 "semi": true。别听某些人说“现代 JS 不需要分号”,真到了多行解构 + 自动插入分号(ASI)的边界场景,比如下面这种:

const user = getUser()
[status, data] = parse(user) // 没分号?直接报错 ReferenceError

我踩过两次坑,一次是本地跑得好好的,CI 上挂了;另一次是合并 PR 后某函数突然 undefined,查了半小时才发现是上一行结尾少了分号,被 ASI 错误吞掉导致下一行变成数组访问——这种问题根本不会在 ESLint 里报警,只有运行时崩。

所以我的原则是:不赌 ASI,不省分号,不给团队埋静默雷。

这几种错误写法,别再踩坑了

见过太多项目把格式化当成“装个插件就完事”,结果越用越乱。列几个高频翻车现场:

  • 在 .eslintrc 里硬塞 Prettier 规则:比如写 "prettier/prettier": "error" 然后一堆 "no-unused-vars": "warn" 并列。ESLint 和 Prettier 职责完全不同,前者管逻辑,后者管样式。混在一起等于让 ESLint 去干排版活,不仅慢,还容易冲突。我改过一个项目,删掉所有 prettier 相关规则,只留 eslint-config-prettier 关闭冲突项,CI 时长直接降了 40%。
  • 本地开了 Prettier 插件,但 .prettierrc 放错位置:有次同事把配置文件扔进了 src/ 目录下,结果 VS Code 只对 src 里的文件生效,jest.config.jswebpack.config.js 全部格式化失能。后来统一放项目根目录,加了 .editorconfig 做兜底:
root = true

[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

[*.js]
indent_style = space
indent_size = 2

EditorConfig 不依赖任何编辑器插件,Git 提交前也能靠 editorconfig-checker 验证,比纯靠 IDE 更稳。

  • 用 Prettier 格式化 JSON 或 HTML 文件,却忘了关掉 auto-save on format:VS Code 默认开启这个选项,结果你双击打开 package.json 改个版本号,保存时它顺手把整个文件重排成单行(因为 printWidth=80)。我建议:"editor.formatOnSave": false,改用 Ctrl+Shift+I 手动触发,或者只对特定后缀启用:
"[json]": {
  "editor.formatOnSave": true,
  "editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[javascript]": {
  "editor.formatOnSave": true,
  "editor.defaultFormatter": "esbenp.prettier-vscode"
}

实际项目中的坑

最头疼的是和后端共用的 API 响应结构。我们前端用 TypeScript 写接口定义,但后端返回的字段名是 snake_case,比如 user_name。如果格式化工具自动把对象属性重排,可能会把 userNameuser_name 混淆(尤其当两者都存在时)。我的解法是:在 tsconfig.json 里加 "noUnusedLocals": true,再配合 ESLint 的 @typescript-eslint/no-unused-vars,逼自己显式声明转换逻辑:

interface ApiUser {
  user_name: string;
  created_at: string;
}

interface FrontendUser {
  userName: string;
  createdAt: string;
}

function transformUser(api: ApiUser): FrontendUser {
  return {
    userName: api.user_name,
    createdAt: api.created_at,
  };
}

这样 Prettier 会按字母序重排对象属性,反而帮我看清字段映射是否完整。但如果用 as anyObject.keys() 动态取值,格式化就救不了你——它不检查语义,只管语法。

另一个真实坑点:CSS-in-JS。比如用 styled-components,写了:

const Button = styled.button
  padding: ${props => props.size === 'large' ? '16px' : '8px'};
  background: #333;
;

Prettier 默认会把 template literal 拆成多行,但 styled-components 的解析器对换行敏感,有时会导致 CSS 解析失败。我的方案是:在 .prettierrc 加一行 "embeddedLanguageFormatting": "off",关掉 JS 中模板字符串的格式化,手动维护可读性。虽然不够全自动,但比半夜线上按钮样式崩掉强。

结尾

以上是我踩坑两年总结出来的代码格式化最佳实践。没有银弹,也没有“一劳永逸”的配置,只有不断根据团队节奏、项目体量、协作习惯去微调。Prettier 是工具,不是教条;ESLint 是守门员,不是裁判员;Husky 是保险丝,不是免死金牌。

这套组合现在在我手头的项目里跑得挺稳,偶尔还有小问题,比如某个 JSX 属性被 Prettier 强制换行后破坏了可读性,我就加个 // prettier-ignore ——不完美,但够用。

如果你有更好的方案,比如怎么优雅处理 GraphQL SDL 文件格式化、或者 monorepo 下多个 prettier 配置的继承策略,欢迎评论区交流。这个话题我还会继续写,下篇打算聊聊“如何让新同学第一天就能跑通整套格式化流程”。

本文章不代表JZTHEME立场,仅为作者个人观点 / 研究心得 / 经验分享,旨在交流探讨,供读者参考。
发表评论

暂无评论