说白了,兼容简单, 但是性能保证即便用AI也需要时间。
最近 Bun 那边动静挺大------底层从 Zig 换成 Rust,而且这事儿 AI 还帮了不少忙。看到这个新闻,脑子里第一个冒出来的想法就是:
"既然 AI 都能写代码了,让它把 Node.js 的 API 全抄过来不就行了?现在 Node 兼容性不是还有 10% 没搞定吗?AI 搞定它不是分分钟?"
乍一听好像挺有道理的对吧。但你仔细想想就会发现,这剩下的 10%,反而是最难啃的硬骨头。
那些至今没兼容的 Node.js API
咱先摆事实。Bun 官方文档加上社区反馈,下面这些 API 现在还都没法完全跑通,或者行为上对不上:
| 模块 / API | 现在啥情况 | 卡在哪儿 |
|---|---|---|
| http 客户端请求体 | 只支持整块塞进去,不支持流式发送 | 流式背压那套逻辑是 V8 + libuv 攒出来的,JSC 那边机制不一样 |
| https 的 Agent 配置 | 部分选项跑出来效果对不上 | Agent 复用 Socket 的逻辑跟 Bun 的异步 I/O 调度打架 |
| perf_hooks | API 有,但没过 Node 官方测试 | 性能计时那套语义深度绑在引擎实现上,V8 跟 JSC 路子不同 |
| async_hooks | createHook 这些核心 API 还没实现,调用会警告 | 想完全模拟 V8 的异步追踪就得大改引擎,性能也扛不住 |
| module.register | 没实现 | Bun 推自己的 Bun.plugin,这个优先级低 |
| 原生模块(Native Addons) | 像 bcrypt、canvas 这类基本跑不起来 | 这些模块是按 V8 的二进制编出来的,JSC 用不了 |
| repl | 整个模块没动 | Bun 自己的 REPL 更强,没必要抄 |
顺手聊一下 REPL 是啥
上表里的 repl 可能有人不太熟。简单讲,REPL 就是"读一句、跑一句"那种交互式命令行------你打开终端输 node,看到个 > 提示符,那就是 REPL 了。
Node.js 不仅自己带命令行 REPL,还暴露了一个 repl 核心模块,让你能在代码里自己起一个。下面这种用法就是例子:
js
// 创建一个自定义 REPL,并注入一个变量
const repl = require('repl');
const r = repl.start('my-app> ');
r.context.greeting = 'Hello from Bun!';
// 运行后,在 REPL 里可以直接使用 greeting 变量
// my-app> greeting
// 'Hello from Bun!'
这个模块能改历史记录、加自动补全、定制输出格式什么的。一些框架(比如 NestJS 那个交互式脚手架)会拿它来在运行时临时开个调试口。
那 Bun 咋就不实现它呢?其实原因挺直接的。Bun 自己就有个更猛的 REPL(直接输 bun 就进),原生支持 TypeScript、JSX、Top-level await,连 npm 包都能自动补全。再说 Node 那个 repl 模块内部挺复杂,牵涉一堆终端控制和事件循环适配,搬到 JSC 上重写不划算。况且真正常用 REPL 的人基本就是开个终端敲一敲,几乎没人会在自己代码里 new 一个 REPL 实例出来。
所以这事儿不是"做不到",是"不值得做"。AI 倒是能帮你把 repl 模块的源码翻译成 Rust,但它不会替你做这个判断------Bun 团队心里清楚得很,自己有个更好的方案,这功能优先级就排到后面去了。这种取舍,AI 替不了。
AI 到底搞不定啥?
你可能会想:"那既然有差异,就想办法兼容呗,AI 写代码这么猛,差哪儿补哪儿不就行了?"
这话听着挺对,但问题在于,AI 能"翻译"代码,没法"适配"行为。下面拿两个具体例子说说。
例子一:流式请求里的"背压"
Node.js 的 http 客户端发大文件的时候,常这么写:fs.createReadStream().pipe(request)。底下走的是 V8 + libuv 的背压机制------TCP 缓冲区满了就自动暂停读,缓冲区空出来再发 'drain' 事件接着读。
想在 JSC 上把这一套原样复刻,得做这些事:自己模拟 libuv 的写缓冲区水位线判断;让 JSC 的异步 I/O 跟 JS 层的 'drain' 事件触发时机对上;处理 stream.write() 返回 false 之后的等待逻辑。
那 AI 能干啥?它确实能把 Node.js 的 _write 实现翻译成 Rust 代码。但翻译完跑起来,背压行为跟 V8 是不是一模一样?AI 验证不了啊------它不知道 JSC 的事件循环啥时候处理 I/O 回调,也不知道微任务队列、定时器精度的差异会怎么影响流控。
Bun 现在的选择就是:先不搞流式发送,干脆把整个请求体缓冲起来。这是个工程上的取舍------真去模拟流式,成本高得离谱,还会拖累平时那些小请求。这种"值不值得"的事儿,AI 拍不了板。
例子二:async_hooks 的异步追踪
Node.js 的 async_hooks 能追踪每个异步资源从创建到销毁的全过程,做类似 CLS(连续本地存储)那种东西就靠它。这套 API 完全是挂在 V8 内部对 Promise、setTimeout、nextTick 的钩子机制上的。
要在 JSC 上弄出一样的语义来,得在 JSC 的每个异步操作入口和出口都插回调,异步 ID 的生成规则、传播路径得跟 V8 严丝合缝,还得控制住性能开销(Node 自己开了这个都会明显变慢)。
AI 能给你写一个看起来能跑的 createHook,但 ID 传播顺序在复杂场景下(比如好几个 Promise 链套着 setImmediate)跟 Node 一不一样,它保证不了;它也不知道 JSC 对 Promise 的优化策略(微任务调度那些)跟 V8 有啥差别,钩子触发时机容易错位。
Bun 一直拖着没实现 async_hooks.createHook,不是写不出来------是写一个行为对得上、性能扛得住的版本,AI 还真搞不定。这得靠真正懂两个引擎内部实现的工程师一行一行去抠。
那 AI 到底干了啥?
话说回来,AI 也没那么废。在 Bun 的开发里,AI 确实有出力:它能根据 Node 的行为描述批量生成边界测试用例,让工程师拿来跟 Bun 的实现对比;也能把 Node 的 TypeScript 类型定义转成 Bun 那边的格式;像 Buffer 里面某些无状态的方法,AI 写起来挺快。
但 "哪些 API 值得兼容"、"性能跟正确性怎么取舍"、"引擎层的差异咋修"------这些事儿还是得人来定。
总结一下
要是 Node.js 和 Bun 跑在同一个引擎上,AI 几乎能把所有 API 一键迁过来。但现实不是这样啊------它俩跑在两套虚拟机上,设计哲学、内部机制全都不一样。
把 V8 的 API 硬搬到 JSC 上,那不叫"翻译",叫"重新设计"。重新设计这事儿,得先搞明白两边的差异,再做取舍,最后还得反复验证------偏偏这些是 AI 最不擅长的。
所以要是你问"AI 是不是早晚都能搞定"------
- 要是"搞定"是说"写一个看起来能跑的版本",AI 已经能了。
- 要是"搞定"是说"行为完全对得上、性能不缩水、长期能维护",AI 还差得远。
这也是 Bun 那 10% 没兼容的 API 一直存在的原因。好消息是,对绝大多数人来说,Bun 已经够用了(通过率超 90%,Express、Fastify 这些框架无缝跑)。剩下那 10% 的硬骨头,还是得工程师们一块一块啃------AI 是帮手,不是替身。