一、快不是魔法,是换了一套底层
说实话,第一次跑bun run dev,我愣了几秒。不是卡了,是启动太快——快到像没执行。后来看日志,好家伙,一个Express应用,300毫秒变成了30毫秒。这个体感差异,比任何benchmark都震撼。
Bun的快,表面上是广告词,背后是两条:JavaScriptCore + Zig。先说JavaScriptCore,Apple家的引擎,Safari在用。V8的JIT是启发式优化,JSC呢?它更激进,把类型推断直接做进AST。但这不是关键。关键是Bun用Zig写运行时,内存管理不是GC,而是手动控制——好吧,借用Zig的comptime,很多东西在编译期就定死了。这带来的直接好处:没有V8的GC停顿,启动时不预热堆。你可以类比成:V8是开着自动挡的车,Bun是手动挡,换挡时机全由你定——所以快。
等等,这里有个细节。Bun不是照搬Node的API层?是的,它重写了。比如fs,它直接用io_uring,Linux的异步I/O,而Node用的是libuv线程池。这个差异,在高并发文件读取时,Bun的调度成本接近零。

二、数据不说谎:启动、安装、打包

我用wrk做了个简单测试。同一个Hello World HTTP服务,Node 20启动要320ms,Bun 1.2只要35ms。别小看这285ms,在Serverless场景下,冷启动计费就是按这个算的。再说安装依赖,我用一个大项目,npm install 耗时47秒,bun install 只要7秒。为什么?npm是串行解压,每个包打成一个tarball,再逐个解包;Bun把依赖树的整个图一次性解析,并行下载,再通过硬链接减少磁盘写入。这个设计语言,就是工程美学。
不过话说回来,Bun的打包器也有意思。esbuild已经很偏执了,Bun快在零外部依赖,内置了所有算法。比如Tree Shaking,它直接走AST,不搞装模作样的导入导出分析。但注意,别拿Rust那套编译速度来比。Bun是运行时,不是编译器。它的快,是场景化的快。
三、三个坑,每个都让我摔过
第一个坑:原生模块的兼容性。node-gyp编译的addon,Bun尝试用NODE_BINDINGS去兼容,但很多C++写的模块,比如bcrypt,直接崩。这没法忍。解决方案只有一个:换纯JS实现,或等社区出Bun原生版。我用了bun-vite-plugin直接绕开,但这是临时方案。
第二个坑:Node API的“缺角”。你用fs.watch,在Linux上Bun会漏事件。实测监听一个文件,改动三次,只触发两次。要命的是,这个bug现在还没彻底修好。怎么办?自己加一个polling fallback,用setInterval检查mtime。丑,但是稳。
第三个坑:Windows,哎。Bun的Windows支持说实话是二等公民。文件路径的分隔符、符号链接、环境变量,都能踩雷。我在生产环境是Linux,测试机是Windows,常常跑挂。解决方案:强制统一开发环境,用WSL2或Docker。别为了省事在Windows裸跑。
我的经验是:Bun适合新项目,别去动老旧的Node项目——除非你有时间处理坑。
四、工程美学的另一面:集成度
Bun的野心是替换npm、yarn、pnpm、webpack、tsc、jest。这玩意全干,而且干得不错。`bun run`执行速度比npm script快,因为它是直接跑JS,不经过shell层。`bun test`不需要额外配置,内置expect和mock。这减少了多少配置文件的哲学讨论?爽。
但是捆绑意味着锁死。你没法换掉某一部分,除非不用Bun。
一个细节:Bun的模块解析速度,让人想起了Node当年用的是同步查找,Bun用并发遍历,配合包内的manifest缓存。这个优化,使得大项目的启动不需要像Node那样,在require一堆模块时干等。

所以,Bun不是什么未来,它就是现在。但作为架构师,你得更清醒地踩坑。