Skip to content

Vite+ 1.0 与 Oxlint:切换前我会先验证这些提速数据 ​

译注:本文编译自 dev.to 文章《Vite+ 1.0 and Oxlint: The Speedup Claims I'll Test Before Switching》,作者以第一人称记录了对 VoidZero 最新进展公告的解读,以及在实际项目中切换工具链前会执行的验证步骤。原文链接见文末。

作者坦言,自己已经拖延了四个月的 lint 清理工作。原因不是难,而是每次在中等规模的 TypeScript 项目上跑带类型信息的 ESLint,都要等很久。因此,当 Evan You 在 Cloudflare 博客发布 VoidZero 四个月进展报告,并提到"在大型代码库中比 ESLint 快最多 18 倍"时,作者的第一反应是怀疑,第二反应是打开终端。

结论先行:这些数字是厂商数据,方向可信,而 Vite+ 1.0 是首次把整套基于 Rust 的 JavaScript 工具链打包在一起。作者尚未在自己的仓库上实测,因此这篇文章是对公告的解读,加上他允许这些工具接近客户项目之前会执行的具体检查。

四个月里实际交付了什么 ​

公告列出了五项内容,它们属于不同类型的声明:

  • Oxc React Compiler 编译 React 应用比 Babel 版本快 10 倍;
  • Vitest 5 比 Vitest 4 快最多 50%;
  • tsgolint(Oxlint 背后的类型感知引擎)已稳定,比搭配 typescript-eslint 的 ESLint 快 12 到 18 倍;
  • Oxfmt 比 Prettier 快 7 倍,其 JSON、CSS、SCSS、Less、GraphQL 和 YAML 格式化器已用 Rust 重写;
  • Vite+ 达到 1.0。

注意这些限定词。"最多 50%"是最好情况,"最多 18 倍"是大型代码库上的最好情况,React Compiler 的"10 倍"是编译步骤的数字,而编译时间很少是构建中最慢的部分。每一项在某个项目上都是真实的提速,但没有一项能说明在"我的项目"上会发生什么。这不是在批评 VoidZero,所有厂商都这么写。它提醒我们:由工具作者给出的基准回答的是"这能快吗",而不是"对我而言会快吗"。

作者还认可了这套技术栈的论证:Oxc 是编译器,Rolldown 是打包器,Vite 位于其上,Oxlint 和 Vitest 复用相同的部件。当某一层变快,上层就免费变快。共享解析器和共享 AST 意味着一次修复能让多个工具受益,因此作者倾向于相信这个逻辑。

最关心的部分:类型感知 lint ​

带类型信息的 lint 是最慢的,也是最能抓真实 bug 的:浮动的 Promise、误用的异步回调、不安全的 any 流入函数。这些都需要把 TypeScript program 放在内存里,而这正是 ESLint 加 typescript-eslint 耗时的地方。

据公告,tsgolint 支持 61 条 typescript-eslint 类型感知规则中的 59 条,且 Oxlint 可以在 lint 和类型检查之间共享同一个 TypeScript program,而不是构建两次。第二部分是作者会首先关注的,因为大多数 CI 流水线把 tsc --noEmit 和 ESLint 作为两个独立步骤运行,两者都要为解析整个项目付出代价。

公告展示的配置改动很小:

typescript
import { defineConfig } from "oxlint";

export default defineConfig({
  options: {
    typeAware: true,
    typeCheck: true,
  },
});

对比作者当前的配置:一个 eslint.config.js,带指向 tsconfig 的 parser 选项、一个查了两次文档才搞懂的 project service 设置,以及一段解释为什么慢规则慢的注释。即便提速最终只有 6 倍而非 18 倍,更少的活动部件也是胜利。

不过,缺失的两条规则很重要。如果团队依赖 tsgolint 未覆盖的那两条 typescript-eslint 规则之一,就得同时运行两个 linter 一段时间。公告没有说明是哪两条,这是作者会首先去文档里确认的事。

Oxfmt 与 Prettier 之问 ​

格式化器是个信仰话题,所以务实一点。Oxfmt 声称输出与 Prettier 兼容,迁移只需两条命令:

shell
pnpm add -D oxfmt
pnpm oxfmt --migrate prettier
pnpm oxfmt

任何 Prettier 替代品的风险都在 diff。如果输出哪怕有 0.5% 的行不同,就会产生一个巨大的格式化 commit,破坏 git blame。作者曾用 Laravel Pint 经历过一次"一个 commit 毁掉 git blame"。因此计划很无聊:在分支上跑 Oxfmt,统计改动行数,如果保留就把格式化 commit 加入 .git-blame-ignore-revs。

对作者而言,格式化器的速度是最不吸引人的收益。小项目上格式化本来就只有几秒。他会为了单一工具链而切换,而不是为了省那几秒。

Vite+ 1.0 与决策疲劳 ​

Vite+ 打包了 Vite 8、Vitest 5、Rolldown、Oxlint、Oxfmt 和任务缓存,并替你选好了默认值。Evan You 的主张是:AI agent 和人类都在"我该用哪个 linter"上浪费时间,更少的选择意味着更快的交付。作者一半同意。

同意的一半:新项目不该在首次 commit 前就需要六个配置文件。不同意的一半:默认值很好,直到你需要偏离的那天,然后你读的是一个统一工具的文档,而不是五个知名工具的文档。作者有客户项目因框架原因锁定特定 ESLint 插件,一个"默认值很棒"的工具链帮不上忙。他会把 Vite+ 用于新项目,旧项目则按兵不动,直到有理由为止。

作者还对一种叙事持怀疑态度。公告称 2026 年的使命是让开发者的 agent 更快,因为一旦推理变快,慢的 linter 或类型检查器就成了瓶颈。这话本身没错。但如果 agent 循环花 40 秒等构建,解决办法往往是少跑一些,而不是把同样的事跑得更快。把 lint 范围限定到改动文件,胜过每次迭代都跑快 10 倍的全量 lint。更快的工具和更聪明的调用可以叠加,两者都做。

打包式开发与 React Compiler ​

还有两项值得一提。第一是 Bundled Dev(原 Full Bundle Mode),在开发环境使用 Vite 的生产打包器。它目前藏在实验性 flag 后面,公告称 Cloudflare 自家 dashboard 的所有内部开发者已在用它。这是个有用的数据点,因为大型 dashboard 应用正是非打包开发服务器在数千个模块请求下吃力的场景。

typescript
import { defineConfig } from "vite";

export default defineConfig({
  experimental: {
    bundledDev: true,
  },
});

第二是 Oxc React Compiler,通过 @vitejs/plugin-react 的 compiler: true 加上 oxc-transform-react 包启用。作者曾写过 React 19.3 view transitions 如何让他删掉手写的计时代码,而编译器属于同一类改动:组件里更少的手工活。他的顾虑是,编译器在正确性上的风险不同于 linter。慢的 linter 浪费时间,错误的编译输出则会发布 bug。在任何涉及金钱的项目上信任它之前,他会先跑完整测试并做视觉检查。

切换前我会跑什么 ​

以下清单按在真实项目上的执行顺序排列,大约花一小时,是检验厂商声明的诚实方式。

第一,给当前基线计时。把现有的 lint、类型检查和测试命令各跑三次,记下中位数。没有基线,"快 18 倍"就是无法验证的数字。

shell
for i in 1 2 3; do /usr/bin/time -f "%e s" pnpm eslint . ; done
for i in 1 2 3; do /usr/bin/time -f "%e s" pnpm tsc --noEmit ; done
for i in 1 2 3; do /usr/bin/time -f "%e s" pnpm vitest run ; done

第二,在分支上用新工具做同样的事,区分冷缓存和热缓存。冷缓存数字是 CI 看到的,热缓存数字是本地看到的。第三,对比输出:同一 commit 上两个 linter 的 lint 结果,以及同一批文件上的格式化输出。一个更快但漏掉某类 bug 的工具不是胜利。第四,对照你的配置,检查 tsgolint 未覆盖的那两条类型感知规则。

如果想本周就试,挑最慢的仓库,今天跑上面的基线循环,把三个中位数记在以后找得到的地方,然后在分支上装 Oxlint 对比。作者也会在自己的项目上这么做,并更希望读者告诉他公告数字在自己那里不成立,而不是轻信他或厂商的说法。

原文链接 ​