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

Deno 1.12 版本说明

Deno 1.12 已经发布,包含以下功能和变化

如果您已经安装了 Deno,则可以通过运行以下命令升级到 1.12

deno upgrade

如果您是第一次安装 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

新功能

支持更多 Web 密钥功能

如上一个版本博客文章中所宣布,此版本为我们的 Web 密钥实现添加了三种新功能:密钥生成、签名和签名验证。

要生成密钥

const keyPair = await crypto.subtle.generateKey(
  {
    name: "RSA-PSS",
    modulusLength: 2048,
    publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
    hash: { name: "SHA-256" },
  },
  true,
  ["sign", "verify"],
);

然后对一些数据进行签名

const data = new TextEncoder().encode("hello world");
const signature = await crypto.subtle.sign(
  { name: "RSA-PSS", saltLength: 32 },
  keyPair.privateKey,
  data,
);

然后验证签名

const isValid = await crypto.subtle.verify(
  { name: "RSA-PSS", saltLength: 32 },
  keyPair.publicKey,
  signature,
  data,
);

有关微妙的加密 API 的更多信息,请查看MDN 上的文档

我们正在努力在下个版本中启用更多 Web 密钥 API。

感谢 Divy Srivastava 为此功能做出的贡献。

原生 HTTP 中的服务器端 WebSocket 支持

此版本添加了对将不稳定 HTTP 服务器上的传入 HTTP 请求升级到 WebSocket 连接的支持。就像原生 HTTP 服务器本身一样,此功能仍然不稳定,尚未经过彻底的实战测试。请报告您遇到的任何问题。在过去几周中,我们修复了 HTTP 实现中的所有已知错误,并计划在 1.13 版本中稳定原生 HTTP API。

要将传入的 Request 升级到 WebSocket,您可以使用 Deno.upgradeWebSocket 函数。这将返回一个包含 ResponseWebSocket 对象的对象。返回的响应应使用 respondWith 方法来响应传入的请求。此时,WebSocket 处于活动状态,可以使用。

因为 websocket 是一个对称协议,所以 WebSocket 对象与可用于客户端通信的 WebSocket 对象相同。可以在MDN 上找到它的文档。我们意识到此 API 可能难以使用,并计划在 WebSocketStream 稳定并可以使用后切换到WebSocketStream

以下是如何使用新 API 的示例

// https://deno.org.cn/blog/v1.12/ws_server.js

async function handleConn(conn) {
  const httpConn = Deno.serveHttp(conn);
  for await (const e of httpConn) {
    e.respondWith(handle(e.request));
  }
}

function handle(req) {
  if (req.headers.get("upgrade") != "websocket") {
    return new Response("not trying to upgrade as websocket.");
  }
  const { websocket, response } = Deno.upgradeWebSocket(req);
  websocket.onopen = () => console.log("socket opened");
  websocket.onmessage = (e) => {
    console.log("socket message:", e.data);
    websocket.send(new Date().toString());
  };
  websocket.onerror = (e) => console.log("socket errored:", e.message);
  websocket.onclose = () => console.log("socket closed");
  return response;
}

const listener = Deno.listen({ port: 8080 });
console.log("listening on https://127.0.0.1:8080");
for await (const conn of listener) {
  handleConn(conn);
}

您可以使用 deno run --allow-net --unstable https://deno.org.cn/blog/v1.12/ws_server.js 运行此代码段。要连接到 websocket,请打开 Deno REPL 并运行以下命令

> const ws = new WebSocket("ws://127.0.0.1:8080/");
  ws.onmessage = (e) => console.log(e.data);
> ws.send("hi");

感谢 crowlKats 为此功能做出的贡献。

REPL 获得 TypeScript 支持以及更多改进

此版本为 REPL 带来了一个备受期待的功能 - TypeScript 支持。在 1.12 之前,Deno REPL 只能执行 JavaScript 代码。这限制了 REPL 的可用性:如果您想在 REPL 中复制粘贴一些 TypeScript 代码,它将无法工作。唯一的办法是手动剥离类型注释。使用此版本,REPL 能够实时地转译您的 TypeScript 代码(即剥离类型)并执行生成的 JavaScript 代码。

之前

$ deno
Deno 1.11.5
exit using ctrl+d or close()
> function log(message: string) { console.log(message) }
Uncaught SyntaxError: Unexpected token ':'

之后

$ deno
Deno 1.12.0
exit using ctrl+d or close()
> function log(message: string) { console.log(message) }
undefined

请记住,REPL 中不支持类型检查。我们不确定此功能的可用性,因此如果您有任何反馈,请在denoland/deno#11078上评论。

还有一些提高使用体验的改进

静态导入声明支持

由于 REPL 在“脚本上下文”(而不是 ES 模块上下文)中运行,因此无法在其中使用 import ... from ...; 语法。为了进一步增强 REPL 的可用性,我们添加了将静态导入声明自动转换为动态导入声明的功能。

之前

$ deno
Deno 1.11.5
exit using ctrl+d or close()
> import { serve } from "https://deno.land/[email protected]/http/server.ts";
Uncaught SyntaxError: Cannot use import statement outside a module

之后

$ deno
Deno 1.12.0
exit using ctrl+d or close()
> import { serve } from "https://deno.land/[email protected]/http/server.ts";
Download https://deno.land/[email protected]/http/server.ts
...
undefined

Tab 补全显示可能的补全列表

Tab 补全对于在 REPL 中快速导航至关重要。Deno 在几个版本中一直支持 Tab 补全,但是,候选补全是在每次按下 Tab 键时逐个显示的。为了提高对具有大量属性的对象的补全的可用性,Deno 现在将显示所有可能的补全列表。

支持 MessageChannelMessagePort

此版本再次引入了对 Web 平台 API 的支持:来自通道消息 APIMessageChannelMessagePort。此 API 允许创建一个包含两个相互关联的 MessagePort,它们可以用于在彼此之间复制或传输复杂的 JavaScript 对象。使消息端口特殊的是,它们本身可以被传输到其他消息端口和工作线程之间。这允许在不同的工作线程和主线程之间创建任意数量的“套接字”,数据可以通过它们发送和传输。

使用消息端口的库的一个示例是comlink。Comlink 是一个很小的(1.2k 压缩)库,它通过使用简单的 RPC 接口而不是相当复杂的 postMessage API 来使 Web 工作线程更易于使用。以下是一个示例,我们在其中使用 Comlink 将生活在 Web 工作线程中的简单计数器公开给主线程

// https://deno.org.cn/blog/v1.12/comlink/main.js

import * as Comlink from "https://cdn.skypack.dev/[email protected]?dts";

// Start a worker with the code in ./worker.js
const url = new URL("./worker.js", import.meta.url);
const worker = new Worker(url, { type: "module" });

// Let comlink wrap this worker to provide a nice API
const obj = Comlink.wrap(worker);

// Call methods and get properties on the object exposed from the worker, as if
// it is a local value.
console.log(`Counter: ${await obj.counter}`);
await obj.inc();
console.log(`Counter: ${await obj.counter}`);

worker.terminate();
// https://deno.org.cn/blog/v1.12/comlink/worker.js

import * as Comlink from "https://cdn.skypack.dev/[email protected]?dts";

// Create the object to expose. It has a counter property, and a method to
// increment that counter.
const obj = {
  counter: 0,
  inc() {
    this.counter++;
  },
};

// Expose the object to the host side using comlink.
Comlink.expose(obj);

您可以使用 deno run --allow-net https://deno.org.cn/blog/v1.12/comlink/main.js 自己运行此示例。

有关 Comlink + Deno 的更多信息,请查看此问题:https://github.com/GoogleChromeLabs/comlink/issues/553

WebAssembly 的流式实例化和异步编译支持

Deno 从 v1.0.0 开始支持 WebAssembly,但仅支持缓冲实例化 API(即 WebAssembly.compile()WebAssembly.instantiate())。

在 v1.12 中,我们能够解决一些技术债务,使我们能够支持此 API 的流式版本,这些版本接受 ResponsePromise<Response>,我们很高兴地宣布 WebAssembly.compileStreaming()WebAssembly.instantiateStreaming() 现在也得到了完全支持。

感谢 Andreu Botella 为此更改做出的贡献。

Atomics 和 SharedArrayBuffer 在 worker 之间的共享

JavaScript 是一种为单线程运行而设计的语言。因此,所有对象、基本类型和函数只能从一个线程访问。当使用 Web Workers API 在不同线程上创建多个 JS 上下文时,这有时会受到限制。它们只能通过传递克隆的消息相互通信。

在许多其他语言中,您可以以一种可以在一个线程上修改数据并在另一个线程上反映更改的方式在线程之间共享数据。

随着 SharedArrayBuffer 的引入,JavaScript 也获得了在线程之间可变共享数据的能力。这也带来了同步对这些缓冲区的读写操作机制的必要性:Atomics

此版本支持在 web worker 之间传递 SharedArrayBuffer,并支持 Atomics。这为将 C、C++ 或 Rust 代码编译为 WebAssembly 并支持多线程开辟了无限的可能性。ffmpeg.wasm 是一个广泛使用此功能的项目示例。这是一个流行的 FFmpeg 库的纯 WebAssembly / JavaScript 端口,它允许您转换和处理不同的视频和音频格式。

要了解更多关于 webassembly 多线程的信息,请查看 Ingvar Stepanyan 在 web.dev 上的帖子

请注意,在此版本的 Deno 中,SharedArrayBuffer 和由它们支持的类型化数组不能传递给平台 API(例如 fetchDeno.readTextDecoder)。在 Deno 的未来版本中可能会添加在某些平台 API 中使用 SAB 的支持。

FinalizationRegistryWeakRef 现在可以可靠地工作

V8 在 两年前 添加了对 FinalizationRegistryWeakRef API 的支持。这些 API 存在于 Deno 中,但是我们收到了很多报告,表明它们无法正常工作。

由于偿还了与 WebAssembly API 相同的底层技术债务,Deno v1.12 正式支持终结器和弱引用。

在 Chrome DevTools 中提供更好的调试支持

Deno 具有内置的调试功能,可以使您使用远程调试器(如 Chrome Devtools 或 VSCode 内置调试器)。

在尝试查找错误或分析您的应用程序时,这些工具非常有用;并且确保完整的调试体验至关重要。

在 v1.12 中,我们为 Chrome Devtools 集成带来了两个提高生活质量的改进

在 DevTools 和终端之间传递控制台消息

调试功能中的一个长期问题是控制台输出没有打印到 DevTools 控制台。在 v1.12 中,此问题已得到修复,控制台日志在两种方式之间在终端和 DevTools 之间传递,即从您的应用程序代码或 DevTools 控制台中使用的所有控制台日志记录语句都将在您的终端和 DevTools 中显示。

DevTools console

Chrome Devtools 提示

我们改进了 about://inspect 页面上显示的提示,以便轻松区分多个正在运行的 Deno 进程。

之前

about://inspect prompt before

之后

about://inspect prompt after

提示现在不仅包含进程的 PID,还包含主模块 URL。

测试运行程序的改进

此版本为 deno test 子命令带来了两项小改进

--shuffle=<SEED>

添加了一个名为 --shuffle 的新标志。您可以使用此标志以随机顺序执行测试用例。此标志对于捕获测试之间意外的执行顺序依赖关系很有用,这通常意味着非确定性测试。

感谢 Casper Beyer 为此功能做出的贡献。

--fail-fast=<N>

deno test 支持 --fail-fast 标志,该标志在第一个测试失败时停止测试套件的执行。在此版本中,我们为该标志添加了一个可选参数,允许您指定在 N 个测试失败后停止执行的阈值。

感谢 Yasser A.Idrissi 为此功能做出的贡献。

fetch 添加自定义代理支持

有时需要对出站请求使用 HTTP(S) 代理。以前,您可以使用 HTTP_PROXYHTTPS_PROXYNO_PROXY 环境变量在应用程序范围内配置此选项。此版本增加了对在每个 fetch 基础上配置代理的支持。

为此,您必须使用不稳定的 Deno.createHttpClient 函数创建一个 Deno.HttpClient,并将此客户端传递给 fetch,作为初始化程序中的 client 参数。

const client = Deno.createHttpClient({
  proxy: {
    url: "http://myproxy.com:8080",
    basicAuth: { username: "deno", password: "***" },
  },
});
const response = await fetch("https://myserver.com", { client });

感谢 Tomofumi Chiba 为此功能做出的贡献。

readFile 中的 AbortSignal 支持

此版本增加了在 readFile 中指定中止信号的支持。这使您能够在文件太大或读取时间过长的情况下终止文件读取。

const aborter = new AbortController();
Deno.readFile("./super_large_file.txt", { signal: aborter.signal })
  .then((data) => console.log("File read:", data.length))
  .catch((err) => console.error("File read failed:", err));
setTimeout(() => aborter.abort(), 1000);

将来可能会在其他 API 中添加对中止的支持。

感谢 Benjamin Gruenbaum 为此功能做出的贡献。

弃用 Deno.copy

此版本弃用了 Deno 命名空间中的 Deno.copy 实用函数。linter 现在将警告任何使用此 API 的情况。

您可以使用来自 https://deno.land/[email protected]/io/util.ts 的完全相同的 copy 函数作为替代。

在 TypeScript 配置中支持 types 选项

Deno 支持 有限的 TypeScript 选项集

此版本添加了对 types 属性 的支持,该属性可用于在检查程序时包含任意类型定义。

此选项在程序执行之前的类型检查、语言服务器和不稳定的 Deno.emit() 运行时 API 中受支持。

新的 JavaScript 语言特性

此版本将 Deno 所构建的 V8 JavaScript 引擎升级到 9.2。这增加了对以下新语言特性的支持

at() 方法在 Array、String 和 TypedArrays 上

at() 方法返回给定索引处的 value。特殊之处在于 at() 支持使用负索引进行索引

let arr = [1, 2, 3, 4];
arr.at(-1); // Returns 4

为 Intl.DateTimeFormat 添加 dayPeriod 选项

dayPeriod 选项(ECMA402 2021 的一部分)已添加到 Intl.DateTimeFormat() 方法中,以便调用者可以格式化时间,例如“早上 7 点”、“上午 11 点”、“中午 12 点”、“下午 1 点”、“下午 6 点”、“晚上 10 点”(或中文“清晨 7 时”,“上午 11 时”,“中午 12 时”,“下午 1 时”,“下午 6 时”,“晚上 10 时”。

语言服务器的改进

此版本为内置的 Deno 语言服务器带来了另一组稳定性改进和功能,最值得注意的是

快速修复操作以忽略 deno lint 错误

依赖关系悬停信息

Dependency hover information

Deno 命名空间现在已解冻且可配置

在此版本之前,对 Deno 全局命名空间的任何更改都是不可能的,因为在运行时引导后,该对象被“冻结”。

从 v1.12 开始,Deno 全局命名空间不再被冻结,并且是可配置的。您可以更改或删除现有属性,甚至添加新的属性

之前

$ deno
Deno 1.11.5
exit using ctrl+d or close()
> Deno.someProperty = "foo";
Uncaught TypeError: Cannot add property someProperty, object is not extensible
    at <anonymous>:2:8

之后

$ deno
Deno 1.12.0
exit using ctrl+d or close()
> Deno.someProperty = "foo";
"foo"

这种能力不应过度使用,但在测试期间可能派上用场,当您发现自己需要例如模拟来自一个网络 API 调用的返回值时。

修复具有循环依赖关系的并发动态导入

此版本修复了 ES 模块加载实现中的一个长期存在的问题,如果用户尝试并发动态导入具有循环依赖关系的代码,可能会导致挂起或崩溃。触发此故障的确切情况非常具体,因此该错误在野外可能很少见。

如果您有兴趣了解更多详细信息,请访问 denoland/deno#3736

感谢 Nayeem Rahman 贡献了此修复。

Web 平台兼容性状态更新

在过去几个月里,我们一直在努力将越来越多的跨浏览器 Web 平台测试 套件集成到 Deno 中。此测试套件由 Chrome、Firefox 和 Safari 用于验证所有引擎是否以相同的标准化方式实现 Web API。

在每个版本中,我们都运行并通过了越来越多的 Web 平台测试 - 例如,在 url 套件中,我们现在拥有比 Chrome 和 Firefox 的最新版本更好的规范兼容性。您可以在 wpt.fyi 上查看我们当前的状态并比较兼容性。

Andreu Botella 表示衷心的感谢,他在过去几周内贡献了许多兼容性修复和 WPT 运行器改进。

2021 年第三季度路线图

我们在 路线图问题 中概述了 2021 年第三季度的计划功能和发布计划。欢迎提供反馈。