跳到主要内容
Deno 2.4 已发布,带来 deno bundle、bytes/text 导入、稳定的 OTel 等功能
了解更多

今天我们发布 Deno 1.9.0。此版本包含许多新功能、性能改进和错误修复。

如果您已安装 Deno,可以通过运行 deno upgrade 升级到 1.9。如果您是首次安装 Deno,可以使用以下方法之一:

# Using Shell (macOS and Linux):
curl -fsSL https://deno.land/x/install/install.sh | sh

# Using PowerShell (Windows):
iwr https://deno.land/x/install/install.ps1 -useb | iex

# Using Homebrew (macOS):
brew install deno

# Using Scoop (Windows):
scoop install deno

# Using Chocolatey (Windows):
choco install deno

新功能和变更

原生 HTTP/2 Web 服务器

Deno 中当前的 HTTP 服务器 std/http 是在 TCP 套接字之上用纯 TypeScript 实现的。尽管使用了脚本化的 HTTP 服务器,但它却具有出人意料的良好尾部延迟。但是 std/http 的主要缺点是它仅支持 HTTP/1.1 - 并且没有简单的路径可以向前发展到 HTTP/2。

最终,我们不想从事编写 HTTP 服务器的业务。HTTP 变得越来越复杂,并且原生代码中已经有实现良好的 HTTP 服务器。

因此,我们使用了 Hyper 在 Deno 中构建一个新的原生 HTTP/2 服务器 API。

与 std/http 纯 TypeScript HTTP 服务器相比,该绑定将 hello-world 的吞吐量提高了 48%。

response time histogram

我们希望尽快稳定这个新的 API,但目前您必须使用 --unstable 标志。请测试它并向我们提供反馈。

const body = new TextEncoder().encode("Hello World");
for await (const conn of Deno.listen({ port: 4500 })) {
  (async () => {
    for await (const { respondWith } of Deno.serveHttp(conn)) {
      respondWith(new Response(body));
    }
  })();
}

我们特别注意使用与 fetch() API 相同的 RequestResponse 对象。Request 和 Response 对象都具有可流式传输的正文,允许与客户端进行全双工通信。

另请参阅下面关于 ALPN 的部分,这对于通过 TLS 宣传 HTTP/2 是必需的。

使用 serde_v8 更快地调用 Rust

我们重建了绑定基础设施,使其显著更简单、更快:我们从核心中删除了超过 1500 行代码,将基线绑定(又称操作或操作调用)开销提高了多达 ~65 倍-98%,并建立了清晰的操作基础,这对于我们未来的发展(插件、未来优化等)将非常有用。

在 Deno 的早期版本中,操作调用遵循请求/响应模式,将其数据编码为 ArrayBuffer 的自定义“有效负载”。历史上,这些有效负载使用了各种编码,从 JSON、FlatBuffers 到自定义二进制编码……这不仅是性能瓶颈,也是复杂性和碎片化的主要来源。

@AaronO 建议,与其在这些二进制格式、JS 和 Rust 之间来回序列化,不如更有效地在 v8 和 Rust 值之间直接序列化。遵循这一见解和快速原型,serde_v8 应运而生。serde_v8 旨在在 v8 和 Rust 值之间提供“最大效率”或“零开销”的双射,同时保持表达性和熟悉性(因为它建立在 David Tolnay 出色的 serde 库之上)。

基线操作开销是一个重要的基准,它衡量给定类别操作调用的最小成本(每调用纳秒数)

op_baseline chart

这些操作层面的改进并非仅仅是学术性的,它们显著提高了 Deno 的效率,并有助于在我们的 HTTP 基准测试中实现吞吐量和延迟增益。您应该会在自己的 Deno 程序中看到改进,尤其是在高负载下或以前受操作调用效率瓶颈影响的程序。

deno_common chart

正如您所看到的,Deno 中的许多常见函数现在速度提高了约 3 倍。

Blob URL 支持及 fetch 改进

此版本引入了对 blob:(也称为对象 URL)的支持。用于创建和撤销 blob URL 的 API 与浏览器中相同

const blob = new Blob(["Hello World!"]);
const url = URL.createObjectURL(blob);
console.log(url); // blob:null/7b09af21-03d5-461e-90a3-af329667d0ac

const resp = await fetch(url);
console.log(await resp.text()); // Hello World!

URL.revokeObjectURL(url);

Blob URL 可以在 fetch 中使用,通过 new Worker 实例化 Web Worker,以及在动态导入 (使用 import()) 中使用。

除了 blob URL,fetch 现在还支持 data URL。

const resp = await fetch("data:text/plain;base64,SGVsbG8gV29ybGQh");
console.log(await resp.text()); // Hello World!

LSP 中的导入补全

在此版本中,Deno 语言服务器(为 Deno 的编辑器扩展提供支持的工具)获得了一些很棒的新功能和改进。

首先,我们改进并重新引入了旧 VS Code 扩展中的导入补全功能。它允许用户在导入语句中获得补全。LSP 为本地文件、已下载到您的 DENO_DIR 缓存的文件以及注册表补全提供补全功能。

以下是三者的示例

要为 https://deno.land/x 注册表启用补全,请将以下内容添加到您的 VS Code(或其他编辑器)设置中:

{
  "deno": {
    "suggest": {
      "imports": {
        "hosts": {
          "https://deno.land": true
        }
      }
    }
  }
}

注册表自动补全目前由 https://deno.land/x 提供。我们希望更多注册表能够实现注册表协议以支持此新功能。Skypack 注册表已表示兴趣,并且可能很快得到支持。如果您想为自己的注册表添加支持,可以阅读注册表补全文档

除了新的导入补全功能外,我们还实现了 textDocument/foldingRangetextDocument/selectionRange LSP 函数,这些函数使您的编辑器能够在选择时提供更好的文本吸附,并更好地支持代码块的折叠和展开。

此版本还包括对 LSP 的许多错误修复,其中最突出的是一个在 Windows 系统上顽固的错误,它导致 LSP 在遇到特定 file:// URL 时崩溃。

--allow-env--allow-run 的允许列表

Deno 的几个权限标志接受允许列表,这使得能够精细地划分程序的权限。例如,--allow-read=/tmp 仅授予对 /tmp 目录的读取权限。

在 1.9 之前,--allow-env--allow-run 都是“全有或全无”的,这意味着传递这些标志分别授予对环境变量的完全访问权限和在系统上生成任何二进制子进程的权限。

现在可以精确指定程序应该访问哪些环境变量,或者程序被允许生成哪些子进程。

$ deno run --allow-env=DEBUG,LOG https://deno.org.cn/blog/v1.9/env_permissions.ts
$ deno run --allow-run=deno https://deno.org.cn/blog/v1.9/run_permissions.ts

此外,Deno.permissions.query() 现在允许通过使用 command 字段查询执行特定二进制文件的权限。

await Deno.permissions.query({ name: "run", command: "deno" });

交互式权限提示

目前在 Deno 中,如果您运行的程序缺少适当的权限标志,它将抛出错误并退出。在 1.9 中,我们添加了 --prompt 标志,允许用户在运行时根据需要迭代地授予权限。

使用 --prompt 在运行来自互联网的一次性脚本时特别有用——您无需预先知道所有必需的权限,而是可以不带任何权限运行脚本,并根据程序请求逐个授予或拒绝它们。

自行运行演示:deno run --prompt https://deno.org.cn/blog/v1.9/prompt_permissions.ts

请告诉我们 --prompt 对您是否有用。我们正在考虑在未来的版本中默认启用它。

Deno.listenTls 中的 ALPN 支持

HTTP/2 协议与连接无关。这意味着它可以在 Unix 套接字、TCP 套接字或使用 TLS 的连接上使用。主流 Web 浏览器仅允许通过在 TLS 握手期间宣布支持 HTTP/2 的 TLS 连接进行 HTTP/2。这通过“应用层协议协商”TLS 扩展(也称为 ALPN)完成。此 TLS 握手扩展允许 TLS 服务器和客户端协商他们将用于在 TLS 连接上通信的应用程序协议。在 Web 上,两个主要应用程序协议是 HTTP/1.1 和 HTTP/2。它们的 ALPN 协议名称分别为“http/1.1”和“h2”。浏览器只会向宣布支持 HTTP/2 的服务器发送 HTTP/2 请求,如果未列出任何 ALPN 协议,或者 ALPN 协议中仅列出“http/1.1”,则将使用 HTTP/1.1。

在此之前,std/http 服务器仅支持 HTTP/1.1,因此无需在 TLS 连接上支持 ALPN。随着此版本中 Deno.serveHttp 的引入,情况发生了变化。为了使 Deno 中的完整 HTTP/2 成为可能,我们现在添加了对在启动带有 Deno.listenTls 的 TLS 监听器时指定 ALPN 协议的支持。

以下是创建一个完全支持 HTTP/2 的 HTTPS 服务器的示例:

const listener = Deno.listenTls({
  port: 443,
  certFile: "./cert.pem",
  keyFile: "./key.pem",
  alpnProtocols: ["h2", "http/1.1"],
});

for await (const conn of listener) {
  handleConn(conn);
}

async function handleConn(conn: Deno.Conn) {
  const httpConn = Deno.serveHttp(conn);
  for await (const { request, respondWith } of httpConn) {
    respondWith(new Response(`Responding to ${request.url}`));
  }
}

新 API 稳定化

1.9 版本稳定了几个与文件系统相关的 API

  • Deno.fstat
  • Deno.fstatSync
  • Deno.ftruncate
  • Deno.ftruncateSync

此外,以下方法已添加到 Deno.File 类中:

  • File.stat
  • File.statSync
  • File.truncate
  • File.truncateSync

新 API 弃用

为了使使用 Deno 编写的代码能够直接移植到浏览器和其他非 Deno 运行时,我们决定弃用并最终从 Deno 命名空间中删除所有不受系统 API 支持的 API。这些 API 将移至 Deno 标准库,该库也可以在浏览器中使用。

在此版本中,我们弃用了以下 API:

  • Deno.Buffer
  • Deno.readAll
  • Deno.readAllSync
  • Deno.writeAll
  • Deno.writeAllSync
  • Deno.iter
  • Deno.iterSync

这些 API 已移至 std/io 模块。我们已经在 deno lint 中引入了一个新的 lint 规则,它会查找并警告您使用这些不稳定 API 的情况。它还会建议在标准库中可以找到该 API 的位置。

我们计划在 Deno 2.0 版本中移除这些已弃用的 API。在未来的 2.0 预发布版本中可能会引入更激进的弃用消息。请尽快迁移使用这些已弃用 API 的代码。

新的 TypeScript 默认选项 useDefineForClassFields

在此版本中,我们已将 Deno 默认 tsconfig 更改为包含 "useDefineForClassFields": true 选项。此选项使 TypeScript 对类字段的处理与标准 ECMA 脚本语义保持一致。此选项不能在用户代码中覆盖。我们预计大多数用户不需要更改其代码。