ESLint集成实战:从配置到团队协作的完整指南
我的 ESLint 配置,改了三次才稳定下来
我第一次在项目里配 ESLint 时,以为就是装个包、加个配置文件完事。结果团队一拉代码,CI 直接爆红,各种规则冲突、格式报错,连空格都管。折腾了两天,才勉强跑通。后来在多个项目里反复踩坑,终于摸索出一套“能用、不烦、还能真帮上忙”的配置方式。下面分享下我现在的写法。
先说结论:我建议用 @eslint/config-array + 共享配置 + IDE 深度集成的组合。但别急着复制,先看看我踩过的坑。
这几种错误写法,别再踩坑了
第一种:直接在 package.json 里写 ESLint 配置。看起来省事,但很快就会失控。比如:
{
"eslintConfig": {
"rules": {
"no-unused-vars": "warn",
"semi": ["error", "always"]
}
}
}
问题在哪?当项目变大,你要区分测试、构建、源码目录的规则时,这种写法根本没法扩展。而且团队成员一改就冲突,根本没法维护。我现在一律用独立的 .eslintrc.js 或 eslint.config.js(ESLint 8.21+ 新格式)。
第二种:全靠 eslint --fix 自动修复,以为万事大吉。其实很多规则是不能自动修的,比如逻辑错误、变量命名风格。更糟的是,有人把 --fix 加到 pre-commit 里,结果每次提交都偷偷改代码,队友 pull 下来发现一堆“格式变更”,根本看不出业务改动。我建议只在开发阶段用 IDE 自动修复,提交前人工确认。
第三种:盲目开启所有规则。比如 eslint:all,听着很安全,实际上会引入大量实验性或过时的规则。我曾经开过,结果项目里一堆 prefer-const 报错,但有些变量确实需要后续赋值,强行改反而降低可读性。现在我只用 eslint:recommended 作为基础,再按需加规则。
核心代码就这几行
我现在主推 ESLint 扁平化配置(flat config),从 ESLint 8.21 开始支持,配置更清晰、作用域更明确。以下是我目前在 React 项目中的配置(TypeScript + Vite):
// eslint.config.js
import js from '@eslint/js';
import ts from 'typescript-eslint';
import react from 'eslint-plugin-react';
import reactHooks from 'eslint-plugin-react-hooks';
export default [
{
ignores: ['dist/', 'node_modules/', 'build/']
},
js.configs.recommended,
...ts.configs.recommended,
{
files: ['**/*.{js,jsx,ts,tsx}'],
languageOptions: {
parserOptions: {
ecmaFeatures: { jsx: true }
}
},
plugins: {
react,
'react-hooks': reactHooks
},
rules: {
'react/react-in-jsx-scope': 'off', // React 17+ 不需要显式导入
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'warn',
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'warn'
}
}
];
为什么这么写?几点关键考虑:
- 明确忽略目录:避免扫描构建产物,拖慢速度还可能误报
- 分层继承:先用官方推荐规则打底,再叠加 TypeScript 和 React 插件
- 环境感知:比如
no-console在开发时 warn,上线前 error,避免漏掉调试日志 - 关掉过时规则:React 17 以后 JSX 转换不需要
React导入,所以关掉对应检查
这套配置在 CI 里跑得稳,本地开发也不卡。最重要的是——团队新人一拉代码就能跑,不用问“你 ESLint 配了吗”。
实际项目中的坑
第一个坑:TypeScript 类型定义和 ESLint 冲突。比如你在 tsconfig.json 里设了 "strict": true,但 ESLint 的 @typescript-eslint/no-explicit-any 又报错。这时候要统一标准:要么全靠 TS 检查,要么让 ESLint 补充。我现在的做法是——TS 负责类型安全,ESLint 负责代码风格和潜在 bug,两者互补但不重叠。
第二个坑:Monorepo 项目里不同子包规则不同。比如工具包用 CommonJS,前端用 ESM,测试文件允许 any。这时候别试图用一个配置搞定所有。我用 flat config 的 files 字段做精准匹配:
{
files: ['packages/utils/**/*.js'],
rules: {
'no-var': 'off' // 兼容老语法
}
},
{
files: ['packages/web/**/*.{ts,tsx}'],
rules: {
'@typescript-eslint/no-explicit-any': 'error'
}
}
第三个坑:VS Code 插件和命令行结果不一致。这是因为插件可能用了全局 ESLint,而项目依赖的是本地版本。解决方法很简单:在 VS Code 设置里加一行:
{
"eslint.packageManager": "pnpm" // 或 yarn / npm
}
并且确保安装了 dbaeumer.vscode-eslint 插件,然后重启编辑器。我之前因为没设这个,本地不报错,CI 却挂了,查了好久才发现是版本差异。
别忘了和 Prettier 和平共处
很多人把 ESLint 和 Prettier 搞成“打架”状态。比如 ESLint 要求分号,Prettier 去掉分号,结果保存一次代码来回震荡。我的方案是:让 Prettier 管格式(缩进、引号、分号),ESLint 管逻辑(未使用变量、错误用法)。具体做法是用 eslint-config-prettier 关掉所有格式相关规则:
npm install -D eslint-config-prettier
然后在配置里加到最后(确保覆盖前面的规则):
import prettier from 'eslint-config-prettier';
export default [
// ...其他配置
prettier
];
这样 ESLint 就不会对格式指手画脚了,交给 Prettier 处理。我在 .prettierrc 里统一风格,团队只要装了 Prettier 插件,保存就自动格式化,没人再争论“该用单引号还是双引号”。
最后一点:别追求 100% 规则覆盖
我见过有人为了消除所有 ESLint 警告,把代码改得面目全非,比如硬拆函数、加无意义的注释。其实 ESLint 是工具,不是枷锁。如果某条规则在特定场景下明显不合理,大胆用 eslint-disable-next-line 注释,并写上原因。比如:
// eslint-disable-next-line no-await-in-loop
// 这里必须串行请求,避免 API 限流
for (const id of ids) {
await fetch(https://jztheme.com/api/item/${id});
}
比起“零警告”,我更看重“有意义的警告”。现在我的项目里 ESLint 报错基本都是真问题,而不是风格之争。
以上是我个人对 ESLint 集成的完整总结,有更优的实现方式欢迎评论区交流。这个技巧的拓展用法还有很多,比如结合 lint-staged 做增量检查,后续会继续分享这类博客。

暂无评论