跳到主要内容
Deno 2.4 已发布,带来 deno bundle、字节/文本导入、OTel 稳定版等更多功能
了解更多

Deno 1.16 发布说明

Deno 1.16 已标记并发布,包含以下特性和变更

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

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

新特性和变更

fetch 现在支持获取文件 URL

此版本新增了对使用 fetch 从本地文件系统读取文件的支持。这对于使用 import.meta.url 获取相对于当前模块的文件非常有用,这通常用于 WASM 模块。

以下是读取 /etc/hosts 文件的示例

const resp = await fetch("file:///etc/hosts");
const text = await resp.text();
console.log(text);

注意:以上示例在 Windows 上将不起作用,因为 /etc/hosts 文件在 Windows 上的位置不同。请尝试以下代码

const resp = await fetch("file:///C:/Windows/System32/drivers/etc/hosts");

使用 fetch 从本地文件系统读取文件需要 --allow-read 权限。

文件内容是分块读取的,因此这是通过 HTTP 流式传输大文件而无需将整个文件加载到内存中的好方法

import { serve } from "https://deno.land/std/http/server.ts";

serve((_req) => {
  const resp = await fetch(new URL("./static/index.html", import.meta.url));
  return new Response(resp.body, {
    headers: { "content-type": "text/html; charset=utf-8" },
  });
});

文件获取的行为并未在任何 Web 规范中指定,但我们的实现是基于 Firefox 中从磁盘读取文件的实现

  • 如果未找到文件,fetch 返回的 Promise 将以 TypeError 拒绝。
  • 如果指定路径上的项是目录,fetch 返回的 Promise 将以 TypeError 拒绝。
  • 响应中未设置 content-length 头。这是因为响应体是一个流,因此无法提前知道确切的长度。
  • 响应中未设置 content-type 头。如果您想从文件扩展名推导出内容类型,可以使用 media_types 模块(位于 https://deno.land/x)。

支持新的 JSX 转换

React 17 引入了一个新的 JSX 转换,它改进了 JSX 转换 API,并允许自动导入 JSX 运行时库。从本版本开始,Deno 支持这些转换。

它可以通过两种不同的方式使用。第一种是在 .jsx.tsx 文件中使用 @jsxImportSource pragma。例如,从 esm.sh 使用 Preact:

/** @jsxImportSource https://esm.sh/preact */

export Welcome({ name }) {
  return (
    <div>
      <h1>Welcome {name}</h1>
    </div>
  );
}

另一个选项是使用配置文件并在项目范围内设置编译器选项

{
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "https://esm.sh/preact"
  }
}

然后通过命令行传递 --config 文件(或在编辑器中设置 deno.config 选项)。

有关更多详细信息,请参阅手册中关于配置 JSX 的部分。

新的不稳定信号监听 API

此版本新增了一个用于监听操作系统信号的不稳定 API。新 API 目前仍处于实验阶段,未来可能会发生变化。它取代了现有的 Deno.signals API(该 API 也是不稳定的)。

const listener = () => {
  console.log("SIGTERM!");
};

// Starts listening to SIGTERM
Deno.addSignalListener("SIGTERM", listener);

// Stops listening to SIGTERM
Deno.removeSignalListener("SIGTERM", listener);

我们很乐意听取社区的反馈。如果您有任何建议,请提交 issue

Error.cause 现在在控制台中显示

自 Deno 1.13 起,Error.cause 属性已受支持,可用于为错误附加原因。这对于调试应用程序深处发生的错误非常有用,允许开发人员将这些错误包装在有用的信息中,以帮助调试问题。

从本版本开始,当通过 console.log 抛出或记录错误时,我们将在控制台中显示 Error.cause 属性。

> throw new Error("main error", { cause: new TypeError("caused by this") })
Uncaught Error: main error
    at <anonymous>:2:7
Caused by TypeError: caused by this
    at <anonymous>:3:12

这与 Node.js 17 的行为一致。

感谢 Kenta Moriuchi 实现了此功能。

现在可以显式执行 TLS 连接握手

在建立 TLS 连接时,在加密连接上读取或写入数据之前,需要执行 TLS 握手。大多数用户无需关心握手的具体细节,这就是为什么当用户首次尝试在连接上读取或写入数据时,我们会自动执行握手。

此版本向 Deno.TlsConn 添加了一个 handshake() 方法,可以调用该方法来显式触发握手。此方法返回一个 Promise,该 Promise 在握手完成后解析。如果该方法在握手已完成后调用,则 Promise 将立即解析。如果该方法在握手正在进行但尚未完成时调用,则返回的 Promise 将在握手完成后解析。

Web Streams API 的改进

此版本引入了 Web Streams API 的多项新功能

  • ReadableStreamBYOBReader 现已受支持。这些“自带缓冲区”(也称为 BYOB)ReadableStream 读取器允许将数据读入开发人员提供的缓冲区,从而与常规 ReadableStreamDefaultReader 相比,最大限度地减少复制。您可以在 MDN 上阅读有关此 API 的更多信息。
  • WritableStreamDefaultController.signal 现已受支持。有关更多信息,请参阅说明
  • ReadableStream.getIterator 已被移除。此方法自 Deno 1.7 以来已弃用,并且从未在任何浏览器中实现。ReadableStream 本身始终实现了 AsyncIterable 协议,因此在迭代流时请改用它。

感谢 @crowlKats 在 Web Streams 实现方面所做的工作。

Deno.startTls 已稳定

在 Deno 中,有两种通过 TLS 连接到服务器的方式:Deno.connectTlsDeno.startTls。前者用于您希望打开 TCP 连接然后立即在其上开始 TLS 通信的情况。后者用于您需要首先创建纯文本 TCP 连接,交换一些数据,然后在其上开始 TLS 通信的情况。

Deno 1.16 稳定了 Deno.startTls API。这使得为稳定版 Deno 编写 SMTP 驱动程序成为可能。它也允许为稳定版 Deno 编写 Postgres 和 MySQL 驱动程序。https://deno.land/x/postgres 驱动程序现在在 Deno 稳定版中完全可用。

import { Client } from "https://deno.land/x/postgres@v0.14.0/mod.ts";

const client = new Client({
  user: "user",
  database: "test",
  hostname: "psql.example.com",
  port: 5432,
  tls: {
    enforce: true,
    caCertificates: [await Deno.readTextFile("/path/to/ca.crt")],
  },
});
await client.connect();

const result = await client.queryObject("SELECT id, name FROM people");
console.log(result.rows); // [{id: 1, name: 'Carlos'},  ...]

逐测试权限现在已稳定

早在 Deno 1.10 中,我们引入了一个允许您为每个测试设置权限的功能。这使得测试程序在不同权限设置下的行为变得超级容易。此功能现已稳定:它不再需要 --unstable。有关示例,请查看上面链接的 1.10 博客文章。

请记住,测试用例请求的权限不能超过使用 --allow-* 标志授予进程的权限。如果权限对象中省略了某个键,则其值将从相应的 --allow-* 标志继承。

localStorage 不再需要 --location 参数

在 Deno 的早期版本中,localStorage API 只能在用户使用 --location 标志启动 Deno 进程时使用。此版本在您不使用 --location 标志启动 Deno 时,为 localStorage 使用的存储桶添加了一个隐式不透明密钥。其派生方式如下:

  • 当使用 --location 标志时,位置的源用于唯一存储数据。这意味着 http://example.com/a.tshttp://example.com/b.ts 以及 http://example.com:80/ 等位置都将共享相同的存储,但 https://example.com/ 将不同。
  • 如果没有位置说明符,但指定了 --config 配置文件,则使用该配置文件的绝对路径。这意味着 deno run --config deno.jsonc a.tsdeno run --config deno.jsonc b.ts 将共享相同的存储,但 deno run --config tsconfig.json a.ts 将不同。
  • 如果没有配置或位置说明符,Deno 将使用主模块的绝对路径来确定共享哪些存储。Deno REPL 生成一个基于当前工作目录的“合成”主模块,即从启动 deno 的位置。这意味着从相同路径多次调用 REPL 将共享持久化的 localStorage 数据。

一些有助于阐明此行为的示例

# You can persist data, even without --location
$ cat one.js
console.log(localStorage.getItem("file"));
localStorage.setItem("file", Deno.mainModule);
$ deno run one.js
null
$ deno run one.js
file:///tmp/one.js

# The key for the storage bucket is derived from the main module, so a module
# cannot read or write data written by a program with a different main module.
$ cat two.js
console.log(localStorage.getItem("file"));
localStorage.setItem("file", Deno.mainModule);
$ deno run two.js
null
$ deno run two.js
file:///tmp/two.js

# The key is derived from the **main module** (the entrypoint), not the module
# that called `localStorage`!
$ cat three.js
import "./two.js";
$ deno run three.js
null
$ deno run three.js
file:///tmp/three.js

# If you have a config file, that is used as the key for the storage bucket.
$ deno run --config=deno.jsonc one.js
null
$ deno run --config=deno.jsonc one.js
file:///tmp/one.js
$ deno run --config=deno.jsonc two.js
file:///tmp/one.js
$ deno run --config=deno.jsonc one.js
file:///tmp/two.js

# You can use --location to specify an explicit origin though, like before. In
# this case, different main modules (entrypoints) can share a single storage
# bucket.
$ deno run --location=https://example.com one.js
null
$ deno run --location=https://example.com one.js
file:///tmp/one.js
$ deno run --location=https://example.com two.js
file:///tmp/one.js
$ deno run --location=https://example.com one.js
file:///tmp/two.js

更多信息可在手册中找到:https://docs.deno.org.cn/runtime/manual/runtime/web_storage_api

支持在中止 AbortSignal 时指定原因

WHATWG 最近指定了对中止 AbortSignal 时指定原因的支持。Deno 是第一个实现此新功能的平台

const abortController = new AbortController();
abortController.abort();
console.log(abortController.signal.reason); // DOMException: The signal has been aborted

const abortController = new AbortController();
const reason = new DOMException("The request timed out", "TimeoutError");
abortController.abort(reason);
console.log(abortController.signal.reason); // DOMException: The request timed out

感谢 @crowlKats 实现了此功能。

Deno 到 npm 包构建工具

我们继续改进 Node 兼容模式(--compat),但在本版本中没有具体内容要宣布。不过,我们发布了一个名为 dnt 的新系统,用于将用 Deno 编写的模块发布为 npm 包。

默认情况下,dnt 会将您的 Deno 模块转换为规范的 TypeScript,进行类型检查,构建一个包含 TypeScript 声明文件的 ESM 和 CommonJS 混合包,最后在 Node.js 中针对输出运行您的 Deno 测试代码。完成后,您只需 npm publish 输出目录即可。

一个已经使用 dnt 的示例项目是 deno_license_checker,它现在已作为 npm 包发布:https://npmjs.net.cn/package/@kt3k/license-checker

试试看:

npm install -g @kt3k/license-checker

这允许您在 Node 环境中使用 Deno 优先的代码。

目前仍处于早期阶段,但如果您能尝试、彻底测试并检查其输出,并查看出现哪些问题或挑战,我们将不胜感激,以便我们优先处理并解决它们。

V8 更新至 9.7 版本

此版本随 V8 9.7 发布。Deno 1.15 随 V8 9.5 发布,所以这次您将获得两个 V8 版本的新 JS 好东西 😀

像往常一样,V8 版本也带来了一系列性能改进和错误修复。

有关更多详细信息,请参阅 V8 的发布说明:https://v8.node.org.cn/blog/v8-release-97

WebAssembly 引用类型现已受支持

引用类型提案允许在 WebAssembly 模块中不透明地使用来自 JavaScript 的外部引用。externref(以前称为 anyref)数据类型提供了一种安全地持有 JavaScript 对象引用并与 V8 的垃圾回收器完全集成的方法。

此功能在 V8 9.6 中引入。请参阅:https://v8.node.org.cn/blog/v8-release-96

findLastfindLastIndex 数组方法

ArrayTypedArray 上的 findLastfindLastIndex 方法从数组末尾查找与谓词匹配的元素。

例如

[1, 2, 3, 4].findLast((el) => el % 2 === 0);
// → 4 (last even element)

有关更多信息,请参阅功能说明





HN 评论