Deno 1.12 发布说明
Deno 1.12 已发布,包含以下特性和更改
- 支持
generateKey、sign和verifyWeb 加密 API - 原生 HTTP 服务器中的 Websocket 支持
- REPL 中的 TypeScript 支持
- 支持
MessagePort和MessageChannel - WASM 线程支持现已可用
如果您已安装 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 加密实现增加了三项新功能:密钥生成、签名和签名验证。
生成密钥
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 加密 API。
感谢 Divy Srivastava 贡献了此功能。
原生 HTTP 中的服务端 WebSocket 支持
此版本增加了在不稳定的 HTTP 服务器上将传入 HTTP 请求升级为 WebSocket 连接的支持。与原生 HTTP 服务器本身一样,此功能仍不稳定,尚未经过彻底的实战测试。请报告您遇到的任何问题。在过去的几周里,我们修复了 HTTP 实现中的所有已知错误,并初步计划在 1.13 版本中稳定原生 HTTP API。
要将传入的 Request 升级为 WebSocket,您可以使用 Deno.upgradeWebSocket 函数。这会返回一个由 Response 和 WebSocket 对象组成的对象。返回的响应应使用 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://: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://: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
...
undefinedTab 补全显示可能的补全列表
Tab 补全是在 REPL 中快速导航的关键功能。Deno 在多个版本中都支持 Tab 补全,但以前每次按下 Tab 键时,候选补全都是逐个显示的。为了提高具有大量属性的对象的补全可用性,Deno 现在将显示所有可能的补全列表。
支持 MessageChannel 和 MessagePort
此版本再次引入了对 Web 平台 API 的支持:来自 Channel Messaging API 的 MessageChannel 和 MessagePort。此 API 允许创建两个相互关联的 MessagePort,它们可用于在彼此之间复制或传输复杂的 JavaScript 对象。消息端口的特别之处在于它们自身可以通过其他消息端口和在 worker 之间传输。这允许在不同 worker 和主线程之间创建任意数量的“套接字”,数据可以通过这些套接字发送和传输。
一个利用消息端口的库的例子是 comlink。Comlink 是一个微型(1.2k gzip 压缩)库,它通过简单的 RPC 接口而不是相当复杂的 postMessage API 来暴露 Web Worker,从而使使用 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 中,我们解决了一些技术债务,这使得支持接受 Response 或 Promise<Response> 的此 API 的流式版本成为可能,我们很高兴地宣布,WebAssembly.compileStreaming() 和 WebAssembly.instantiateStreaming() 也已得到全面支持。
感谢 Andreu Botella 贡献了此更改。
Atomics 和 SharedArrayBuffer 在 Worker 之间共享
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 版本中,SharedArrayBuffers 和由它们支持的类型化数组不能传递给平台 API(例如 fetch、Deno.read、TextDecoder)。在 Deno 的未来版本中可能会增加在某些平台 API 中使用 SAB 的支持。
FinalizationRegistry 和 WeakRef 现在工作可靠
V8 两年多前增加了对 FinalizationRegistry 和 WeakRef 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 中。

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

之后

提示现在不仅包含进程的 PID,还包含主模块的 URL。
测试运行器改进
此版本为 deno test 子命令带来了两项小改进
--shuffle=<SEED>
添加了一个名为 --shuffle 的新标志。您可以使用此标志以随机顺序执行测试用例。此标志有助于捕获测试之间意外的执行顺序依赖性,这通常意味着非确定性测试。
感谢 Casper Beyer 贡献了此功能。
--fail-fast=<N>
deno test 支持 --fail-fast 标志,该标志会在第一个测试失败时停止测试套件的执行。在此版本中,我们为此标志添加了一个可选参数,允许指定在 N 个测试失败后停止执行的阈值。
感谢 Yasser A.Idrissi 贡献了此功能。
为 fetch 添加自定义代理支持
有时需要为出站请求使用 HTTP(S) 代理。以前,您可以使用 HTTP_PROXY、HTTPS_PROXY 和 NO_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。这增加了对以下新语言特性的支持
Array、String 和 TypedArrays 上的 at() 方法
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 错误的快速修复操作
依赖悬停信息

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 年第三季度的计划功能和发布时间表。欢迎提出反馈意见。

