从什么时候开始,前端项目“格式化”变成了个需要开会讨论的事?我印象最深的一次,是团队里三个人写同一个组件库,有人用单引号,有人用双引号,有人喜欢加分号,有人觉得分号是噪音。Git 提交记录里一半是“fix style”,Code Review 里最热闹的评论永远是“这里该加个空格”。后来我把 ESLint 和 Prettier 这套组合装进项目,提交前自动检查、保存时自动修复,规则直接锁死在配置文件里,争论瞬间消失,提交记录干净了,新人进来也不再问“你们的缩进是几个空格”这种问题了。
这篇文章不聊虚的,就是一套可以直接抄走的实战方案:ESLint 做代码质量检查,Prettier 做代码统一格式化,两者搭配使用,再配合 Husky 和 lint-staged 在 Git 提交前自动把关。不管你是正在搭建前端工程化的新手,还是被团队代码风格折磨到崩溃的老手,这套组合都能让你的项目“风格自治”。先说清楚这两兄弟的分工,再一步步把工具链配起来,最后给你一份排坑实录。
1. ESLint 和 Prettier 到底解决什么问题:先搞清楚分工再动手
很多新手会有个困惑:ESLint 不是能格式化代码吗?为什么还要装 Prettier?这俩功能不是重叠的吗?这是个特别常见的问题,但答案其实很简单——它们的核心职责完全不一样。
1.1 一个是“纪律委员”,一个是“排版工人”
ESLint 的角色更像团队里的纪律委员,它管的是“代码对不对”。比如你定义了一个变量但从来没用过,它会报错;你在循环里改了循环变量的值,它也能查出来;你用了==而不是===,它会提醒你有安全隐患。这些属于代码质量和潜在 bug 的范畴,ESLint 就是干这个的。
Prettier 的角色则是个偏执狂级别的排版工人,它只关心“代码长得帅不帅”。字符串用什么引号、行尾加不加分号、换行时怎么缩进、一行最多多少列——这些纯风格问题,Prettier 会用一套固定规则把代码重新“打印”一遍,输出结果在任何机器上都是一模一样的,没有任何主观判断空间。
简单记忆:ESLint 查“错”,Prettier 管“美”。前者抓的是逻辑隐患,后者治的是风格分裂。
1.2 为什么两者必须搭配使用
这里要纠正一个误区:ESLint 确实自带一部分格式化能力,比如quotes规则可以强制单引号或双引号,semi规则可以强制加不加分号。但问题是,ESLint 的强项是“找问题”,它的格式化规则覆盖范围有限,而且不同规则的配置粒度很细,细到你为了一件小事要写一大堆配置。
Prettier 则完全不同,它不需要你逐条声明该怎么格式化,它内置了一套经过大量社区验证的默认风格,你只需要在.prettierrc里改几个关键开关。它的格式化能力是全量覆盖的,从 JSX 属性换行到 Markdown 表格对齐,全都拿捏得死死的。
最合理的分工是:让 Prettier 负责所有风格问题,再用 ESLint 负责代码质量和潜在错误检测。这样一来,两个工具各司其职,你不会在 ESLint 里为了对齐引号风格浪费精力,也不用担心 Prettier 帮你写了错代码。
1.3 多人协作时这套组合真正解决的问题
实际项目里,代码风格混乱从来都不是技术问题,是协作成本问题。举个我遇到过的真实场景:项目里有个老同事,标准的“加分号党”,提交的代码每个语句结尾都带分号;另一个新同事是从 Python 那边转过来的,习惯无分号风格。两个人改同一个文件时,Git 的 diff 里全是引号和分号的改动,真正的逻辑改动反而被淹没在一堆“风格漂移”里。
Code Review 的时候,评审人被迫在一堆格式噪音里找真正的 bug,效率极低。更现实的是,这种格式争论会消耗团队情绪,本来该讨论的是实现方案,结果每次都在“该不该加分号”这种破事上吵半小时。
ESLint + Prettier 这套组合的核心价值就在这里:把风格决策从“人”手里拿走,交给工具。规则定好之后,谁都不需要再争了,机器说了算。提交之前 check 一下,不规范根本不允许提交,省下的时间拿去写业务,不香吗?
2. 从零搭建:初始化项目与安装工具链
接下来直接进入实操环节。我用目前最典型的项目场景来演示:Vite + React + TypeScript。这套组合的本质其实不绑定任何框架,换成 Vue、Svelte 或者纯 Node 项目原理完全一致,只是插件名会有些变化。
2.1 创建项目与安装依赖
先创建一个 Vite + React + TS 项目,这一步大家都会,就不多啰嗦了:
npm create vite@latest my-project -- --template react-ts cd my-project npm install然后安装 ESLint、Prettier 以及相关插件:
npm install -D eslint prettier npm install -D typescript-eslint @eslint/js npm install -D eslint-plugin-react-hooks eslint-plugin-react-refresh npm install -D eslint-config-prettier这里每个包都是有明确职责的:
eslint:核心检查引擎prettier:核心格式化引擎@eslint/js:ESLint 官方推荐的 JavaScript 规则集typescript-eslint:让 ESLint 能理解 TypeScript 语法,并提供 TS 专属规则eslint-plugin-react-hooks:React Hooks 规则检查,比如 hooks 的依赖数组规范eslint-plugin-react-refresh:React Fast Refresh 相关检查,Vite 项目标配eslint-config-prettier:这个很关键,它的作用是把 ESLint 里和 Prettier 冲突的格式化规则全部关掉,避免两兄弟打架
注意:如果你用的是 Vue 项目,把
react-hooks和react-refresh替换成eslint-plugin-vue即可。原理一致,插件不同。
2.2 版本选择:我为什么要站在 ESLint 9 和 Prettier 3 这边
写这篇文章的时候,ESLint 已经到了 9.x 版本,配置文件默认是 flat config(也就是eslint.config.js),以前的.eslintrc那套配置方式已经被官方标记为废弃。网上大量教程还停留在旧版 eslintrc 的写法,新手照着抄经常会遇到ESLintError: .eslintrc is no longer supported之类的问题,非常劝退。
所以我这边直接用 flat config 来讲,这套配置在当前和未来一两年内都是主流。
Prettier 3.x 没有什么颠覆性的配置变更,但它把配置文件解析、CLI 输出这些底层能力做了不少优化,而且和 ESLint 的兼容性处理得更干净。直接用最新稳定版就好。如果你是为新项目搭建,版本选eslint@^9、prettier@^3,没有历史包袱。
2.3 基础文件结构
安装完依赖之后,在项目根目录创建这些文件:
├── .prettierrc ├── .prettierignore ├── eslint.config.js ├── package.json └── src/等一下,这里有一个细节我必须强调:新版 ESLint 的 flat config 只有一个入口文件,那就是eslint.config.js,以前常见的.eslintrc.js和eslintConfig(package.json 里的字段)都不再被读取了。你这个项目如果是新搭建的,就老老实实新建eslint.config.js,别再去网上找老教程了。
3. 核心配置:ESLint 的 flat config 与 Prettier 的无缝配合
配置环节是整个工程化的核心,也是大多数人容易踩坑的地方。我先把完整配置贴出来,再逐块解释每一段在干什么,为什么这么写。
3.1 先说说 ESLint 9 的 flat config 是怎么回事
flat config 是 ESLint 从 9.0 开始默认的配置系统,它把旧版基于“继承 extends”的写法改成了“纯对象数组”的写法。每个配置对象都明确告诉你:这些文件用这些规则。没有继承链的魔法,没有配置项的隐式叠加,一切都在一个数组里平铺。
好处是配置可预测性大幅提升。你看到的配置就是最终生效的配置,不会再遇到那种“我在 A 配置里 extends 了一下,结果规则被某个依赖覆盖成奇怪行为”的玄学问题。坏处?唯一的坏处是你需要习惯这种新写法,但一旦理解了其实比旧版简单太多。
3.2 配置文件详解:一份能直接落地的 eslint.config.js
下面是我实际项目里在用的一份配置,做了一些简化,但核心结构完全一致:
import js from '@eslint/js'; import globals from 'globals'; import reactHooks from 'eslint-plugin-react-hooks'; import reactRefresh from 'eslint-plugin-react-refresh'; import tseslint from 'typescript-eslint'; import prettier from 'eslint-config-prettier'; export default tseslint.config( { ignores: ['dist', 'node_modules'] }, { files: ['**/*.{ts,tsx}'], extends: [ js.configs.recommended, ...tseslint.configs.recommended, ], languageOptions: { ecmaVersion: 2020, globals: { ...globals.browser, ...globals.node, }, }, plugins: { 'react-hooks': reactHooks, 'react-refresh': reactRefresh, }, rules: { ...reactHooks.configs.recommended.rules, 'react-refresh/only-export-components': [ 'warn', { allowConstantExport: true }, ], }, }, prettier, );这个配置里每个部分都有讲究。tseslint.config()是个封装方法,它把 TypeScript ESLint 的配置项做了一层扁平化合并,比手动展开数组要省事不少,还能处理配置覆盖的优先级问题。
{ ignores: ['dist', 'node_modules'] }是全局忽略配置,告诉 ESLint 别去检查构建产物和依赖目录,不然跑一次检查要扫几万个文件,速度慢还没意义。
files: ['**/*.{ts,tsx}']指定这套规则只作用于 TypeScript 文件。如果你项目里还有.js文件,可以再加一个对象,或者用['**/*.{js,jsx,ts,tsx}']全部覆盖。
languageOptions里设置 ECMAScript 版本和全局变量。globals.browser把window、document、localStorage这些浏览器环境变量都声明了,不然 ESLint 会爆'window' is not defined这种误报。globals.node则让process、__dirname这类 Node 环境变量正常通过。这个配置在纯前端项目中很常用,因为 Vite 的配置文件本身也是 Node 环境下跑的。
plugins和rules部分是 React 项目专属配置。react-hooks的推荐规则会强制你正确使用useEffect、useMemo的依赖数组,react-refresh/only-export-components则检查你是否在组件文件里导出了非组件的内容,这个规则对 Vite 的 Fast Refresh 体验影响很大,导出一个常量函数会导致热更新失效,配置成 warn 能提前提醒你。
最后一行prettier,就是刚才说的eslint-config-prettier,它会把 ESLint 里所有跟格式化相关的规则全部关掉。这块太关键了,我单独拉出来讲。
3.3 Prettier 的核心参数选择与理由
Prettier 的配置文件放根目录,命名是.prettierrc,用 JSON 格式写。我用的配置如下:
{ "semi": true, "singleQuote": true, "printWidth": 100, "trailingComma": "all", "tabWidth": 2, "jsxSingleQuote": false, "arrowParens": "always", "endOfLine": "auto" }逐个解释一下这些参数的取舍:
semi: true:行尾加分号。这个配置在网上争议最大,但我的观点是加分号是默认安全选项。虽然现代 JavaScript 有 ASI(自动分号插入)机制,不加分号大多时候也不会出错,但在某些情况下,比如一行以[或(开头时,不加分号会导致上一行被错误解析。团队协作时,为了少数人觉得“好看”去承受这种不确定性,性价比不高。
singleQuote: true:字符串用单引号。这更多是行业审美趋势,也跟团队习惯有关。JS 社区对单引号的偏好度确实更高,而且 JSX 里的属性又强制用双引号,单双混用反而是某种程度上的自动分类,视觉上能区分“这是字符串”和“这是 JSX 属性”。
printWidth: 100:每行最多 100 列。Prettier 默认是 80,但 80 在现代宽屏显示器下太紧了,嵌套几层就疯狂换行,反而影响阅读。我个人经验是 100 是个不错的平衡点,既不会太长导致横向滚动,又不会频繁折行。如果你团队习惯比较保守或者经常多人 review,120 也完全可以,但这个参数决定了 Prettier 的换行策略,建议全团队保持一致后不要轻易改。
trailingComma: "all":所有函数参数、数组、对象等多行结构的结尾都加上逗号。这个参数我个人强烈推荐,原因很实用:git diff更干净。你在数组末尾加一项时,如果最后一项后面有逗号,diff 只会显示新增的一行;如果没逗号,还得顺手改上一行,diff 就变成两行。这种小优化在频繁改动数据配置的项目里体验差别巨大。
tabWidth: 2:缩进 2 个空格。这个是前端社区的绝对主流,不多解释。
jsxSingleQuote: false:JSX 属性保持双引号。配合上面说的单双引号分离策略,JSX 属性统一双引号,代码视觉上更清晰。
arrowParens: "always":箭头函数参数永远加括号。即使只有一个参数也不省略括号。这是为了避免“今天写x => x,明天加一个参数变成(x, y) => x + y,原来那个x => x也要跟着改”这种无意义 diff。
endOfLine: "auto":换行符自动识别。这个是 Windows + Mac 协作项目必备。Windows 默认 CRLF,Mac/Linux 是 LF,如果不处理这个参数,Prettier 会把整个文件的换行符全改一遍,Git diff 里全是^M符号,非常痛苦。
配置完之后,再建一个.prettierignore文件,跟 ESLint 的 ignores 功能一样,告诉 Prettier 哪些文件不需要格式化:
dist node_modules package-lock.json pnpm-lock.yaml.gitignore文件已经有这些目录了,但 Prettier 不认识.gitignore,必须单独声明。
3.4 解决 ESLint 和 Prettier 的规则冲突
很多人第一次同时跑 ESLint 和 Prettier 会碰到这种场景:ESLint 提示“字符串必须用双引号”,Prettier 却把代码统一成了单引号。两个工具的命令一前一后跑,代码被来回改,像两个人在编辑同一个文件打架。
解决方式就是eslint-config-prettier。它做的事情很简单粗暴:把 ESLint 内置规则里所有与 Prettier 冲突的规则全部关掉。因为风格问题已经交给 Prettier 了,ESLint 再用自己那套格式化规则去管就是多管闲事,还产生冲突。
配置方法非常轻量,在上面那段配置文件的数组里,把prettier作为最后一个配置对象传入即可。为什么必须放最后一个?因为 flat config 数组后面的配置会覆盖前面的配置,只有放在最后,它才能确保关闭规则的优先级最高。这一步就是“配置顺序第一坑”,很多人把prettier放在数组前面,结果冲突规则又被后面的配置覆盖回来了,白配了。
3.5 适配 TypeScript 和 React 的注意事项
TypeScript 的适配主要依赖typescript-eslint项目。它提供了两套规则集:recommended(推荐)和strict(严格)。默认推荐配置已经能拦住绝大多数与类型相关的常见问题,但如果你的项目对类型安全要求特别高,可以升级到strict模式,但要做好心理准备,初期会暴露出不少历史遗留的类型问题。
React 项目还有两个专属插件值得投入时间:
eslint-plugin-react-hooks的规则主要是管useEffect依赖的。它会在你遗漏依赖项时警告,比如你用了useEffect(() => { fetchData(id) }, []),但fetchData和id都没进依赖数组,它就会提醒你“请把 id 加进依赖数组”。这个检查能预防很多闭包过期的问题。
eslint-plugin-react-refresh则是在 Vite 项目里配合 Fast Refresh 用的。Fast Refresh 要求一个文件只导出 React 组件,如果在同一个文件里既导出了组件又导出了一个工具函数,热更新会退化成整页刷新,开发体验直线下降。这个插件就是提前帮你发现这类问题。
4. 自动修复与编辑器集成:把规范变成零成本
配置写完之后,接下来要解决的问题是:怎么让这套规范真正跑起来,并且在开发过程中自动生效,而不是等提交的时候才被拦截。
4.1 命令行自动修复命令
ESLint 提供--fix参数,可以直接自动修复所有能修复的问题:
npx eslint . --fixPrettier 也有对应的格式化命令:
npx prettier --write .--fix能修复的包括:未使用变量删除、变量定义改为const、字符串引号统一这些;但有一些问题它不会帮你修,比如逻辑错误、变量泄漏为全局这种需要人判断的问题。Prettier 的--write则是“全格式化”,它会对文件重新排版,改完基本整个文件的格式都统一了。
注意别把eslint . --fix和prettier --write的执行顺序搞反。先跑prettier --write格式化风格,再跑eslint . --fix修复可以自动修复的代码质量问题。反过来很容易出现 Prettier 把 ESLint 刚修好的格式又改回去的尴尬情况。当然实际开发中你不会频繁手动敲这两个命令,这就是下面编辑器集成的意义。
4.2 VSCode 保存时自动格式化
如果你用 VSCode(大多数人),强烈建议配置保存时自动格式化。安装两个扩展:ESLint 和 Prettier。
然后在项目根目录建一个.vscode/settings.json:
{ "editor.defaultFormatter": "esbenp.prettier-vscode", "editor.formatOnSave": true, "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit" } }这个配置实现了三件事:
editor.formatOnSave: true让编辑器在你 Ctrl+S 保存时自动运行 Prettier 格式化当前文件,这样不规范的文件一保存就被重排了。
editor.codeActionsOnSave.source.fixAll.eslint让编辑器在保存时自动执行 ESLint 的--fix操作。这个动作主要是为了修复 ESLint 能自动修的那些代码质量问题,比如自动导入、未使用变量清理等。
editor.defaultFormatter指定默认格式化工具是 Prettier,避免和其他格式化插件冲突。这里有个常见的坑:VSCode 默认格式化工具可能是 TypeScript 语言服务内置的,或者你装了其他格式化插件,多个插件同时接管格式化会导致“改完 A 又改 B”的现象,所以必须在项目级别明确指定。
4.3 在 package.json 里固化工作流
团队协作时,配置文件只能保证“如果你跑了我就能生效”,但没有人会主动记着在提交前手动跑两条命令。所以在package.json里固化 scripts 是很重要的一步:
{ "scripts": { "lint": "eslint .", "lint:fix": "eslint . --fix", "format": "prettier --write .", "format:check": "prettier --check ." } }lint和format:check是给 CI 在合并前跑的检查命令,确保代码合并进主干之前已经全部符合规范。lint:fix和format是给开发者本地开发时手动跑的。这套命令组合是团队协作基线,没有这两条命令,后面配的 Husky 也没法用。
5. Husky + lint-staged:提交前守住最后一道防线
配置再完美,总有人会绕过。有人会忘记配置编辑器,有人会直接跳过 IDE 里的警告,还有人会把提交信息写得乱七八糟。所以必须在 Git 提交这个唯一必经之路上设置关卡,强制校验。
5.1 为什么这个环节必不可少
没有 Git 钩子之前,代码风格检查属于“自觉行为”。你想查就查,不查也没人知道你提交的是不是一堆格式垃圾。问题往往是这样出现的:老同事跑过 lint 了提交没问题,新同事提交了个格式半残的文件,CI 只要不是配了检查就发现不了,等 Code Review 阶段被指出来又是一轮改代码。
Husky 的作用是把检查时机从“人想起来跑”变成“Git 操作自动触发”。当git commit命令执行时,husky 会先去执行你配置的钩子脚本,如果脚本返回非零状态码,提交直接终止,不规范的代码根本进不了 Git 仓库。这就是工程化的“强制力”所在。
5.2 安装并初始化 husky
Husky 从 9.x 版本开始,安装方式变得非常简洁:
npm install -D husky npx husky inithusky init会自动帮你做几件事:在项目根目录创建.husky/目录、生成一个pre-commit示例钩子、在 package.json 里添加"prepare": "husky"脚本。这个prepare脚本是关键,它会让安装了 husky 的项目在npm install之后自动激活 Git 钩子,新克隆项目的人跑一次 install,钩子就自动装上了,不需要手动做任何事。
然后编辑.husky/pre-commit文件,内容先简化为:
npm run lint npm run format:check但是,这一步先别急,直接这样写有个性能问题:每次提交都要全量检查整个项目,项目一大检查就要几十秒,开发者会被逼疯的。这就是 lint-staged 登场的原因。
5.3 配置 lint-staged 实现精准检查
lint-staged 的思路很聪明:只检查本次提交时发生变动的文件。你改了两个文件,提交时就只 lint 和 format 这两个文件,其他文件一概不动。这样既保证了提交内容都是干净的,又不会让开发者等太久。
安装:
npm install -D lint-staged然后在 package.json 里加一段配置:
{ "lint-staged": { "*.{ts,tsx,js,jsx}": [ "prettier --write", "eslint --fix", "eslint" ], "*.{json,css,md}": [ "prettier --write" ] } }这个配置的含义是:当 Git 暂存区里有.ts/.tsx/.js/.jsx文件时,按顺序执行prettier --write格式化、eslint --fix自动修复、最后再跑一次eslint做最终校验。如果最后一步校验失败,说明这个文件存在无法自动修复的问题,提交会被阻止,你需要手动处理。
为什么最后要再跑一次eslint?因为--fix只能修一部分问题,修完之后可能还有残留问题需要人工处理。如果不加最后这个校验,前面的--fix就只是“尽力而为”,残留的问题照样能进到仓库里。
然后修改.husky/pre-commit:
npx lint-staged这样每次提交时,只有暂存区内的文件会被执行格式化和代码检查。
5.4 提交前检查的实战坑点
lint-staged 第一次跑起来时会有几个小坑,我帮你提前踩掉:
第一个坑:lint-staged 原文件路径问题。如果你之前配置过 prettier 的--write时带了目录参数,比如prettier --write src/,在 lint-staged 里千万不要这么写。lint-staged 会自动把暂存的文件路径传递到命令末尾,你只需要写命令名。如果你写了目录参数,它会格式化整个目录,等于没做“只检查改动文件”的优化。
第二个坑:Windows 用户的 husky 钩子不生效。Husky 的核心机制是向.git/hooks写入 shell 脚本,Windows 上如果 Git 没有正确安装到系统 PATH,或者你使用的是某些第三方终端应用没有继承环境变量,钩子可能静默失败。遇到这种情况,先用npx husky手动激活一次,再看.git/hooks/pre-commit是否存在。如果还是没有效果,检查 git 版本,建议升级到 2.36 以上,husky 9.x 对 Windows 的兼容性在这个版本之后才有保障。
第三个坑:不要在生产环境跑 lint-staged。husky 生成的prepare脚本会在每次npm install时执行,如果你在 CI 里构建或者将来有人用npm install --production装依赖,prepare 脚本也会执行,但此时可能没有 devDependencies。建议在.npmrc或者 CI 配置里显式跳过 husky 安装,或者至少在服务端安装时带上--production=false,避免奇怪的报错。
第四个坑,也是我认为最重要的:提交前只检查,不自动改用户未暂存的文件。lint-staged 有个设计,它会用临时快照来工作,确保你已经git add过但还没提交的内容在检查时保持原样,这样你只需要 add 一次。但如果 lint-staged 自动修复了格式,修改后的文件会出现在工作区,但没有重新 add,此时提交会失败。怎么处理?初始化提交时先git add一遍,让 lint-staged 跑的时候所有改动都已经在暂存区里了,如果它改了文件,命令返回成功,但你本地还有未提交的格式改动,下一轮提交自然会带上。实际使用中体验稍微绕一点,习惯了就还好。
6. 常见问题与排查技巧实录
配置一套工具链,不遇到点问题是不现实的。我从自己项目和帮别人排障的经历里挑了几个高频问题,整理成一份速查手册。
6.1 规则冲突与配置不生效
问:ESLint 明明在配置里写了semi: false,为什么执行后分号还是被加上去了?
大概率是eslint-config-prettier被放到了配置数组的中间,后面的规则覆盖了前面的。上面已经强调过,prettier必须放在数组最后一位,这是 flat config 覆盖机制决定的。还有一个可能是你的 IDE 没有加载最新配置,重启一下 ESLint 服务或者重启 VSCode 就有奇效。
问:配置了 Prettier 的singleQuote: true,但 ESLint--fix后字符串全变成双引号了?
这说明你没有安装eslint-config-prettier,或者它配置的顺序不对。ESLint 自带的quotes规则默认强制双引号,Prettier 强制单引号,二者冲突。装好eslint-config-prettier并放最后,ESLint 的quotes规则就会关闭,冲突自然消失。
问:eslint.config.js里写的规则,在编辑器里完全没生效。
先检查你打开的是不是项目根目录,ESLint 在编辑器中默认只向上查找最近的eslint.config.js。如果你在src/子目录建了配置文件,根目录没有,编辑器可能会在项目根找到一份旧版配置文件并忽略你的新配置。最佳实践是在项目根目录只保留一份eslint.config.js,不用建第二份。
6.2 编辑器格式化结果与命令行不一致
问:我在 VSCode 里保存时格式化好的代码,命令行跑prettier --write .后又被改了,怎么回事?
这几乎可以肯定是 VSCode 里配置了不止一个格式化工具,或者默认格式化工具被设置成了别的。比如你项目里装了 Volar(Vue 插件),Volar 自己也能格式化,容易抢占 Prettier 的工作。正确做法是保证editor.defaultFormatter明确指定为 Prettier,同时在项目级 settings 里把其他格式化插件的 Auto Format 关掉。
问:团队里有人用 WebStorm,有人用 VSCode,格式化结果不一致。
WebStorm 需要在设置里手动启用 Prettier 作为默认格式化器,Setings -> Languages & Frameworks -> JavaScript -> Prettier,选择Automatic Prettier configuration,然后勾选Run on save。这个配置通常只需要每个人自己设置一次,配合.editorconfig文件可以进一步统一缩进和换行行为。不过只要大家最终都是跑同样的.prettierrc,结果就会一致,编辑器只是触发方式不同而已。
6.3 团队协作中的规则变更流程
问:项目做大了之后,想改某个 lint 规则,怎么操作才安全?
我的经验是:宁可多花点时间做渐进式变更,也不要一次性把整条规则打开然后全量修复。比如你的团队决定不再使用any类型,想开启no-explicit-any,这会瞬间爆出几千个错误。正确做法是先用warn级别跑一个月,给大家时间逐步修复,再升级成error强制禁止。
如果项目实在历史包袱太重,也可以在 ESLint 配置里用override只针对新文件开启新规则,旧文件放行。这是非常实用的过渡策略:
{ files: ['src/**/*.ts'], rules: { '@typescript-eslint/no-explicit-any': 'off' } }6.4 快速排查清单
最后附上一份我自己排障时常用的检查清单,遇到问题先过一遍:
| 症状 | 排查方向 | 解决思路 |
|---|---|---|
| 配置不生效 | 配置文件位置/命名 | 确认是eslint.config.js且位于项目根目录 |
| 冲突修复 | eslint-config-prettier顺序 | 确保prettier配置在数组最后 |
| 编辑器格式化不一致 | defaultFormatter设置 | 项目级 settings.json 明确指定 Prettier |
| Git 钩子不执行 | husky 是否激活 | 重新运行npx husky init |
| lint-staged 用了全量文件 | 命令写法问题 | 只写命令名,不要带目录参数 |
| Windows 钩子不生效 | Git 环境变量 | 升级 Git,手动运行npx husky激活 |
| CI 里报 lint 错误 | scripts 漏配或忽略配置 | 检查 package.json 的 scripts 和 ignores |
配置一套 ESLint + Prettier 的说到底就是给项目立了一份“自动执行的规矩”,前期花半小时搭好,后面每天省下的都是跟同事扯皮的时间。我个人在实际操作中的体会是,这套工具链真正磨合好的标志不是所有人都会配,而是所有人都不需要知道它具体怎么配——打开项目,改代码,保存,提交,一切自动发生,这才是工程化该有的样子。最后再分享一个小技巧:如果你要管理多个项目,别在每台新机器上重装一遍依赖,把.vscode/settings.json、eslint.config.js、.prettierrc这几个文件放进你的个人代码模板里,以后开新项目直接复制,五分钟就能落地一套完整的规范体系。