我用 Oxlint 替换了团队项目的 ESLint,构建从 75 分钟缩到了 10 秒

我用 Oxlint 替换了团队项目的 ESLint,构建从 75 分钟缩到了 10 秒

去年我在写 Rust 工具链文章的时候,提到过 Oxlint 这个项目。当时它还只是个 beta 版,虽然跑起来飞快,但规则覆盖不够,真上生产项目有点虚。

今年 8 月 Oxlint 1.0 稳定版发了。我就趁着项目迭代期,把团队一个中型前端项目从 ESLint 换到了 Oxlint。这篇文章说说迁移过程、踩的坑、以及这玩意儿到底值不值得换。

先说结论:如果你还在忍受 ESLint 越来越慢的 lint 速度,尤其是 CI 上 lint 要跑好几分钟那种,Oxlint 值得一试。不是噱头,是真的快。

Oxlint 是什么

Oxlint 是 Oxc 项目的一部分。Oxc 是用 Rust 写的前端工具链集合,包括解析器、linter、格式化器等。Oxlint 对标的就是 ESLint。

1.0 版本发布时,它已经支持了 520+ 条 ESLint 兼容规则,涵盖了 eslint core、typescript-eslint、eslint-plugin-react、eslint-plugin-vitest 等主流插件的规则。

关键数据:官方说比 ESLint 快 50-100 倍。Airbnb 拿自己的仓库(12.6 万个文件)跑了一下,Oxlint 7 秒就跑完了,ESLint 直接超时跑不完。

安装和使用

安装非常简单,一行命令:

npx oxlint@latest

对,不用装任何 npm 包到项目里。你甚至不需要项目里有 node_modules,因为 Oxlint 是 Rust 编译的二进制文件。

我习惯在 package.json 加个脚本:

{
  "scripts": {
    "lint": "oxlint",
    "lint:fix": "oxlint --fix"
  }
}

然后跑:

pnpm lint

第一次跑的时候我就被震惊了。660 个文件的项目,跑了大概 300ms。之前 ESLint 同样的项目要跑 40 多秒。

配置文件对比

ESLint 的配置,大家应该都熟:

// .eslintrc.js
module.exports = {
  env: {
    browser: true,
    es2022: true,
    node: true,
  },
  extends: [
    'eslint:recommended',
    'plugin:@typescript-eslint/recommended',
    'plugin:react/recommended',
    'plugin:react-hooks/recommended',
  ],
  parser: '@typescript-eslint/parser',
  parserOptions: {
    ecmaVersion: 'latest',
    sourceType: 'module',
  },
  plugins: ['@typescript-eslint', 'react', 'react-hooks'],
  rules: {
    'no-console': 'warn',
    '@typescript-eslint/no-unused-vars': 'error',
    'react/react-in-jsx-scope': 'off',
  },
};

Oxlint 的配置更简洁:

{
  "rules": {
    "no-console": "warn",
    "no-unused-vars": "error",
    "react/react-in-jsx-scope": "off"
  }
}

而且 Oxlint 默认零配置就能跑——它会自动识别项目中的 TypeScript、React、Vue 等配置,不需要你显式声明。

Oxlint 的配置格式类似 ESLint 的 v8 平面配置系统。下面是官方文档里的完整示例:

{
  "env": {
    "browser": true,
    "es2022": true
  },
  "rules": {
    "no-debugger": "error",
    "no-console": "warn",
    "no-unused-vars": ["error", { "args": "none" }],
    "react/no-direct-mutation-state": "error",
    "typescript/no-unused-vars": "error"
  },
  "settings": {
    "react": {
      "version": "detect"
    }
  }
}

迁移步骤

我迁移的过程分了四步:

第一步:并行跑,不急着删 ESLint

用 eslint-plugin-oxlint 插件,它会在 ESLint 里关闭那些已经被 Oxlint 覆盖的规则:

pnpm add -D eslint-plugin-oxlint

然后在 ESLint 配置里加:

// eslint.config.js
import oxlint from 'eslint-plugin-oxlint';

export default [
  // ... 其他配置
  oxlint.configs.flatRecommended,
];

这样就保证两边不会重复报错。

第二步:CI 里先用 Oxlint 前置检查

把 lint-staged 配置改成先跑 Oxlint,再跑 ESLint:

{
  "*.{ts,tsx,js,jsx}": [
    "oxlint --fix --no-ignore",
    "eslint --cache --max-warnings 0 --fix"
  ]
}

这一步很重要。Oxlint 因为快,几百个文件几百毫秒就跑完了,先跑一遍能过滤掉大部分问题。ESLint 只需要处理 Oxlint 覆盖不到的规则。

第三步:验证规则覆盖

用 Oxlint 自带的统计命令,看看还有多少规则依赖 ESLint:

oxlint --stats

它会输出当前项目中触发的规则数量、类别分布等。

第四步:砍掉 ESLint

等确认 ESLint 里的那些独特规则(比如某些自定义插件)也用不上了,彻底卸载:

pnpm remove eslint eslint-plugin-react eslint-plugin-react-hooks @typescript-eslint/eslint-plugin @typescript-eslint/parser

配置文件也从 .eslintrc.js 换成 .oxlintrc.json。

性能对比

我记录了迁移前后的一些数据:

场景 ESLint Oxlint 提升
全量扫描 660 文件 43s 320ms 134x
增量扫描(改 5 文件) 8s 120ms 66x
CI 全量 lint 75s 480ms 156x
pre-commit hook 12s 200ms 60x
内存占用 1.2GB 180MB 6.7x

注意,ESLint 在 CI 上能跑到 75 秒,是因为我们项目配了不少插件和规则。Oxlint 在 CI 上半秒不到就搞定了。

内存方面的差距也很明显。我们有个同事 16GB 内存的 MBP,跑 ESLint 的时候风扇直接起飞,换 Oxlint 之后安静了。

再说说 ESLint 的 “no-unused-vars” 规则。它其实有个性能 bug:开启之后,每次 lint 都会做一次全量 AST 遍历来检查变量引用。项目越大越慢。Oxlint 用 Rust 写,同样的检查用多线程并行处理,不仅快,还比 ESLint 省内存。

一个真实的案例:我们有个文件 import 了十几个类型,但只用了两三个。ESLint 要解析整个文件的所有 import 声明才能报错。Oxlint 用 Rust 的并行解析,同样的文件 5ms 就搞定。

踩到的坑

当然,也不是完美的。说几个我遇到的问题:

1. 自定义 ESLint 插件不支持

这个是最大的痛点。我们团队写了一个 eslint-plugin-internal,专门校验内部 API 的调用规范。Oxlint 目前不支持自定义插件。

解决方案:保留 ESLint 只跑这个插件的规则,其他的交给 Oxlint。

2. 类型感知规则暂时没有

Oxlint 不像 @typescript-eslint 那样能用 TypeScript 的类型信息来做检查。比如 no-floating-promises、strict-boolean-expressions 这些需要类型推断的规则,Oxlint 目前还不支持。

官方说后续版本会考虑,但目前还不是重点。

3. 部分 Rule 的行为略有差异

比如 no-unused-vars 规则,Oxlint 默认不检查函数参数(args: ‘none’),而 ESLint 默认检查。迁移时需要手动配置对齐。

我遇到过的情况:ESLint 报了一个 “no-console” 的 warn,但 Oxlint 默认是 “off”——因为 Oxlint 把 no-console 归到了 “style” 类别,默认不开启。需要手动在配置里打开。

4. 报错信息风格不同

Oxlint 的报错格式长得跟 ESLint 不太一样:

  ⚠ oxlint(no-console) src/index.ts:10:3
  Unexpected console statement.
  help: https://oxc.rs/docs/guide/usage/linter/rules/no-console.html

不如 ESLint 的彩色输出直观。不过习惯了也一样看。

当前更适合哪些场景

根据我这几个月的使用体验,我总结了一下 Oxlint 的适用场景:

强烈推荐:

  • 中小型项目(< 5000 文件)
  • CI lint 时间超过 30 秒的项目
  • 没有大量自定义 ESLint 插件的项目
  • 新项目,从一开始就用 Oxlint

谨慎评估:

  • 大型 monorepo(但 Airbnb 证明了它能处理 12 万文件)
  • 重度依赖自定义插件的项目
  • 需要类型感知规则的项目

暂时不建议:

  • 对自定义插件依赖极重的团队
  • 已经在用 Biome 并且没有 lint 瓶颈的团队

Oxlint vs Biome

很多人会问,已经有 Biome 了,为什么还需要 Oxlint?

我个人的看法:

Biome 走的是 “全家桶” 路线,一个工具管格式化+lint。好处是统一,坏处是格式化这块争议比较大。Oxlint 专注做 lint,格式化你可以继续用 Prettier。

性能上,Oxlint 的基准测试显示它比 Biome 还快 2 倍。虽然在实际项目中差距没那么明显,但 Oxlint 确实更快一些。

规则覆盖上,Oxlint 目前 520+ 规则,Biome 的 lint 规则也在增长。但 Oxlint 对 ESLint 规则的兼容性做得更好,迁移成本更低。

选哪个主要看你更需要什么:想要一个工具搞定格式化+lint 选 Biome,想要 lint 性能拉满+与 ESLint 平滑过渡选 Oxlint。

总结

Oxlint 1.0 的发布,意味着 Rust 重构前端工具链又迈出了一大步。ESLint 过去十年做得很好,但它的架构决定了它不可能再快多少。Oxlint 用 Rust 重写的这条路,已经被 Rspack、Biome、esbuild 等工具验证过了——快 50 到 100 倍不是吹的。

如果你也被 ESLint 的慢折磨着,花一个小时试试 Oxlint。不亏的。

配置上,从 .eslintrc 到复制一份 .oxlintrc.json,改改规则名,基本就完事了。迁移工具 oxlint-migrate 还能自动帮你转。

最大的收益不是省下来的那几十秒——而是开发体验的提升。代码改完,保存,lint 结果弹出来的感觉,才应该是现代开发工具该有的样子。

评论区

0 条评论

登录后可评论。

查看完整榜单
查看完整榜单
查看完整榜单