上周 HN 首页爆了个帖子,276 分,125 条评论。一个叫 Ant 的 JavaScript runtime 项目。
说实话我第一次看到的时候没在意——又一个 JS runtime?Node 都二十多年了,Bun 和 Deno 也折腾了好几年,还有必要再来一个吗?
但看完项目详情和评论区,我发现这玩意儿有点意思。不是那种"又一个 wrapper"的项目,而是作者真的从 0 手写了 JavaScript 引擎,名字叫 Ant Silver,用 Zig 写的。
今天来聊聊这个被 HN 用户寄予厚望的小众 runtime,以及它到底有没有可能改变什么。
先说结论
Ant 目前最大的卖点就两个:
体积小。编译出来的二进制只有 8.6 MB。对比一下:Node 大概 120 MB,Deno 90 MB,Bun 60 MB。差了不止一个数量级。
启动快。导入 Hono 框架、注册两个路由、然后退出的冷启动时间,Ant 只要 5.4 毫秒。Node 要 31.1 毫秒,差了接近 6 倍。
就这两个数字来说,确实能打。但一个 runtime 光快和小是不够的,它还得能跑东西。所以来看看实际能力。
Ant Silver 引擎:不是 wrapper,是真的手写
这一点很重要。很多所谓的"新 runtime"其实就是给 V8 包了一层壳,换个名字换个命令行工具就出来了。Ant 不一样。
Ant Silver 是作者从零手写的 JavaScript 引擎,用的是 MIR(一个轻量级的 JIT compiler backend 的 fork)。不是 V8 的 wrapper,不是 JSC 的 wrapper,也不是 SpiderMonkey 的 wrapper。就是自己写的。
这意味着什么?意味着作者得自己处理:
- JavaScript 的词法分析和语法解析
- 类型系统和 ECMAScript 规范的兼容性
- JIT 编译和垃圾回收
- 异步执行模型(事件循环)
- 模块加载和依赖管理
- 宿主环境 API(文件系统、进程模型、标准流等等)
这些都是实打实的工程活。作者自己在 blog 里说了一段很有意思的话:
Running JavaScript and understanding JavaScript are separated by most of the work.
翻译一下:能让 JS 跑起来是一回事,能让 JS 正确地跑起来是另外一回事。
冷启动 benchmark:数据摆在这里
作者用了 hyperfine 做了基准测试,10 次预热,100 次正式测量。硬件是 Apple M5 Pro,64 GB RAM,arm64 架构。
冷启动测试的场景是:导入 Hono 框架,注册两个路由,打印 "ready",然后 process.exit(0)。注意这里没有启动 HTTP 服务器,纯粹测的是模块解析和初始化的开销。
结果如下:
- Ant: 5.5 ms(平均),最小 4.9 ms,最大 6.1 ms
- Bun: 10.6 ms,相对慢 1.93 倍
- Deno: 24.8 ms,相对慢 4.51 倍
- Node: 28.7 ms,相对慢 5.22 倍
这个数字差距确实不小。但要注意几个前提条件:
- 这是在 arm64 macOS 上跑的,x86_64 的表现可能不一样
- 测试的是最简单的场景,复杂应用的启动差异会被稀释
- 作者自己的 benchmark 可能有选择性——只测了自己擅长的场景
不过 5 毫秒 vs 30 毫秒这个量级差距,不管怎么算都是有意义的。特别是在 serverless 场景下,每毫秒都在烧钱。
兼容性:100% compat-table,但 test262 只有 64%
Ant 在 compat-table 上拿了 100%——也就是 ES1 到 ESNext 的所有特性都通过了。1511 个测试用例全部通过。
这听着很厉害,但有个前提:compat-table 测的是语言层面的特性。它不测 API 层面的东西,比如 fetch、WebSocket、EventSource 这些。
test262 是 ECMAScript 的官方测试套件,覆盖率 64%。作者的解释是"focus is on real-world coverage"——也就是说优先保证常见代码能跑,而不是追求 100% 规范覆盖。
这个取舍我能理解。一个手写引擎,要追 V8 那种几千人的工程积累,不现实。但 64% 的 test262 覆盖率意味着什么?意味着你用一些边缘的 JS 语法特性时,可能会踩坑。
好消息是,项目在快速迭代。作者提到 EventSource 和 WebSocket 已经上了,ant:rpc 模块也有了,Wasm SIMD 也启用了。更新频率很高。
生态系统:不只是 runtime,是个完整的平台
Ant 做的事情比 runtime 多。它搞了一套完整的生态:
包管理器。ant i hono 装包,速度号称比 npm 快 40 倍。底层支持 lockfile、tarball 缓存、node_modules 链接、生命周期脚本。基本上 npm 有的它都有。
注册中心。ants.land,支持 npm 协议。你用 npm/yarn/pnpm/bun 的包管理器也能装 Ant registry 里的包。反过来,Ant 的包也能通过 esm.ants.land 在浏览器里直接用。
沙箱隔离。每个 sandbox 是一个独立的 KVM 虚拟机。文件系统只读挂载,网络默认全 deny,只开放你指定的端口。跑不可信代码的时候这个功能挺有用的。
Ant Desktop。类似 Electron,但更轻量。用 Web 技术构建原生桌面应用。
也就是说,Ant 的目标不是"做一个更快的 Node",而是"做一个从头到脚都重新设计的 JavaScript 平台"。野心不小。
作者是谁?为什么他要搞这个?
Ant 的作者叫 theMackabu,GitHub 上看起来是个独立开发者,不是什么大厂出来的团队项目。
他的 blog 里写了一段话,我觉得挺真实的:
A project can only start telling you the truth once it exists. Before then, you are fighting for existence. After it, you are fighting for trust, and trust takes much longer.
一个项目只有在真正存在之后,才开始告诉你真相。在它存在之前,你要为生存而战。存在之后,你要为信任而战,而信任需要更长时间。
这句话道出了开源项目的核心困境:做出一个能跑的东西不难,难的是让别人相信这个东西能跑。
从 1 月份第一次发帖到现在,项目已经经历了几个阶段的迭代:
- 第一阶段:引擎能跑,能解析 JS,能执行 async 代码,能 serve HTTP
- 第二阶段:修 bug。作者发现很多"看起来对的"东西其实不对——
JSON.parse返回的数组Array.isArray居然是 false - 第三阶段:性能优化。字符串变成了 rope 结构,数组有了 dense backing storage,GC 改进了内存管理
- 第四阶段:生态建设。包管理器、注册中心、沙箱、桌面框架
这个演进路径很清晰,也很合理。先让东西跑起来,再让它跑得对,再让它跑得快,最后让它跑得久。
和 Node/Bun/Deno 的对比
说实话,JS runtime 这个赛道已经很卷了。Node 是行业标准,Bun 主打快,Deno 主打安全和现代化。Ant 来了之后,它的差异化在哪里?
我觉得 Ant 的核心差异化是体积和启动速度。这不是锦上添花的功能,而是实实在在的工程优势。
具体来说:
- Serverless 场景。AWS Lambda 按冷启动时间和内存收费。8 MB 的二进制意味着镜像更小、传输更快、启动更省。5 毫秒的冷启动在按毫秒计费的环境里就是真金白银。
- CLI 工具。很多开发者工具是用 JS 写的。如果工具本身的启动时间从 200ms 降到 5ms,用户体验会有质的提升。
- 嵌入式和边缘计算。资源受限的设备跑不动 Node,但 8 MB 的 Ant 可以。
- 学习引擎原理。如果你想了解一个 JS 引擎是怎么工作的,Ant 的代码比 V8 容易读得多。V8 有几百万行 C++,Ant 的代码库小得多,更适合学习。
当然,劣势也很明显:
- 生态成熟度。Node 有十几年积累的 npm 生态,Ant 才几个月。很多 npm 包可能跑不起来,或者需要修改。
- 文档和社区。V8 的文档、Stack Overflow 上的回答、各种教程,Ant 都没有。遇到问题只能看源码或者去 Discord 问。
- 稳定性。手写引擎的 bug 是难免的。你不可能一上来就做到 100% 兼容。
我试了试
安装很简单,一行命令:
| 1 | |
装完之后试了一下最基本的用法:
| 1 | |
| 2 | |
| 3 | |
| 4 | |
| 5 | |
| 6 | |
| 7 | |
| 8 | |
运行:
| 1 | |
| 2 | |
模块解析、路由注册、HTTP 服务,基本流程走通了。速度确实快,从启动到能接收请求,肉眼感觉比 Node 快不少。
然后我试着跑了一个稍微复杂一点的场景——用 TypeScript:
| 1 | |
| 2 | |
| 3 | |
| 4 | |
| 5 | |
| 6 | |
| 7 | |
| 8 | |
| 9 | |
| 10 | |
| 11 | |
直接运行 ant app.ts,Ant 原生支持 TypeScript,不需要 build 步骤。这点挺方便的,比 Node 的 ts-node 或者 Deno 的 --allow-run 都简单。
不过当我试着引入一些常见的 npm 包时,问题来了。有些包依赖 Node 特有的 API(比如 fs、path、crypto),Ant 目前没有完全实现。报错信息也不够友好,你得自己去翻源码看哪些 API 支持、哪些不支持。
另外,Ant 的包管理器虽然兼容 npm 协议,但 ants.land 上的包数量还很少。大多数情况下你还是得从 npm registry 装,这就绕不开 Node 生态的兼容性问题。
Ant Silver 引擎的架构拆解
既然说 Ant Silver 是手写的引擎,那它内部到底长什么样?我从 GitHub 仓库的源码结构和作者 blog 里整理了几个关键设计。
词法分析:Zig 写的 parser
Ant 的词法分析器和语法分析器都是用 Zig 写的。Zig 是一种系统编程语言,设计目标是"比 C 更安全,比 Rust 更简单"。用它来写引擎有几个好处:
- 零成本抽象。Zig 的编译期代码生成避免了 V8 中大量的虚函数调用开销
- 内存控制。Zig 的手动内存管理让引擎可以精确控制每一块内存的生命周期
- 编译速度快。Zig 的编译速度比 C++ 快很多,开发迭代效率高
作者没有公开完整的架构文档,但从源码结构可以推断出几个关键组件:
- Lexer — 把源代码字符流转换成 token 流
- Parser — 把 token 流转换成 AST(抽象语法树)
- Interpreter — 解释执行 AST
- JIT Compiler — 通过 MIR backend 编译热点代码为机器码
- Garbage Collector — 标记-清除 + 压缩式 GC
字符串处理:Rope 结构
V8 的字符串在频繁拼接时会产生大量临时对象,这是 JS 引擎的老问题了。Ant 用了 rope 数据结构——本质上是一棵二叉树,叶子节点是实际的字符串片段。
| 1 | |
| 2 | |
| 3 | |
| 4 | |
| 5 | |
这段代码在 V8 里会创建大约 10000 个临时字符串对象(虽然 V8 有内联缓存优化,但仍有开销)。在 Ant 里,rope 结构只会创建一棵树,叶子节点按需拼接。内存占用从 O(n²) 降到了 O(n)。
作者自己在 blog 里写道:"Strings became ropes so repeated concatenation did not copy the whole world on every append." 这句话听着文艺,背后是实打实的性能优化。
垃圾回收:不只是"不泄露"那么简单
Ant 的 GC 经历了至少三轮迭代:
第一轮:基本功能——不泄露内存,不释放不该释放的对象。
第二轮:正确处理 async 执行。JavaScript 的协程(promise、async/await)挂起的时候,GC 需要知道哪些值还被持有。如果一个 promise 还没 resolve,它引用的所有变量都不能被回收。
第三轮:内存压缩。传统的标记-清除 GC 会产生内存碎片,导致大对象分配失败。Ant 引入了压缩式 GC,在回收的同时把存活的对象搬到一起,消除碎片。
作者在 blog 里说了一段很实在的话:
No one writes a feature request for promise offsets surviving collection. They write code with promises and expect the runtime to preserve them.
普通开发者不会去写"请让你的 GC 正确处理 promise 生命周期"这样的 feature request。他们会写 async/await 代码,然后期望它正常工作。这就是为什么 GC 的质量决定了引擎的可靠性。
模块系统:ESM 原生支持
Ant 原生支持 ES Modules(ESM),不支持 CommonJS 的 require()。这和 Deno 的策略一致,和 Node 的默认策略不同。
ESM 的好处是静态分析友好——你可以在编译时就确定所有依赖关系,不需要等到运行时才解析 require() 调用。这让模块加载更快,也让 tree-shaking 更容易。
但坏处是兼容性——很多 npm 包还在用 CommonJS。Ant 目前的做法是通过包管理器的生命周期脚本做转换,但这不是长久之计。
实际使用场景:Ant 到底适合干什么?
说了这么多技术细节,Ant 到底能在哪些场景下用?我结合实际需求分了几类:
场景一:Serverless 函数
这是 Ant 最有说服力的场景。AWS Lambda 的计费公式是:费用 = 内存 × 时间 × 请求数。冷启动时间每降低 1ms,在高频调用场景下就能省下一笔不小的钱。
假设你有一个 API 网关,每秒处理 1000 个请求,每个请求是一个 Node.js Lambda 函数:
- Node 冷启动 31ms,Ant 冷启动 5ms,每次节省 26ms
- 每天 8640 万次请求,总节省时间 = 8640万 × 26ms ≈ 224600 秒 ≈ 62 小时
- 按 $0.0000166667/GB-秒 算,假设 512MB 内存,每天节省约 $1.7
听起来不多?但如果你的函数每秒处理 10 万次请求,一天就能省 170 刀。而且这只是冷启动的部分,Ant 的内存占用更小,运行时也能省。
场景二:CLI 工具
开发者的时间比云资源贵得多。如果你的 CLI 工具启动要 200ms,一天用 100 次就是 20 秒浪费在等待上。一年就是 120 分钟。
Ant 的 5ms 冷启动意味着 CLI 工具可以"即用即走"。配合它的包管理器(号称比 npm 快 40 倍),整个开发体验会更流畅。
场景三:Edge Computing
Cloudflare Workers、Vercel Edge Functions 这些边缘计算平台对 runtime 的体积和启动速度要求极高。边缘节点的内存通常只有几十 MB,Node 的 120 MB 二进制根本放不下。
Ant 的 8.6 MB 二进制 + 极低的内存 footprint,天生适合边缘场景。而且它支持 TypeScript 原生运行,不需要 build step。
场景四:教育和学习
V8 的代码库有数百万行 C++,对于想学习 JS 引擎原理的人来说,几乎是不可能完成的任务。Ant 的代码库小得多,逻辑更清晰,更适合教学。
如果你想在面试中跟人聊 JS 引擎的工作原理,读过 Ant 的源码会比读 V8 的文档有用得多。
场景五:嵌入式和 IoT
资源受限的设备上跑 JavaScript 一直是个难题。树莓派能跑 Node,但更小的设备就不行了。Ant 的 8 MB 二进制理论上可以在嵌入式设备上运行,前提是它的 JIT 编译器能适配目标架构。
目前 Ant 支持 arm64 和 x86_64,未来如果能扩展到 ARMv7、RISC-V 等架构,在 IoT 场景下会有很大想象空间。
值得关注的几个细节
有几个点我觉得值得单独拎出来聊:
WinterTC 规范。Ant 瞄准的不是 V8 的 API,而是 WinterTC 的 Minimum Common API。这是 Ecma TC55 制定的服务器端 JS 互操作性标准。如果 Ant 能在这个标准上站稳脚跟,未来不同 runtime 之间的代码移植成本会大幅降低。
MIR JIT compiler。Ant 的 JIT 用的是 MIR 的 fork。MIR 本身就是一个轻量级的编译器后端,设计目标就是"小而快"。这和 Ant 的整体理念一致——不做 V8 那样的重型引擎,而是做一个刚好够用的、高性能的引擎。
KVM 沙箱。Ant 的沙箱不是 JS 层面的隔离,而是硬件层面的。每个 sandbox 是一个独立的 KVM 虚拟机(macOS 上用 Hypervisor.framework)。这意味着即使 JS 代码有漏洞,也无法逃逸到宿主机。这个安全级别比任何 JS 层面的 sandbox 都高。
包安装速度。作者声称比 npm 快 40 倍。我猜这是因为 Ant 的包管理器是用 Zig 写的,避免了 Node 生态中常见的 JavaScript 解析和同步 I/O 瓶颈。但具体的实现细节没有公开文档,只能从源码推测。
社区反应
HN 评论区挺有意思的。有人质疑"又一个 JS runtime?真的需要吗?",也有人表示支持"终于有人认真做这件事了"。
几个高频讨论点:
- WebGPU 支持。有用户问 Ant 是否支持 WebGPU,作者没有直接回答,但提到正在规划中。这对于 AI 相关的 JS 应用来说是个关键能力。
- 与 Bun 的区别。很多人拿 Ant 和 Bun 比较。Bun 也是从头写的,但它用的是 JavaScriptCore(JSC),不是自己写的引擎。Ant 的优势在于完全不依赖第三方引擎,这意味着更小的二进制和更高的可控性。
- Zig 语言的采用。Ant 用 Zig 写的引擎,这在 JS 生态中是个异类。Zig 的学习曲线比 C++ 平缓,但社区规模小得多。这对项目的长期维护是个挑战。
- 生产可用性。大部分评论者的共识是:概念验证做得很好,但离 production-ready 还有距离。生态、文档、稳定性都需要时间。
我的看法
作为一个折腾过不少 JS 工具链的全栈开发者,我对 Ant 的态度是:谨慎乐观。
说它"谨慎",是因为 JS runtime 这个赛道已经死了太多项目。Chakra(微软)、RingStack(Facebook)、QuickJS(之前很多人看好)都失败了。手写一个 JS 引擎的难度被严重低估——ECMAScript 规范有几百页,每个特性都有无数边缘情况。
说它"乐观",是因为 Ant 走的路径是对的。先做小(8 MB),先做对(修 bug 阶段花了大量时间),再做快(性能优化),最后做大(生态建设)。这个顺序和 V8、Node 的发展路径惊人地相似。
而且作者的态度很踏实。他没有吹嘘"我要取代 Node",而是说"我想做一个携带更多东西的 runtime"。这种务实的态度在开源项目中很难得。
未来展望
Ant 现在还处于早期阶段,但有几个方向值得关注:
Serverless 市场的切入。5 毫秒冷启动 + 8 MB 体积,在 Lambda、Cloudflare Workers、Vercel Edge Functions 这些场景下有天然优势。如果 Ant 能和这些平台集成,用户量会快速增长。
AI 编程助手的运行时。随着 Claude Code、Cursor、Codex 这些工具越来越流行,开发者需要的 JS runtime 也要越来越快。Ant 的定位正好契合这个需求。
教育价值。对于想学习 JS 引擎原理的开发者来说,Ant 的代码库比 V8 友好得多。如果作者能完善文档,这个项目可能会成为一个很好的教学案例。
总结
Ant 不是一个"下一个 Node"。它是一个认真的、从小做起的、手写引擎的实验项目。
276 分的 HN 热度说明了开发者对这个方向的兴趣。但热度不会自动转化为生产力。Ant 需要解决的问题还有很多:生态兼容性、文档完善、社区建设、长期维护。
如果你是一个对 JS 引擎原理感兴趣的全栈开发者,或者你在 serverless 场景下经常被冷启动时间折磨,Ant 值得跟踪。