Deno 1.16 发布说明
Deno 1.16 已标记并发布,包含以下特性和变更
fetch
现在支持获取文件 URL- 支持新的 JSX 转换
- 新的不稳定信号监听 API
Error.cause
现在在控制台中显示- 现在可以显式执行 TLS 连接握手
- Web Streams API 的改进
Deno.startTls
已稳定- 逐测试权限现在已稳定
localStorage
不再需要--location
参数- 支持在中止
AbortSignal
时指定原因 - Deno 到 npm 包构建工具
- WebAssembly 引用类型现已在稳定版中可用
findLast
和findLastIndex
数组方法
如果您已经安装了 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.connectTls
和 Deno.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.ts
和http://example.com/b.ts
以及http://example.com:80/
等位置都将共享相同的存储,但https://example.com/
将不同。 - 如果没有位置说明符,但指定了
--config
配置文件,则使用该配置文件的绝对路径。这意味着deno run --config deno.jsonc a.ts
和deno 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。
findLast
和 findLastIndex
数组方法
Array
和 TypedArray
上的 findLast
和 findLastIndex
方法从数组末尾查找与谓词匹配的元素。
例如
[1, 2, 3, 4].findLast((el) => el % 2 === 0);
// → 4 (last even element)
有关更多信息,请参阅功能说明。