前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版

ESLint 常用配置与团队落地 从规则集到 CI 卡口

首页2018-01-27 22:41:24Front-End
规范ESLintPrettier前端工程化

规范文档写得再全,落地一个月之后代码里还是四五种风格。原因不复杂,文档只是写在那儿,没有任何东西在拦。

ESLint 的价值不在于它有多少条规则,而在于它是少数能在保存、提交、构建三个环节把不合规代码挡回去的工具。这篇不逐字段讲配置文件的语法,那部分我另写了一篇;这篇只聊落地。一份最小可用的规则集长什么样、要不要用现成的 config 包、和 Prettier 怎么分工、--fix 能修到什么程度、lint-staged 怎么做增量、CI 上卡在哪一步、规则从松到严怎么推。那两份规则清单一条没删,放在后面当速查表,并且标注了哪些在新版本里已经改名或移除。

在本篇文章中,我们将从浅入深,和大家一起学习以下知识:

  • 三类规则的性质不同,该用什么态度对待
  • 一份能直接抄进项目的最小规则集,以及每条为什么这么定
  • 主流 config 包怎么选,airbnb、standard、typescript-eslint 各适合谁
  • 和 Prettier 的分工线画在哪,为什么现在不推荐 eslint-plugin-prettier
  • --fix 能自动修什么,不能修什么,大规模修复怎么提交
  • lint-staged 做增量检查的完整配置和常见坑
  • CI 卡口设在哪一步,--max-warnings 和 --cache 怎么用
  • 规则严格度从 off 到 error 的推进节奏
  • 一份带修正标注的常用规则速查表
  • 迁到 ESLint 9 扁平配置之后,上面这些方案要改哪里

# 一、先分清三类规则

配置之前先分清楚 ESLint 的规则其实是三种不同性质的东西,混在一起谈就会吵不完。

第一类是「基本上写错了」。no-dupe-keys、no-unreachable、no-const-assign 这些,触发了几乎百分之百是 bug。这类规则没有讨论空间,全开 error。

第二类是「能跑但不该这么写」。eqeqeq、no-eval、no-param-reassign 属于这一档。它们有明确的理由,但也确实存在合理的例外,需要团队达成一致,个别地方允许带规则名的 eslint-disable。

第三类是纯格式。缩进、引号、分号、行宽,这一类完全是偏好问题,讨论它们的性价比最低。我的建议是整体交给 Prettier,ESLint 一条都别管,理由第四节展开。

分清这三类之后你会发现,团队里真正需要讨论的只有第二类,第一类照抄推荐集,第三类交给格式化工具。绝大多数关于「ESLint 配置」的争吵,都是在第三类上耗时间。

# 二、一份能直接用的最小规则集

先给一份能直接抄进项目的。这份规则不多,但每一条都是有实际约束力的。

'rules': {
    // no-var
    'no-var': 'error',
    // 要求或禁止 var 声明中的初始化
    'init-declarations': 2,
    // 强制使用单引号
    'quotes': ['error', 'single'],
    // 要求或禁止使用分号而不是 ASI
    'semi': ['error', 'never'],
    // 禁止不必要的分号
    'no-extra-semi': 'error',
    // 强制使用一致的换行风格
    'linebreak-style': ['error', 'unix'],
    // 空格2个
    'indent': ['error', 2, {'SwitchCase': 1}],
    // 指定数组的元素之间要以空格隔开(,后面), never参数:[ 之前和 ] 之后不能带空格,always参数:[ 之前和 ] 之后必须带空格
    'array-bracket-spacing': [2, 'never'],
    // 在块级作用域外访问块内定义的变量是否报错提示
    'block-scoped-var': 0,
    // if while function 后面的{必须与if在同一行,java风格。
    'brace-style': [2, '1tbs', {'allowSingleLine': true}],
    // 双峰驼命名格式
    'camelcase': 2,
    // 数组和对象键值对最后一个逗号, never参数:不能带末尾的逗号, always参数:必须带末尾的逗号, 
    'comma-dangle': [2, 'never'],
    // 控制逗号前后的空格
    'comma-spacing': [2, {'before': false, 'after': true}],
    // 控制逗号在行尾出现还是在行首出现
    'comma-style': [2, 'last'],
    // 圈复杂度
    'complexity': [2, 9],
    // 以方括号取对象属性时,[ 后面和 ] 前面是否需要空格, 可选参数 never, always
    'computed-property-spacing': [2, 'never'],
    // TODO 关闭 强制方法必须返回值,TypeScript强类型,不配置
    // 'consistent-return': 0
  }
@前端进阶之旅: 代码已经复制到剪贴板

这份配置里有几条值得单独说说。

'no-var': 'error' 加上 'init-declarations': 2 是一组。前者禁掉 var,后者要求声明变量时就给初值。两条一起的效果是逼你把变量声明尽量往使用处挪,而不是在函数顶部先声明一堆空变量。老项目上这两条会炸出很多报错,可以先设成 1 观察。

'semi': ['error', 'never'] 是不写分号那一派。这个选择本身没有对错,但一旦定了就必须全项目统一,因为不写分号在少数场景下会踩到 ASI 的坑,比如下一行以 ( 或 [ 开头。团队里一半人写一半人不写才是最糟的情况。

'quotes': ['error', 'single'] 和 'indent': ['error', 2, {'SwitchCase': 1}] 是典型的第三类规则。SwitchCase: 1 这个子选项别漏,不写的话 case 默认和 switch 顶格对齐。

'complexity': [2, 9] 限制圈复杂度,一个函数里的分支超过 9 个就报错。这条我挺推荐开的,它是少数能拦住「一个函数写八百行」的规则。顺带一提,ESLint 9 把可选链 ?. 也算进复杂度了,老项目升上去会有一批函数突然卡线。

最后那两行被注释掉的 consistent-return 说明了一件事,用 TypeScript 之后有一批 ESLint 规则可以直接关掉,因为编译器管得更准。这个取舍在 TS 项目里很常见。

# 三、别从零开始,先挑一个 config 包

自己从零维护两百条规则是件吃力不讨好的事。规则会随着 ESLint 版本变动,改名的、弃用的、默认值改了的,靠人工跟不现实。更省事的做法是先 extends 一个成熟的配置包,再在上面增删十来条。

常见的几个选择,按我自己的使用感受列一下。

配置包 定位 我的感受
@eslint/js 的 recommended ESLint 官方最小推荐集 只包含第一类规则,零争议,任何项目都该垫在最底下
eslint-config-airbnb / airbnb-base 规则最全最严 上手就是几百条报错,适合新项目从头立规矩,老项目慎用
eslint-config-standard 观点很强,不写分号、两空格 省心,但风格是它说了算,改不动
typescript-eslint 的 recommended TS 项目基础 TS 项目基本是必选,它会关掉一批被编译器覆盖的原生规则
eslint-config-next Next.js 项目自带 框架方维护,跟着框架版本走,省事
eslint-config-prettier 不加任何规则,只关规则 和 Prettier 一起用时必装,而且必须放在最后一位

选的时候有个判断标准比「哪个更好」实用得多:看这个包最近有没有在维护、有没有出扁平配置版本。ESLint 9 之后没跟上的包,会把你的升级路堵死。

eslint-config-prettier 那一行要特别注意顺序。它的全部作用就是把前面所有包开启的格式化规则统统关掉,所以放在 extends 数组的最后一位才有意义。放中间等于白装,我见过好几次。

# 四、和 Prettier 的分工线画在哪

这块是最容易配错的地方,也是我态度最明确的一块。

分工线其实很清楚。Prettier 管排版,它把代码重新打印一遍,缩进、换行、引号、分号全部由它决定;ESLint 管代码质量,未使用变量、可疑写法、复杂度、框架用法。两边职责不重叠,才不会互相打架。

问题在于 ESLint 自己也有一大堆格式化规则,它们会和 Prettier 的输出冲突。你保存时 Prettier 把代码格式化成 A,ESLint 立刻报错说应该是 B,来回拉锯。eslint-config-prettier 解决的就是这个,它把所有可能冲突的 ESLint 规则关掉,让格式这件事只剩一个说话的人。

那 eslint-plugin-prettier 呢。它的思路是把 Prettier 当成一条 ESLint 规则跑,格式不符就报成 lint 错误。听起来很整齐,一条命令搞定所有事。

我一开始也是这么配的,用下来的感受是不太值。Prettier 官方文档现在也不推荐这种用法,理由主要有几条:整段格式差异会被报成一堆红色的 lint 错误,噪音很大;Prettier 跑在 ESLint 里比单独跑慢;编辑器里的报错体验也别扭,明明保存一下就能修好的东西,却一直标着红波浪线。

现在更推荐的做法是让 Prettier 单独跑一条命令。

{
  "scripts": {
    "lint": "eslint .",
    "lint:fix": "eslint . --fix",
    "format": "prettier --write .",
    "format:check": "prettier --check ."
  }
}
@前端进阶之旅: 代码已经复制到剪贴板

编辑器里配好保存自动 Prettier,提交前 lint-staged 里两条都跑一遍,CI 上 format:check 和 lint 各卡一道。职责分开之后,出问题也好定位,是格式问题还是质量问题一眼就知道。

顺着上面聊,ESLint 自己对格式化这件事的态度也变了。从 8.53 开始,ESLint 把所有纯格式化规则标记为弃用,不再接受新特性和 bug 修复,官方把它们迁到了独立的 @stylistic/eslint-plugin。方向已经很明确,格式化 ESLint 不打算继续管了。所以如果你还在纠结 indent 和 quotes 怎么配,可以直接跳过这一步。

# 五、–fix 能修什么,不能修什么

eslint . --fix 是投入产出比最高的一个命令,但要知道它的边界。

能自动修的规则,官方文档里会标一个扳手图标。大致规律是「改动是确定的、不会改变语义」的都能修,比如引号、分号、prefer-const、no-extra-semi、多余空格。不能修的是那些「怎么改取决于你想干什么」的,比如 no-unused-vars,ESLint 不知道该删掉这个变量还是你忘了用它;再比如 complexity 超标,拆函数这事只能人来。

有几个参数配合起来很好用。

# 只看会修成什么样,不落盘
npx eslint src --fix-dry-run

# 只修某一类问题,避免一次改动过大
npx eslint src --fix --fix-type problem,suggestion

# 只修布局类,通常搭配格式化迁移
npx eslint src --fix --fix-type layout
@前端进阶之旅: 代码已经复制到剪贴板
fe
  • 一、先分清三类规则
  • 二、一份能直接用的最小规则集
  • 三、别从零开始,先挑一个 config 包
  • 四、和 Prettier 的分工线画在哪
  • 五、–fix 能修什么,不能修什么
  • 六、lint-staged 做增量检查
  • 七、CI 上把卡口设在哪一步
  • 八、规则严格度怎么一步步推上去
  • 九、常用规则速查表
  • 十、迁到扁平配置之后要改什么
  • 总结
  • 参考

← .editorconfig 配了却不生效?把团队缩进和换行一次管住广告行业一些术语 →