Skip to main content

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 Crypto 函数

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

要生成密钥

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,
);

有关 Subtle Crypto API 的更多信息,请查看 MDN 上的文档

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

感谢 Divy Srivastava 贡献了此特性。

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

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

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

由于 WebSocket 是对称协议,WebSocket 对象与可用于客户端通信的对象相同。有关它的文档可以在 MDN 上找到。我们意识到此 API 可能难以使用,并计划在 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/std@0.101.0/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/std@0.101.0/http/server.ts";
Download https://deno.land/std@0.101.0/http/server.ts
...
undefined

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

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

支持 MessageChannelMessagePort

此版本再次引入对 Web 平台 API 的支持:来自 Channel Messaging APIMessageChannelMessagePort。此 API 允许创建一个由两个纠缠的 MessagePort 组成的对象,可用于在彼此之间复制或传输复杂的 JavaScript 对象。消息端口的特殊之处在于它们本身可以通过其他消息端口以及在 worker 之间传输。这允许在不同的 worker 和主线程之间创建任意数量的“套接字”,以便可以发送和传输数据。

一个利用消息端口的库示例是 comlink。Comlink 是一个微小的 (1.2k gzip 压缩) 库,它通过简单的 RPC 接口而不是相当复杂的 postMessage API 使 Web Worker 变得愉快。这是一个示例,我们使用 Comlink 将 Web Worker 中存在的简单计数器暴露给主线程

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

import * as Comlink from "https://cdn.skypack.dev/comlink@4.3.1?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/comlink@4.3.1?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 的更多信息,请查看此 issue: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 贡献了此更改。

worker 之间的 Atomics 和 SharedArrayBuffer 共享

JavaScript 是一种设计为在单线程上运行的语言。因此,所有对象、原始类型和函数都只能从单个线程访问。当使用 Web Workers API 时,这有时会受到限制,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,并将此客户端在初始值设定项的 client 参数中传递给 fetch

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/std@0.101.0/io/util.ts 中相同的 copy 函数。

在 TypeScript 配置中支持 types 选项

Deno 支持 有限的 TypeScript 选项集

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

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

新的 JavaScript 语言特性

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

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

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

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

为 Intl.DateTimeFormat 添加 dayPeriod 选项

已为 Intl.DateTimeFormat() 方法添加 dayPeriod 选项(ECMA402 2021 的一部分),以便调用者可以格式化时间,例如“早上 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 platform tests 套件集成到 Deno 中。Chrome、Firefox 和 Safari 使用此测试套件来验证所有引擎是否以相同的标准化方式实现 Web API。

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

非常感谢 Andreu Botella 在过去几周贡献了许多兼容性修复和 WPT 运行器改进。

2021 年第三季度路线图

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