瑞恩·达尔(Ryan Dahl)上周宣布,Deno 团队加入 Cloudflare,Deno 运行时将在未来一年内逐步停止,JSR 继续在 Cloudflare 的基础设施上运行;按照 Cloudflare 的公告,团队的新任务是将 Deno 的 celld 合并进 workerd。这一宣布的时间距离 Deno 2 发布正好两年后的同一天,而 Bun 在十个月前加入了 Anthropic。
正文

上周,Ryan Dahl 宣布 Deno 团队加入 Cloudflare。Deno 运行时将在未来一年里逐步结束投入,JSR 继续运行在 Cloudflare 的基础设施上;而据 Cloudflare 的帖子,团队的新职责是把 Deno 的 celld 合并进 workerd。
这则宣布恰逢 Deno 2 发布两周年。十个月前,Bun 加入了 Anthropic。人们很容易就此得出结论:替代运行时的实验已经结束,Node 不战而胜。但现实是,生态获得了 Deno 所推动的那些好处,只不过并不是来自 Deno 本身。
竞争是健康生态的必要条件
遗憾的是,Deno 在生产环境中从未真正达到临界规模;2026 年的 Node 与 2018 年一样占据主导。Deno 真正的成就是重新激发并给在位者 Node 施加压力,而且这种压力并非单向。
Deno 起步时是一次彻底决裂。2018 年,Ryan 在 JSConf EU 上列举了他会与 Node 做得不同的地方:默认拒绝的权限、TypeScript 作为一等输入、服务器上的 Web 标准 API、仅支持 ES 模块、单个可执行文件。他还特意强调:没有 npm、没有 package.json,也没有 node_modules。依赖就是 URL。当时的幻灯片说得很直接:“没有必要列出依赖。”
我们这些在 npm 和 Node 上深耕多年的人,当时对最后一点表示怀疑——不是怀疑权限或 TypeScript,那些明显是好的——并且公开说过。我们经历过热链接时代:每个页面都从别人的服务器拉取 jquery.min.js,一旦文件被移动或攻破就会崩溃。URL 只能告诉你某个东西曾在哪里,不能说明它是什么。一旦你在意“测试时的字节就是交付的字节”,就需要完整性哈希和记录它的地方,于是你又发明了锁文件。Node TSC 的 Matteo Collina 在 2020 年说:“又一次 left-pad 事件正在等着发生,但这一次没有‘中央实体’能够修复它。”这些怀疑有些是防御性的,但最后被证明是对的。
Deno 1.0 于 2020 年按最初设定发布,随后四年里一点点把这些设定收了回去。先是用于重新导出每个 URL 的 deps.ts 约定:一个徒有 manifest 之名的文件。接着是 lock.json,每个 URL 带一个完整性哈希:这就是锁文件。然后是 import maps,让你又能写裸模块说明符。Deno 自己的文档如今建议用注册表而非 HTTPS 导入,因为后者“可能在不同文件中漂移到不同版本”并且“信任服务主机”。这些正是 2018 年的反对意见,如今被重述为官方指引。
接着 Bun 出现了。2022 年 7 月发布的 Bun 0.1 做了相反的赌注:为速度从零重建工具链,但能运行现有 npm 包、读取 package.json,并作为 node 的直接替代品。大约六周后,Deno 发布《Deno 即将迎来重大变化》,宣布支持 npm: 说明符,承诺三个月内让 80–90% 的 npm 包可用,并树立新目标成为“最快的 JavaScript 运行时”。Deno 将这种转变归功于用户反馈,但时间点表明 Bun 是其中很大一部分原因。2024 年 10 月的 Deno 2 以完整的 Node 与 npm 兼容性完成了这一转变。在作为 Node 生态的替代品推出四年后,Deno 变成了运行它的另一种方式。
这是正确的决定,也等于承认生态不可能在一夜之间抛弃积攒了十多年的依赖。Cloudflare 在 2022 年开源的 workerd 也出于同样原因长出了自己的 Node 兼容层:因为包都在那里。
Node 最终不得不回应。自 2018 年以来,它陆续提供了全局 fetch、WebSocket、Web Streams 和 Web Crypto;稳定的测试运行器和 --watch;--env-file 与单可执行文件构建;稳定的权限模型;默认开启的类型剥离;node:sqlite;以及 require(esm)。Node 的贡献者(很多是志愿者)完成了倾听并做出重大改变的艰苦工作,值得高度赞扬,但我认为如果没有另外三个运行时率先实现其中许多想法,它不会在这个时间线上做到这些。一个成熟的开源项目很少缺少好点子,它缺少的是紧迫感,而竞争对手提供了这种紧迫感。
标准工作也来自同样的压力:WinterCG 成立于 2022 年,因为几个重要的运行时把“最小的公共 API 是什么?”变成了现实问题;如今作为 Ecma TC55,它应当比接下来任何整合的运行时都活得更久。
包管理器与注册表
在历史的大部分时间里,JavaScript 包生态都是单一文化:一个客户端、一个注册表,以及一份非正式到实际上有十五年未成文的 API 契约。此后的几乎每一项改进,都来自某个拒绝接受现状的人。
Yarn(2016)坚持安装必须是确定性的,并带来了锁文件、workspaces 和离线缓存;npm 5 在第二年发布了 package-lock.json。pnpm 证明了 node_modules 不必是数百 MB 的重复文件。Bun 让安装快到在位者不得不重新审视自己的性能。Deno 主张在引入依赖前应该能读到它的源码;虽然 URL 导入没能持续,但这个理念留了下来。
最近最清晰的例子是安装脚本。pnpm 和 Bun 默认不再运行它们;vlt 从未运行过。我在 npm 工作期间,这个主张一直被提出,却从未成为默认设置。今年 6 月,在一连串供应链蠕虫之后,GitHub 宣布 npm 12 也将默认停止运行安装脚本。这些年里,这个想法本身没有任何改变;只是足够多的其他包管理器实现了它,npm 再也无法把它视为激进。压力也是双向的:我们从 pnpm 借来了 catalogs,而 pnpm 的 namedRegistries 也借用了我们的命名注册表别名。
在注册表方面,JSR 值得称赞。它做了一系列与我们不同的赌注:发布 TypeScript 源码、要求 ESM、从代码生成文档。我们不同意其中部分做法,但因为用户要求,仍然为 vlt 添加了 jsr: 支持。它能在逐步关停中存活下来,因为据我所知,它运行成本低、API 有文档,而且 Cloudflare 有理由让它继续活着。
现在有什么变化
两个最流行的、由风投支持的独立 JavaScript 运行时,如今都进入了业务重心在别处的公司。Jarred Sumner 说,Bun 团队与 Claude Code 团队的关系“可能类似于 Google Chrome 与 V8 之间的关系”,但拥有更大独立性;而 Cloudflare 的帖子明确表示,workerd 的路线图就是 Cloudflare 的路线图。JavaScriptCore 和 V8 一直就是这样获得资金的,但这仍然是一种变化。
是否会出现 Deno 运行时的社区分叉,将告诉我们生态是否足够重视一个默认安全、标准优先且不依附于某朵云的运行时,并愿意为此买单。而 celld 并入 workerd 则在一个此前选择极少的层面带来了新的可能性:由 Ryan 和 Bert Belder 领导的、可自托管的 Workers 与 Durable Objects 开源实现。
JavaScript 几乎在每一层都有保险:三个生产引擎、多个运行时、多个包管理器。当某一层发生整合(正如运行时层正在发生的那样),其他层会稳住局面。让这成为可能的变动,也正是人们抱怨最多的东西。“JavaScript 疲劳”十年来一直是个持续的笑话:每周一个新框架,每月一个新打包器,还有一个又一个包管理器——是的,我知道我自己也贡献了一个。但没有人需要离开 Node 就能获得 fetch、权限模型或无需构建步骤的 TypeScript;也没有人需要更换包管理器就能让安装脚本默认关闭。这些东西出现在人们已经在用的工具里,因为别人先实现了它们,而在位者不得不回应。项目不一定总能赢,但理念通常能赢,而且最终用户无论如何都拿到了这些好处。
致 Ryan、Bert、Bartek 以及 Deno 团队的其他成员(无论是过去还是现在的):你们的共同努力丰富了我们的共同生态。谢谢!
评论
多数人对 Deno 原有形态因被 Cloudflare 收购而终结感到惋惜,并担忧 Deno Deploy 应用、数据与未来分支的命运;分歧主要集中在 Deno 的演变是务实进化还是被炒作反噬,以及 Bun 等替代运行时是否真的让 Node 默认获胜。
肯定 Deno 的贡献与脚本体验,对其原有形态消失感到惋惜
- 多位使用者怀念其脚本化体验:可直接链接依赖运行、无需 node_modules 或独立安装步骤,且是单文件二进制并能自更新;有人原本用它跑智能体工具链的脚本,现在可能退回 Node.js。(认可度:较多人)
- 也有人感谢 Deno 虽未成为长期必备工具,却像当年的 io.js 一样真正推动了 Node 生态的演进,只是最终如 RethinkDB 般慢慢淡出。(认可度:较多人)
- 还有人特别感谢团队做出了社区公认“史上最酷、最有趣、最聪明”的产品发布视频,为告别保留了一点轻松与幽默。(认可度:少数人) > 直接链接依赖、无需 node_modules 且能自更新的单文件二进制体验非常难得,若有活跃分支我会继续用。(@tracker1)
担忧大厂收购后托管服务与分支命运,希望出现活跃分支
- 有人一直想用 Deno 的发布平台和内置数据库部署小程序,却始终担心应用和数据的长期归宿,现在更关心 Deno Deploy 上既有应用会怎样。(认可度:较多人)
- 也有人指出 Bun 和 Deno 现在都成了大厂附属,短期可能有资源利好,长期则未必;因此希望社区出现接手的分支。(认可度:较多人) > 希望有分支接手;Bun 和 Deno 现在都成了大厂附属,这既可能变好也可能变坏。(@maelito)
批评 JavaScript 生态炒作与高流失,认为 Deno 现状早有先兆
- 长期质疑 Deno 的人认为,这次结果印证了 JavaScript 生态被炒作驱动、极端频繁更替的积弊,盲目追逐新框架和运行时终会反噬整个生态。(认可度:较多人)
- 有人进一步怀疑,在“原版 Node”和“根本不是 Node 的替代方案”之间,像 Deno 这种“大体像 Node 但更少缺陷”的运行时并没有足够生态位。(认可度:较多人) > JavaScript 生态里有一大群人被炒作驱动、极端频繁更替,盲目跳到每个新框架和运行时而不顾长期模式,最终只会伤害自己和整个生态。(@RadiozRadioz)
不认同替代运行时实验终结,认为 Bun 在大厂下反而更好用
- 针对“Bun 加入 Anthropic 意味着替代运行时实验结束、Node 默认获胜”的轻易结论,有人反驳说 Bun 其实比以往更好用、更方便。(认可度:少数人) > 虽然 Bun 加入 Anthropic 后容易让人得出替代运行时实验已结束、Node 默认获胜的结论,但 Bun 其实比以往更好用、更方便。(@yayugu)
有价值的补充
- 有一条对 Deno 设计演化的技术澄清值得记录:deps.ts 与 import maps 不应被简单视为“开倒车”。 > deps.ts 只是普通 TypeScript 文件的约定而非运行时内置特性,import maps 也不是倒退而是实现浏览器 JavaScript 的平台特性、走向与浏览器一致,整体而言 Deno 是从固执走向务实、成为 Node 的直接替代。(@crabmusket)