Node.js 自带“加速器”:node --run 是否比 Bun 更快?

30 阅读8分钟

前言

在 JavaScript 后端运行时领域,速度一直是核心战场。近年来,Bun 以其宣称的“一体化”工具链和闪电般的启动速度异军突起,对老牌王者 Node.js 发起了强劲挑战。bun run 的迅捷,让许多开发者开始重新评估他们的工具选择。

然而,Node.js 并未止步。自 v22.0.0 起,它悄然引入了一个专为启动性能而生的秘密武器:node --run。这个内置于 Node.js 核心的命令,旨在以最精简、最直接的方式执行 package.json 中的脚本,宣称要为最常见的用例提供“顶级性能”。

这引发了我们的好奇:在真实的场景下,这位新秀的表现究竟如何?它与 bun run 及传统的 npm run 相比,孰优孰劣?

本文将通过一系列严谨的对比测试,揭开 node --run 的神秘面纱,用数据回答:在 2026 年的脚本启动性能竞赛中,谁才是真正的速度王者?

各位小伙伴请注意!本文将颠覆这个传统观念“bun 启动一定比 Node.js 更快”。


首先我们回顾下 Node.js 运行文件有三种方式:

  1. 直接启动 node foo.[jt]s 🤼‍♂️ bun foo.[jt]s
  2. package.json 脚本运行 npm run foo 🤼‍♂️ bun run foo
  3. 脚本 --run 直接运行 node --run foo Added in: v22.0.0 🤼‍♂️ bun --run foo

统计方法,我们模仿竞技比赛中,去掉一个最高分和一个最低分,可以避免“冷启动”等极端数据干扰,采取去掉最大和最小值然后取平均值,这样抗干扰性强更公平

第一轮:直接启动 JS 文件速度

分别启动两个 mjs 文件,一个空一个有 IO 输出。测试文件:

// empty.mjs
// io.mjs
console.log('hello world');

Node.js 🟢

分别执行 time node empty.js time node io.mjs 各自三次。

❯ time node empty.mjs

node empty.mjs  0.00s user 0.17s system 78% cpu 0.217 total
node empty.mjs  0.00s user 0.17s system 84% cpu 0.203 total
node empty.mjs  0.00s user 0.16s system 69% cpu 0.225 total

无 io 平均耗时:217ms\approx217ms

❯ time node io.mjs

node io.mjs  0.00s user 0.17s system 73% cpu 0.234 total
node io.mjs  0.00s user 0.17s system 72% cpu 0.236 total
node io.mjs  0.00s user 0.17s system 74% cpu 0.229 total

io 平均耗时:234ms\approx234ms

整体耗时:225ms\approx225ms

Bun 🍞

bun empty.mjs  0.04s user 0.17s system 56% cpu 0.375 total
bun empty.mjs  0.00s user 0.21s system 63% cpu 0.337 total
bun empty.mjs  0.03s user 0.23s system 70% cpu 0.371 total

371ms\approx371ms

bun io.mjs  0.05s user 0.17s system 63% cpu 0.338 total
bun io.mjs  0.00s user 0.20s system 56% cpu 0.351 total
bun io.mjs  0.03s user 0.20s system 62% cpu 0.369 total

351ms\approx351ms

整体:361ms\approx361ms

这数据有点反常,1 bun 启动 io 文件速度反而快于空文件,2 其次得到本文第一个非常重要的结论:bun 对 js 文件启动速度反而劣于 Node.js(二者相差 100ms+)。

第二轮:直接启动 TS 文件速度

[!TIP] 注意:想要不加参数直接运行 TS,Node.js 版本需 \geq v22.18.0

分别启动两个 ts 文件,一个空一个有 IO 输出。测试文件:

# 文件内容一样
empty-ts.ts
io-ts.ts

Node.js 🟢

node empty-ts.ts  0.00s user 0.19s system 73% cpu 0.254 total
node empty-ts.ts  0.00s user 0.17s system 72% cpu 0.236 total
node empty-ts.ts  0.00s user 0.17s system 71% cpu 0.240 total

无 io 平均耗时:240ms\approx240ms

❯ time node io-ts.ts

node io-ts.ts  0.00s user 0.17s system 64% cpu 0.263 total
node io-ts.ts  0.00s user 0.17s system 67% cpu 0.255 total
node io-ts.ts  0.00s user 0.17s system 67% cpu 0.254 total

io 平均耗时:255ms\approx255ms

整体耗时:248ms\approx248ms

[!TIP] 小结:Node.js 运行 ts 耗时比 js 增加 ~20ms,这在预期内因为先要 amacro ts 编译器即将所有类型用空格替代

Bun 🍞

bun empty-ts.ts  0.04s user 0.21s system 69% cpu 0.372 total
bun empty-ts.ts  0.01s user 0.15s system 45% cpu 0.372 total
bun empty-ts.ts  0.06s user 0.17s system 56% cpu 0.402 total

无 io 平均耗时:372ms\approx372ms

bun io-ts.ts  0.01s user 0.25s system 64% cpu 0.405 total
bun io-ts.ts  0.00s user 0.21s system 55% cpu 0.388 total
bun io-ts.ts  0.09s user 0.23s system 74% cpu 0.429 total

io 平均耗时:405ms\approx 405ms

整体耗时:388.5ms\approx 388.5ms

[!TIP] 小结:Bun 运行 ts 耗时比 js 增加 ~20ms,预期内。

本文第二个结论:bun 执行 ts 文件启动速度劣于 Node.js(二者相差 388248=140ms+388 - 248 = 140ms+)。

有读者可能会问是否是通过环境变量寻址 bun 耗时导致的。那我们直接跳过寻址:

❯ time /e/pnpm/bun io-ts.ts

/e/pnpm/bun io-ts.ts  0.00s user 0.21s system 55% cpu 0.384 total
/e/pnpm/bun io-ts.ts  0.00s user 0.23s system 67% cpu 0.341 total
/e/pnpm/bun io-ts.ts  0.06s user 0.17s system 56% cpu 0.407 total

直接寻址耗时 384ms 384ms,确实少于 405ms 405ms,但依然显著高于 Node.js 的 255ms255ms!所以

结论一

Bun 无论启动 JavaScript 还是 TypeScript 文件都要慢于 Node.js

注意这里是启动而非运行。

第三轮:package.json 脚本启动

结论先行:执行 package.json 内的 scripts bun run 的启动速度快于 npm run。我们测试下。

npm v11.7.0

我们定义两个 script io 和不含 io 的:

{
  "type": "module",
  "scripts": {
    "io": "echo \"hello world\"",
    "empty": ""
  }
}

npm run

❯ time command npm run io

[!TIP] command npm 是因为我在 ~/.zshrc 内对 npm 做了同名 alias 需通过 command 找到原始命令。

> rsbuild-react-19@1.0.0 io
> io "hello world"

"hello world"
command npm run io  0.00s user 0.39s system 58% cpu 0.660 total
command npm run io  0.00s user 0.34s system 52% cpu 0.649 total
command npm run io  0.01s user 0.31s system 50% cpu 0.638 total

npm run io 耗时:650ms650ms

去除 IO:❯ time command npm run empty

command npm run empty  0.05s user 0.32s system 60% cpu 0.610 total
command npm run empty  0.03s user 0.34s system 59% cpu 0.626 total
command npm run empty  0.00s user 0.33s system 53% cpu 0.611 total

纯启动耗时 616ms616ms

整体耗时:npm run 启动需 600ms\approx600ms。这也太慢了,代码啥都没干呢!

接下来试试 bun run

bun run

❯ time bun run io:

bun run io  0.03s user 0.21s system 79% cpu 0.308 total
bun run io  0.06s user 0.21s system 78% cpu 0.347 total
bun run io  0.00s user 0.21s system 68% cpu 0.313 total

313ms313 ms 左右

❯ time bun run empty

bun run empty  0.04s user 0.18s system 60% cpu 0.380 total
bun run empty  0.01s user 0.21s system 69% cpu 0.330 total
bun run empty  0.03s user 0.21s system 67% cpu 0.361 total

361ms361 ms 左右。第二个反常 io 反而更慢。

整体耗时 337ms337 ms。结论 bun run 大概是 npm run 的两倍(600/300=2600 / 300=2)性能。

突然想起 Node.js v22.0.0 支持 node --run 运行脚本,专为性能而生,看看它表现如何。

第四轮:package.json 脚本 `--run` 直接运行

先解释下 node --run,是的,它也可以执行 package.json 中的脚本,而且生来就是为性能考虑(不会执行 pre / post 钩子)。

它将从 package.json 文件的 "scripts" 对象中运行指定的命令。如果提供的 "command" 不存在,则会列出可用的脚本。

node --run 并不旨在完全匹配 npm run 或其他包管理器的运行命令的行为。Node.js 的实现有意更加精简,以便在最常见的用例中专注于顶级性能。其他运行实现中一些被有意排除的功能包括:

  • 除了运行指定的脚本外,还会运行前置(pre)或后置(post)脚本。
  • 定义特定于包管理器的环境变量。

—— nodejs.org/docs/latest…

node --run

❯ time node --run io:

node --run io  0.00s user 0.17s system 79% cpu 0.216 total
node --run io  0.00s user 0.16s system 81% cpu 0.192 total
node --run io  0.00s user 0.19s system 84% cpu 0.220 total

216ms216ms 完美 🤩

❯ time node --run empty:

node --run empty  0.00s user 0.17s system 89% cpu 0.192 total
node --run empty  0.01s user 0.16s system 80% cpu 0.212 total
node --run empty  0.00s user 0.17s system 89% cpu 0.192 total

192ms192 ms 完美 🤩🤩

node --run 整体平均:204ms204 ms,确实做了性能优化,三倍性能提升 🚀npm run 616ms VS node --run 204ms

bun --run

bun --run empty  0.06s user 0.14s system 53% cpu 0.370 total
bun --run empty  0.05s user 0.21s system 75% cpu 0.347 total
bun --run empty  0.04s user 0.21s system 68% cpu 0.379 total

370ms370 ms

bun --run io  0.01s user 0.18s system 61% cpu 0.324 total
bun --run io  0.01s user 0.23s system 73% cpu 0.334 total
bun --run io  0.03s user 0.17s system 52% cpu 0.382 total

334ms334 ms (io 反而速度快于空脚本 -_-||)

bun --run 整体耗时 352ms352 ms反常三:bun --run 慢于 node --run204ms204 ms

性能数据总结

文章从三个维度(直接启动文件、运行 package.json 脚本、使用 --run 命令)对比了 Node.js 和 Bun 的性能。

核心结论

  1. 直接启动文件:无论 TS or JS,Node.js 以显著优势胜出。
  2. 运行 package.json 脚本:Bun (bun run) 击败 npm (npm run),但 node --run 凭借其精简设计击败 bun run / npm run / bun --run

至此本文颠覆了一个传统观念“bun 启动一定比 Node.js 更快”。

附录:详细数据

表格一:直接运行 JS/TS 文件性能对比 (单位:ms)
测试场景Node.js (v22.18.0)Bun (v1.3.2)性能胜出方
启动空 JS 文件~217~371Node.js (快 ~41%)
启动含 IO 的 JS 文件~234~351Node.js (快 ~33%)
JS 文件启动综合耗时~225~361Node.js 胜出
启动空 TS 文件~240~372Node.js (快 ~35%)
启动含 IO 的 TS 文件~255~405Node.js (快 ~37%)
TS 文件启动综合耗时~248~389Node.js 胜出

结论一:  在直接启动文件(无论 JS 或 TS)的场景下,Node.js 🚀 性能显著优于 Bun,平均领先幅度在 35%-41% 左右。虽然有反直觉但是我的 Windows 下确实如此。


表格二:运行 package.json 脚本性能对比 (单位:ms)
执行方式平均耗时 (含IO)平均耗时 (空脚本)综合平均耗时性能排序 (由快到慢)
node --run~216 ms~192 ms~204 ms🥇 第1名
bun run~313 ms~361 ms~337 ms🥈 第2名
bun --run~334 ms~370 ms~352 ms🥉 第3名
npm run~650 ms~616 ms~633 ms第4名

结论二:

  1. node --run 是绝对的性能冠军,速度是 npm run 的 3.1 倍,也比 bun run 快约 65%
  2. Bun 相关命令性能接近:bun run 和 bun --run 性能在同一水平,都显著快于 npm run(快约 88%)。
  3. npm run 垫底:传统 npm 脚本的启动开销最大,耗时最长。

对我们的启发

  • Node.js vs. Bun 启动速度:在直接启动 .js 或 .ts 文件时,Node.js 比 Bun 快约 40%-60% 。这是“一个非常重要的结论”。
  • Package Script 运行速度
    • bun run 比 npm run 快约 2 倍
    • 而黑马 🐎 node --run 比 npm run 快约 3 倍

node --run 的意义——包脚本运行的革新node --run 无疑是本次测试的最大亮点,其速度非常接近直接运行 node foo.js 的速度,实现了近乎“裸奔”的脚本执行性能。它通过牺牲 npm 脚本的部分高级功能(如 pre/post 钩子、特定的环境变量),换来了  ~3 倍于 npm run 的速度提升,甚至超越了以速度见长的 bun run。对于追求极致构建、测试或启动速度,且不依赖这些高级特性的项目,node --run 提供了一个近乎完美的“性能模式”。

启发:如果你要直接执行文件请使用 Node.js,如果要执行 npm script 建议 99% 情况下用 node --run(无需运行 pre / post npm script 时)否则 bun run

说明

测试环境 Windows 10、git bash、Node.js 🟢 v22.18.0、npm v11.7.0、bun 🍞 v1.3.2。


—— 完 🎉 最新文章请关注公众号 JavaScript与编程艺术 ——