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

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

--allow-read 权限是使用 fetch 从本地文件系统读取文件所必需的。

文件的内容以块的形式读取,因此这是一种通过 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 返回的承诺将拒绝,并带有 TypeError
  • 如果指定路径处的项目是目录,则 fetch 返回的承诺将拒绝,并带有 TypeError
  • 响应中没有设置 content-length 标头。这是因为响应主体是流,因此无法提前知道确切的长度。
  • 响应中没有设置 content-type 标头。如果您想从文件扩展名推断出内容类型,可以使用 media_types 模块,该模块位于 https://deno.land/x 上。

支持新的 JSX 转换

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

它可以使用两种不同的方式。第一种方法是在 .jsx.tsx 文件中使用 @jsxImportSource 标记。例如,要从 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(它也是不稳定的)。

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

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

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

我们很期待社区的反馈。如果您有任何建议,请打开一个问题.

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() 方法,可以调用该方法以显式触发握手。此方法返回一个承诺,该承诺在握手完成后解析。如果该方法在握手已完成之后被调用,则该承诺将立即解析。如果该方法在握手正在进行但尚未完成时被调用,则返回的承诺将在握手完成后解析。

Web 流 API 的改进

此版本引入了 Web 流 API 的多个新功能。

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

感谢 @crowlKats 对 Web 流实现的工作。

Deno.startTls 已稳定

在 Deno 中,有两种方法可以通过 TLS 连接到服务器:Deno.connectTlsDeno.startTls。第一个用于当您想要打开一个 TCP 连接,然后立即开始在该连接上进行 TLS 通信时。第二个用于当您需要先创建一个纯文本 TCP 连接,交换一些数据,然后开始在该连接上进行 TLS 通信时。

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

import { Client } from "https://deno.land/x/[email protected]/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 评论