代码格式化实战中Prettier与ESLint协同配置的关键细节
我的写法,亲测靠谱
我用 Prettier + ESLint + Husky 这套组合,在三个中大型 React 项目里跑了两年多,基本没再手动格式化过代码。不是因为懒,是实在被人工对齐、分号争议、缩进混乱搞怕了。
核心配置就这三行,放在 .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.js、webpack.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。如果格式化工具自动把对象属性重排,可能会把 userName 和 user_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 any 或 Object.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 配置的继承策略,欢迎评论区交流。这个话题我还会继续写,下篇打算聊聊“如何让新同学第一天就能跑通整套格式化流程”。

暂无评论