首页 / 文章 / 用 Biome 替代 ESLint 和 Prettier:速度与你仍需遵守的 Hooks 规则

用 Biome 替代 ESLint 和 Prettier:速度与你仍需遵守的 Hooks 规则

Biome与ESLint及Prettier的测试时间对比、react-hooks与exhaustive-deps之间的差异,以及何时在双运行环境下使用一个ESLint插件才是合理的迁移方式。

1944 词

营销内容宣称该工具能提供Rust二进制文件、单一配置,以及20到50倍的效率提升。之后进行了四路测试,同时统计了Biome无法处理的诊断问题。

使用单个二进制文件与多插件组合相比,处理时间有所减少,但对某条重要规则的覆盖率却没有提升。

查看package.json文件。

统计与代码检查相关的依赖项。那些仍包含eslint、prettier、eslint-config-prettier、eslint-plugin-react-hooks以及解析插件在内的项目,每次提交都会产生额外的开销。Biome则宣传自己是一个可同时完成格式化、代码检查及导入排序功能的单一执行文件。

在该实验室的现有ESLint工具链旁新增了Biome。两种流程都经过了时间测试,之后便移除了ESLint。某个依赖的诊断工具没有看似安全的Biome对应版本。流程最终通过测试,而该诊断工具此前用于阻塞的错误类别在特性分支上又出现了。

当主题切换时,该分支会重新连接WebSocket——这是典型的效果依赖问题。而Biome则保持沉默。

Biome实际输出的句子

官方称,Biome的格式化结果与Prettier的匹配度约为97%,其代码检查规则集则借鉴了ESLint和typescript-eslint。相关设置保存在biome.json文件中,日常使用方式类似pnpm exec biome check --write。

那些炫目的文章没有提及:一些冷门插件及内部规则可能仍然缺失。对于这些不足,要么继续使用ESLint,要么将相关逻辑移植过来。

速度很容易让人喜爱。而覆盖率的缺失则如同租金一般难以承受。

实际记录的时间

实验室使用了TypeScript、React以及少量服务器模块——大约有六十个非node_modules文件。Instant Navigations未被启用,因此仅测量了检查工具的运行时间。

pnpm exec eslint . --max-warnings=0
pnpm exec prettier --check .
pnpm exec biome check .

每种工具各运行三次,中值如下:

  • 仅使用ESLint:4.1秒
  • Prettier的--check选项:1.6秒
  • 先使用ESLint再使用Prettier:5.7秒
  • Biome的check选项:0.22秒

远未达到50倍的差距。与顺序运行的两种工具相比,在小型项目中的加速倍数约为26倍。博客文章中通常提到的文件数量为1万左右。此测试使用的是实际部署的应用程序,趋势数据与之相符,而那些夸大的倍数则不然。

在两次检查中,Prettier与Biome对两个文件存在分歧——一个是位于InvoiceRow内部的冗长JSX属性嵌套结构。Biome保留了原有的嵌套格式。97%的匹配率是真实的;剩下的3%只有在团队逐个文件争论时才会引发争议。

如何在您的设备上查看结果

pnpm add -D --save-exact @biomejs/biome
pnpm exec biome init

会出现一个biome.json文件。在ESLint仍存在的状态下运行一次Biome。在得到一份记录了ESLint发现但Biome未报告的所有问题的清单之前,不要删除任何内容。

pnpm exec eslint . -f unix > /tmp/eslint.txt
pnpm exec biome check --reporter=json > /tmp/biome.json

手动对比后发现,大多数recommended级别的问题都是重叠的。唯一存在差异的问题是:

react-hooks/exhaustive-deps

Biome包含了与钩子相关的检查项,但这些检查与之前针对socket效应设置的警告并不完全一致。在CI系统移除了那条规则后,主题切换相关的连接问题又漏掉了。

ESLint仅针对一个插件返回了警告:

{
  "scripts": {
    "lint": "biome check . && eslint app --plugin react-hooks --rule 'react-hooks/exhaustive-deps:error'"
  }
}

操作繁琐且不够透明。当缺失的规则有名称时,同时使用两个工具是现代环境中一个合理的折中方案。但为了“以防万一”而保留完整的ESLint配置树通常只是浪费资源。

应在当天晚上就验证编辑器的相关功能。Biome的语言服务器取代了原本使用的两个扩展插件,结果钩子相关的警告只出现在持续集成环境中——如果在本地不继续保留针对该规则的ESLint扩展,情况会更糟。最好能在/settings中看到相关提示,而非在早晨的构建过程中才发现问题。

究竟哪种方式更快

Biome以单个原生进程的方式遍历配置树。而ESLint则是基于Node加上各种插件,之后还可能用到Prettier。在60个文件的情况下,加载插件就会占用4.1秒中的大部分时间;一旦文件数量达到数千个,遍历配置树的操作就会占据主导地位,此时就会出现类似大规模项目中的性能表现。

Biome 2 提供了基于类型的代码检查功能,但并未实现 TypeScript-eslint 的全部功能。如果持续集成仍然支持 parserOptions.project,则应单独衡量该流程的耗时——因为那正是性能开销最大的 ESLint 模式。与旧版的 no-unsafe-* 设置相比,其功能存在差距。

关闭基于项目的解析功能后,CI 流程的耗时从约 90 秒降至约 8 秒,却将这一提升归功于 Biome,其实只是因为去掉了基于类型的检查功能而已。遇到这种情况时务必说出来。

详细费用清单

流水线处理时间:从 5.7 秒降至 0.22 秒。多模块项目能最明显地感受到性能提升,而小型应用则主要体现在笔记本电脑上的运行速度更快。

规则方面:有一个依赖检查项消失了,但该路径仍保留 ESLint 检查。

格式化相关问题:有两个 JSX 包装结构,但可以解决。

编辑器方面:两个扩展合并为一个,后又恢复为两个——整体差异不大,但命令行检查速度更快了。

注意:绿色 Biome 运行结果并不等同于绿色 hooks 树。拉取请求中应注明是哪位运行者标记了该文件。

保持启用状态,或停止夜间任务

新项目仓库今晚即可采用 Biome 进行格式检查及基础代码规范校验,完全无需使用 Prettier。

旧项目应进行约一周的双重运行测试。仅对有名称的插件保留 ESLint,删除未使用的配置文件,而不仅仅是对应的 CI 步骤。

当自定义规则本身就是产品功能时,仍需继续使用 ESLint,并将这些规则记录下来。“也许我们需要插件”并不能算作需求清单。

绝不能仅仅因为 Biome 运行速度快就放弃使用 exhaustive-deps。主题切换场景就能证明该规则存在的必要性。

值得明确说明的注意事项

0.22秒对比5.7秒的数据是在一台笔记本电脑上对60个文件进行的测试结果——并非针对1万份文件的测试,也不是56倍的性能提升。

钩子间距取决于配置以及当夜的生物群系构建版本。在将该问题视为永久性故障之前,请重新运行 biome rage 并查看当前的钩子诊断信息;不同版本中标识符可能会发生变化。

插件的管理方式各不相同。如果仅通过 eslint-plugin-import 来控制导入顺序,可尝试使用 Biome 的 organize-imports 功能一周时间。

同步运行这两个工具,列出在清理 Biome 后仍然存在的 ESLint 错误。

这份清单本身就是迁移工作的一部分。剩余的问题需要通过明确的职责划分,由专门的脚本来处理。

当 CI 检测通过且套接字重新连接后

原本使用的 ESLint 加 Prettier 已被 Biome 取代。检查60个文件的耗时从5.7秒降至0.22秒。某个分支已被合并,切换主题后又重新建立了总线的 WebSocket 连接。react-hooks/exhaustive-deps 之前会阻止这种模式,而 Biome 则不会产生相关的错误提示。

糟糕的响应。应暂停该插件,直到“迁移完成”。否则客户将无法看到实时数据。

更好的响应方式。Biome负责格式处理及基础代码检查,而ESLint仅保留用于app目录下的某个插件。

{
  "scripts": {
    "lint": "biome check . && eslint app --plugin react-hooks --rule 'react-hooks/exhaustive-deps:error'"
  }
}

当缺失的规则被明确指出时,两种处理方式都是合理的;但若整个ESLint配置都被保留,则属于资源浪费。格式差异表现为:JSX需要多一层包裹,而Biome版本则保持不变。

请在自己的项目中进行测试。在清理完Biome后的配置中,列出仅由ESLint处理的规则。先列出规则,再记录测试时间。

实验环节的命令与输出

实验前的版本为:Node 24、TypeScript 7、Next 16.3。请将这些信息记录在实验笔记中,以便后续实验无需猜测。

node -v
pnpm exec tsc -v
pnpm exec next --version

请将这三行内容放在笔记的最上方。如果主要版本与所遵循的指南不一致,应立即停止——否则后续命令可能会悄悄导致错误结果。

接下来的路线演示:

pnpm exec next dev

输入 /、/invoices、/invoices/1、/settings,然后再输入一次 /invoices。保持 DevTools 的日志记录功能开启。截取筛选界面和对应的 URL,这对后续相关实验非常有用。

类型检查:

pnpm exec tsc --noEmit --pretty false
echo $?

退出码为零并不代表产品已正式发布,它仅表示为运行时检查清除了路径。

之后,重新执行“如何在您的机器上查看”章节中的检测命令。不要因为上面有数字就跳过这些步骤——不同的机器、环境温度或繁忙的 Chrome 标签页,都会比框架补丁更显著地影响内存使用、检测时长以及计时结果。

记录一条简短的故障处理记录,格式为:“尝试了 X,但仍然出现 Y。”版本信息、命令内容、输出结果以及那句话都是很有用的后续参考资料,而营销截图则没有这种价值。

故障记录本

在 Biome 到达的同一天移除 ESLint 后,套接字问题又出现了。恢复其中一个插件后,代码覆盖率才得以恢复。

试图在 Biome 2 中复制 project-aware typescript-eslint 的行为时,只发现了部分相似之处。旧版的 no-unsafe-* 规则组并不匹配,强行使其匹配会导致问题。

一场持续很久的 JSX 属性格式冲突最终通过采用 Biome 得以解决。

在编辑器中,Biome 的 LSP 取代了 Prettier 和 ESLint 扩展。钩子相关的警告仅出现在 CI 环境中,因此该规则仍保留 ESLint 扩展。两个扩展仍然比五个更好。

删除 ESLint 之前的检查清单

  • [ ] 执行 biome check 后结果正常
  • [ ] 已记录所有仅由 ESLint 处理的规则
  • [ ] 已决定是保留还是主动放弃某些命名插件
  • [ ] exhaustive-deps 已有可信赖的替代方案,否则仍保留 ESLint
  • [ ] 格式差异应冷静审查,而非慌乱回退
  • [ ] 两块秒表都是在同一棵树上取值的
  • 0.22秒对5.7秒,相差六十倍。请在本地重新测量,并在将这一差距视为永久性结果之前,再次检查已安装的Biome中的钩子诊断功能。

    发票应用中的生产环境说明

    CI节省的是实际时钟时间。在演示过程中,更换主题导致websocket重新连接,因为钩子覆盖率消失了。重要的不是时间数值,而是实时的汇总数据。

    与其使用速度极快却会漏检重新连接的检查工具,不如选择速度稍慢但能捕获这些情况的工具。将0.22秒的Biome与一个ESLint插件结合使用也是不错的选择。新项目可以直接从Biome开始,等有规则需要时再添加ESLint;老项目则无需进行不必要的纯化操作。

    命令

    pnpm exec eslint . --max-warnings=0
    pnpm exec prettier --check .
    pnpm exec biome check .
    

    运行三次以获取数据,然后计算中位数。记录 ESLint、Prettier 以及 Biome 的运行结果。输出 ESLint 的 Unix 格式结果,并将 Biome 未报告的错误项进行标注。将这些标注放入拉取请求中,可将时间信息视为脚注处理。

    真正有意义的差异在于那些没有对应规则的错误项。

    基于真实项目的练习

    选择已发布的实际项目仓库,而非沙箱环境。分别记录 eslint .、prettier --check . 和 biome check . 的执行时间——每种命令运行三次。将中位数与文件数量一同列在表格中。

    可以先手动生成规则差异对比,因为自动重命名功能常常会带来误导。列出在 Biome 处理完成后仍然存在的所有 ESLint 错误项。如果列表为空,则说明这些错误可能是 ESLint 本身留下的;如果列表中包含 react-hooks/exhaustive-deps 或对产品至关重要的自定义插件相关错误,则需保留这些错误项。

    打开一个包含已知真实缺失依赖项的钩子的分支,看看是哪个工具会报错。正是由于这个分支的存在,比较过程才不仅仅是竞态条件问题。

    将表格和规则列表提交上去。避免使用仅会删除包的“切换到Biome”类型拉取请求,删除操作应是最后一步,而非起始动作。