四年磨一剑:Deno 2.0 终于来了!

4,839 阅读15分钟

原文:Announcing Deno 2

作者:Ryan Dahl、Bert Belder、Bartek Iwańczuk、Andy Jiang 2024年10月9日

译者注:2020 年 5 月,Deno 1.0 横空出世,而今历时 4 年半(怀胎比哪吒还久),Deno 2.0 终于发布了!新的版本结合了 Deno 1 的简洁性、安全性和性能,并且完全兼容 Node 和 npm,此外还有更多功能。按照惯例,此类文章链接较多,大部分已去除,读者可阅读原文查看。


有兴趣的读者可去YouTube观看我们的发布视频。

Deno 2 发布

Web 是人类最大的软体平台——为它开发意味着有可能触及超过 50 亿人。但随着近年来 Web 开发的加速,开发也变得越来越复杂和难以管理。在编写代码之前,开发人员需要处理繁琐的配置和无关的模板代码,而他们真正想做的却是快速交付产品,为用户创造价值。

尽管有这些复杂性,JavaScript 作为 Web 的语言,依然是过去十年中最受欢迎的编程语言,而 TypeScript 也迅速崛起为第三大语言。这证明了 JavaScript 在 Web 开发中的普遍性和实用性——这也表明 JavaScript 的地位短期内不会改变。

为了简化 Web 编程,我们创建了 Deno:一个现代化、开箱即用的零配置 JavaScript 和 TypeScript 开发工具链。

  • 原生支持 TypeScript
  • 基于 Web 标准构建:Promises、fetch 和 ES 模块
  • 内置工具:内置格式化工具、代码检查、类型检查、测试框架、编译成可执行文件等
  • 默认安全,和浏览器一样

如今,数十万开发人员喜欢使用 Deno,其代码库已经成为GitHub 上星数最多的 Rust 项目之一,仅次于 Rust 语言本身。

虽然我们在 Deno 1 中取得了巨大成就,但下一个主要版本将重点放在 Deno 的规模化使用。这意味着与旧的 JavaScript 基础设施无缝互操作,支持更广泛的项目和开发团队,而不牺牲 Deno 用户所喜爱的简洁性、安全性和“内置工具”特性。

今天,我们很高兴宣布 Deno 2,新版本包含:

  • 与 Node.js 和 npm 的向后兼容,让你可以无缝运行现有的 Node 应用
  • 原生支持 package.jsonnode_modules
  • 新的包管理命令:deno installdeno adddeno remove
  • 标准库已稳定
  • 支持私有 npm 注册表
  • 支持工作区和 monorepo
  • 提供长期支持(LTS)版本
  • JSR:一个在不同运行时中共享 JavaScript 库的现代注册表

我们还在不断改进许多现有的 Deno 功能:

  • deno fmt 现在可以格式化 HTML、CSS 和 YAML
  • deno lint 现在支持 Node 专用规则和快速修复
  • deno test 现在支持使用 node:test 编写的测试
  • deno task 现在可以运行 package.json 脚本
  • deno doc 的 HTML 输出有了更好的设计和搜索功能
  • deno compile 现在支持代码签名和 Windows 上的图标
  • deno serve 可以在多个核心上并行运行 HTTP 服务器
  • deno init 现在可以搭建库或服务器项目
  • deno jupyter 现在支持输出图片、图表和 HTML
  • deno bench 支持关键部分,以实现更精确的测量
  • deno coverage 现在可以输出 HTML 格式的报告

向后兼容,面向未来

Deno 2 向后兼容 Node 和 npm。这不仅让你可以在当前的 Node 项目中使用 Deno,还可以逐步采用 Deno 的全能工具链。例如,在克隆 Node 项目后,你可以使用 deno install 以极快的速度安装依赖项,或使用 deno fmt 格式化代码,而无需使用 Prettier。

Deno 2 对 Node 和 npm 的兼容性非常强大。Deno 2 能识别 package.jsonnode_modules 文件夹,甚至支持 npm 工作区,允许你在任何使用 ESM 的 Node 项目中运行 Deno。如果需要进行小的语法调整,你可以使用 deno lint --fix 进行修复。

在此查看视频

如果你不喜欢 package.jsonnode_modules 目录的杂乱,但仍需要使用 npm 包,你可以通过 npm: 规范符直接导入 npm 包。无需 package.jsonnode_modules 文件夹,Deno 会将包安装在全局缓存中。这让你可以在一个文件中编写带有 npm 依赖的程序——不需要依赖清单、配置文件或 node_modules

import chalk from "npm:chalk@5.3.0";

console.log(chalk.blue("Hello, world!"));
// Hello, world! (in blue)

对于大型项目,依赖清单可以让你轻松管理依赖项。将 npm: 规范符放入 deno.json 文件中的导入映射中,可以简化包的导入:

{
  "imports": {
    "chalk": "npm:chalk@5.3.0"
  }
}
import chalk from "chalk";

console.log(chalk.blue("Hello, world!"));
// Hello, world! (in blue)

通过 npm: 规范符导入 npm 包,你可以在 Deno 中访问超过 200 万个 npm 模块。这甚至包括像 gRPC、ssh2、Prisma、temporal.io、duckdb、polars 这样复杂的包。Deno 甚至支持像 Node-API 本地插件这样的高级功能。

最后,你可以在最喜欢的 JavaScript 框架中使用 Deno 2。Deno 2 支持 Next.js、Astro、Remix、Angular、SvelteKit、QwikCity 以及许多其他框架。

视频:使用 Deno 运行 create-next-app

Deno 现在是一个包管理器,支持 deno install

Deno 2 不仅支持 package.jsonnode_modules 文件夹,还带来了三个重要的子命令,让你可以轻松安装和管理依赖项。

deno install 可以以极快的速度安装依赖项。如果你有 package.json 文件,它会迅速创建一个 node_modules 文件夹。如果你没有使用 package.json,它会将所有依赖项缓存到全局缓存中。

deno install 比 npm 冷缓存情况下快 15%,热缓存情况下快 90%。虽然我们已经非常快了,但在未来几周,特别是在冷缓存的情况下,性能还会进一步提升。

deno adddeno remove 可以分别用于将包添加或移除到你的 package.jsondeno.json 中。如果你之前使用过 npm installnpm remove,那么这些命令会非常熟悉。

JavaScript 注册表

今年早些时候,我们推出了一个现代的、开源的 JavaScript 注册表**,称为 **JSR。

它原生支持 TypeScript(你可以以 TypeScript 源代码的形式发布模块),处理多个运行时和环境中的模块加载复杂性,只允许 ESM 模块,根据 JSDoc 风格的注释自动生成文档,并且可以和 npm 和 npx 类似的系统一起使用(是的,JSR 会将 TypeScript 转换为 .js.d.ts 文件)。

因为你将 TypeScript 上传到 JSR,它能够对所发布的代码有极好的理解。这让我们可以为发布和使用模块的开发者提供无缝的体验。如果你对这些细节感兴趣,可以阅读我们关于如何构建 JSR的文章。

将包发布到 npm 和发布到 JSR 的 并排对比视频

标准库现已稳定

虽然 npm 上有超过 200 万个模块可供使用,但搜索、评估和使用新模块的过程可能耗时。因此,我们花了 4 年时间构建了 Deno 标准库

标准库包含数十个经过严格审核的实用模块,涵盖从数据操作、与 Web 相关的逻辑到 JavaScript 特定功能等各个方面。它在 JSR 上可用,并且可以被其他运行时和环境使用。

为了让你了解 Deno 标准库中可用模块的类型,以下是部分标准库模块及其在 npm 中的等价物列表:

Deno 标准库模块npm 包
@std/testingjest
@std/expectchai
@std/climinimist
@std/collectionslodash
@std/fmtchalk
@std/netget-port
@std/encodingrfc4648

欲了解完整的可用包列表,请访问 jsr.io/@std

私有 npm 注册表

Deno 2 中的私有 npm 注册表与在 Node 和 npm 中的工作方式相同,使用 .npmrc 文件配置

@mycompany:registry=http://mycompany.com:8111/

Deno 会自动检测此 .npmrc 文件,并让你无需额外配置即可拉取私有包。

工作空间和 monorepos

Deno 2 还支持工作空间,这是管理 monorepos 的强大解决方案。只需在 deno.json 中使用 workspace 属性列出成员目录:

{
  "workspace": ["./add", "./subtract"]
}

这些成员可以拥有各自的依赖项、linter 和格式化程序配置等。

Deno 不仅支持 Deno 包的工作空间,它还理解 npm 工作空间。这意味着你可以创建一个 Deno 和 npm 的混合 monorepo(参见此示例),其中的工作空间成员可以包含 package.jsondeno.json 文件:

这个示例 monorepo 包含 npm 成员和 Deno 成员的混合

你还可以通过运行 deno publish 将工作空间成员发布到 JSR。有关示例,请参考 Deno 标准库。不需要手动确定发布包的顺序,只需运行 deno publish,它会为你完成所有操作。

LTS(长期支持)

在大型组织中,开发团队通常需要仔细审核新版本才能在生产环境中使用。由于 Deno 每周都有 bug 修复发布,以及每 6 周发布一个小版本,这对于团队来说可能会非常耗时。为了让这些团队更轻松地管理版本更新,我们引入了长期支持(LTS)发布渠道

从 Deno 2.1 开始,LTS 渠道将在 6 个月内接收回溯的关键错误修复,确保在生产环境中使用时有一个稳定可靠的基础。6 个月后,将基于最新的稳定版本创建一个新的 LTS 分支。所有 LTS 版本都可免费获取,并且采用 MIT 许可证,方便任何需要更稳定和安全环境的团队使用。

从 Deno 2.1 开始,我们将引入 LTS 分支,并在 6 个月内维护和回溯关键 bug 修复

最后,对于需要高级支持的团队,**我们推出了 **Deno 企业服务计划。该计划提供优先支持、直接与我们的工程师联系、保证的响应时间以及对功能请求的优先处理。我们与 Netlify、Slack 和 Deco.cx 等公司合作,帮助他们的工程师更快地开发并为用户提供更多价值。

Deno 很快!

我们投入了大量精力让 Deno 在各种实际场景中保持高速表现。我们的重点是提升在日常 JavaScript 和 TypeScript 开发中的性能,不论是启动时间、处理复杂请求,还是整体的效率。

虽然基准测试不能全面展现性能,但它们能够提供一些关于运行时表现的洞见。以下是一些展示 Deno 优势的基准测试,证明它在开发者和生产环境中都能提供卓越的性能。

请参考每张图表下方的链接,获取更多详细信息和可重现的步骤

编辑: 第一个 HTTP 基准测试 是使用 Deno 1.45 进行的,而不是 Deno 2.0。实际上,Deno 2.0 的性能比图中显示的慢约 20%。这是由于 我们最近禁用了 V8 指针压缩 以解决用户超过 4GB 堆内存限制的问题。我们计划很快重新启用指针压缩,因为它是大多数用户的理想默认设置。此外,我们将引入 deno64 版本,适用于需要更大堆内存的用户。对此错误我们深表歉意。

常见问题

如果 Deno 完全向后兼容 Node,为什么我还要使用 Deno 而不是 Node?

虽然 Deno 可以运行 Node 程序,但它的设计初衷是推动 JavaScript 和 TypeScript 向前发展。Deno 提供了 Node 所不具备的功能,比如原生的 TypeScript 支持、符合 Web 标准的 API、完整的 JavaScript 开发工具链,以及默认安全的执行模型——这些都包含在一个无需外部依赖的单一可执行文件中。使用 Deno 代替 Node 可以减少设置和配置的时间,让你更快地进入编码和交付价值的阶段。

运行 Node 程序时,Deno 的选择性权限系统是否仍然生效?

是的,Deno 默认安全的执行模型在运行 Node 程序或导入 npm 模块时同样适用,确保相同的安全级别。

为什么换了新 logo?原来的可爱恐龙吉祥物去哪了?

从一开始,那个在雨中的可爱蜥脚类恐龙就一直是 Deno 的标志。它的独特魅力始终是 Deno 的象征,但设计风格一直不统一——至少有两个“官方”版本,还有无数变体。随着 Deno 2.0 的发布,我们决定是时候进行一次更新了。

我们希望在保留 Deno 用户喜爱的原始角色精髓的同时,为其赋予更加精致的外观,以匹配 Deno 专业和生产级的特性。在重新设计的过程中,我们意识到虽然雨中的背景充满怀旧感,但在缩小时不易辨识,显得过于繁复,因此我们不得不放弃它。

经过多次迭代,我们发现简化设计并突出核心元素可以达成理想的平衡——既简单友好,又严肃可靠,这正是 Deno 的特点。

(别担心,那只可爱的恐龙还在!)

Deno 起初的愿景是为了现代化 JavaScript。如今为了向后兼容所做的工作,Deno 最初的愿景还剩多少?

重写整个 JavaScript 生态系统并不现实。随着 Deno 的规模超越小型程序,我们意识到支持 Node 和 npm 的兼容性至关重要——尤其是像 gRPC 和 AWS SDK 这样的工具,重写它们几乎不可能。

但 Deno 的目标并不是成为一个用 Rust 实现的 Node 克隆,或是 Node 的替代品。我们的目标是提升 JavaScript,使其超越 2010 年代的 CommonJS,缩小服务端和浏览器环境之间的差距,让开发者能够实际采用。我们不愿接受 JavaScript 必须永远停留在混乱的工具链和层层转译的状态下,无法进化。

Deno 的最初愿景依然是我们一切工作的核心。这包括原生的 TypeScript 支持、内置的 Web 标准(如 Promises、顶级 await、Wasm、fetch 和 ES 模块),以及内置工具链——所有这些都打包在一个无需依赖的可执行文件中。当然,它的默认安全特性也和 Web 一样。

支持 npm 只是让 Deno 更加多样化的一步。我们的使命是提供一个现代化、简化的工具链,提升 JavaScript 的开发体验,而不仅仅是为了支持遗留代码。虽然我们调整了策略,但我们的愿景始终不变:简化并增强 Web 开发。

我喜欢 Deno 是因为它不需要任何配置文件,但随着新包管理器的加入,Deno 2 会变得越来越像 Node,需要 package.json 来添加依赖吗?

完全不会。你仍然可以运行无需任何配置或依赖清单的单文件程序或脚本——这一点并没有改变。新的包管理命令(deno installdeno adddeno remove)是可选工具,旨在简化依赖管理,无论你是使用 deno.json 还是 package.json 文件。它们对更大、更复杂的项目特别有用,但如果你更喜欢无配置的简单性,它们不会成为障碍。

我们的核心目标之一是确保 Deno 可以缩减到简单的单文件程序,而不需要额外的操作就能导入任何包。例如,在 Jupyter 笔记本或快速脚本的场景下,你可以轻松做到:

import * as Plot from "npm:@observablehq/plot";

同时,Deno 也能扩展到处理包含多个文件或多个包的大型项目,如 monorepo。这种灵活性确保了 Deno 对于小型脚本和大型生产级应用都同样有效。

我有一个 Fresh 项目。如果升级到 Deno 2,会有破坏性变更吗?

不会!你的 Fresh 项目可以直接在 Deno 2 中运行,无需任何更改。

Deno 2 什么时候会发布到 Deno Deploy 上?

很快!随时都可能发布。

接下来是什么

Deno 2 集成了开发者喜爱的 Deno 1.x 的所有功能——零配置、为 JavaScript 和 TypeScript 开发提供的一体化工具链、支持 Web 标准 API、默认安全性等——并且完全向后兼容 Node 和 npm(在 ESM 中)。这不仅让在任何 Node 项目中运行 Deno 变得简单,还允许在更大、更复杂的项目中逐步采用 Deno(例如运行 deno fmtdeno lint)。再加上改进的包管理、JSR 和一系列为高级开发团队设计的功能,Deno 现已准备好简化并加速你的开发过程。

不过,鉴于 Deno 的强大功能,我们无法在一篇博客和视频中涵盖所有内容。Deno 还有许多令人兴奋的功能和应用场景尚未提及。例如,使用 deno compile 将一个 JavaScript 游戏编译为桌面可执行文件,并支持跨平台编译(是的,包括 Windows)。又比如 Deno 的 Jupyter 笔记本支持,它可以让你使用 TypeScript 和 @observable/plot 来探索和可视化数据。或者通过 deno doc 从你的 JSDoc 注释和源代码生成文档或静态文档网站。

我们邀请你今天就开始使用 Deno 2,体验 JavaScript 和 TypeScript 开发的未来。立即开始使用 Deno 2:

加入我们的社区,一起塑造 JavaScript 的未来吧!