跳至主要内容
Deno 2 终于来了 🎉️
了解更多

Deno 1.9 版本发布说明

今天我们发布了 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 版本中,操作调用遵循请求/响应模式,将其数据编码在 ArrayBuffers 的自定义“有效负载”中。历史上,这些有效负载使用了各种编码,从 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 工作者以及在动态导入中(使用 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() 现在允许通过使用命令字段来查询执行特定二进制文件的权限。

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 脚本语义保持一致。此选项不能在用户代码中覆盖。我们预计大多数用户不需要更改其代码。