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

Deno 1.39:WebGPU 的回归

Deno 1.39 是 Deno 生态系统的一次重大更新,它包含了备受期待的 WebGPU 的回归,增强了图形、游戏和机器学习功能。我们还引入了新的 deno 覆盖率报告程序,以改善代码库分析,并在 Node.js 兼容性方面取得了重大进展,为 Node.js 开发人员简化了过渡。最后,此版本还包括标准库的更新、性能优化以及最新的 TypeScript 5.3 支持。

如果您已经安装了 Deno,请在您的终端中使用以下命令升级到版本 1.39

deno upgrade

如果您尚未安装 Deno,您可以使用以下命令之一安装它,或者 通过其他多种方式 进行安装。

MacOS/Linux 安装

curl -fsSL https://deno.land/x/install/install.sh | sh

Windows 安装

irm https://deno.land/install.ps1 | iex

以下是 Deno 1.39 中新增内容的概述

WebGPU 回来了

WebGPU API 为开发人员提供了一种低级、高性能、跨架构的方式,可以从 JavaScript 编程 GPU 硬件。它是 Web 上 WebGL 的有效继任者。该规范已最终确定,Chrome 已经发布了该 API。Firefox 和 Safari 正在努力支持该 API。

Deno 在 2021 年初首次引入了 WebGPU,但由于性能问题,它在今年早些时候被移除。通过此版本,我们很高兴重新引入它,并解决了所有性能问题。

与 CPU 相比,GPU 能够以极高的并行性计算某些数值运算。这对于各种应用非常有用,不仅仅是渲染和游戏。例如,机器学习算法通常可以表示为一系列矩阵运算,这些运算可以在 GPU 上非常有效地计算。

我们的 WebGPU 实现基于与 Firefox 中即将推出的 WebGPU 实现相同的底层系统,因此我们有信心它为开发人员构建坚实的基础。

通过 WebGPU 获取有关 GPU 信息的基本示例

// Try to get an adapter from the user agent.
const adapter = await navigator.gpu.requestAdapter();
if (adapter) {
  // Print out some basic details about the adapter.
  const adapterInfo = await adapter.requestAdapterInfo();
  console.log(`Found adapter: ${adapterInfo.device}`); // On some systems this will be blank
  const features = [...adapter.features.values()];
  console.log(`Supported features: ${features.join(", ")}`);
} else {
  console.error("No adapter found");
}

可以在我们的 webgpu-examples 存储库 中查看更多示例。

虽然规范已经稳定,但 WebGPU 在 Deno 中仍然被认为是不稳定的。要在 Deno 中访问它,请使用 --unstable-webgpu 标志。我们计划在从社区获得更多反馈并有更多时间根据 规范测试套件 验证实现后尽快使其稳定。

为了获得更多 webgpu 功能,我们还添加了 std/webgpu(更多信息见下文)。

感谢 wgpu 团队提供的帮助,使这一切成为可能。

新的 deno coverage 报告程序

在此版本中,deno coverage 收到了两个新的报告程序:summaryhtml。您现在也可以省略 deno test--coverage 标志的目录值,因为我们现在默认使用 ./coverage/ 目录。

summary 报告程序是新的默认报告程序。它以简洁的表格形式输出覆盖率摘要,为您提供有关特定文件中覆盖率的信息以及总体摘要。

$ deno coverage
----------------------------------
File         | Branch % | Line % |
----------------------------------
 bar.ts      |      0.0 |   57.1 |
 baz/quux.ts |      0.0 |   28.6 |
 baz/qux.ts  |    100.0 |  100.0 |
 foo.ts      |     50.0 |   76.9 |
----------------------------------
 All files   |     40.0 |   61.0 |
----------------------------------

以前默认的报告程序会将所有文件中未覆盖的行打印到终端,现在可以使用 --detailed 标志仍然可以使用。

您还可以指定 --html 标志以获得以 HTML 格式显示的详细覆盖率报告。

$ deno test --coverage
$ deno coverage --html
HTML coverage report has been generated at file:///path/to/project/coverage/html/index.html

覆盖率报告的示例如下所示

HTML Coverage Report Index

HTML Coverage Report Source

deno coverage --html 的输出是完全静态的,可以托管在任何静态文件服务器上,例如 GitHub Pages。

我们仍然支持 --lcov 标志,它以 LCOV 格式输出覆盖率报告,这对于与其他工具(如 CodecovCoveralls)集成很有用。

我们还计划进一步改进 deno coverage 并使其更实用。如果您有任何反馈,请在 此问题 上发表评论。

deno compile 的更新

Deno 1.39 在 deno compile 功能方面引入了重大改进

更好的 node_modules 支持:使用 --unstable-byonm 标志,deno compile 现在支持“自带 node_modules”,增强了 Node.js 兼容性。此功能允许您在 Deno 项目中直接使用 npm 包,缩小 Deno 和广泛的 npm 生态系统之间的差距。有关此功能的更多信息,请参阅 我们之前的博文

可执行文件灵活命名:Deno 取消了对可执行文件命名的限制。您现在可以使用以数字开头的名称命名编译后的程序,例如 3d_modeler,这在命名约定方面提供了更大的灵活性。

更多动态导入支持deno compile 中现在支持更多动态导入模式。这很有趣,因为 Deno 需要在 deno compile 生成的二进制文件中静态包含所有可能在运行时导入的模块。因此,动态导入可能会出现问题。现在,Deno 可以处理更多动态导入模式,例如

await import(`./dir/${expr}`);

在此示例中,Deno 会将 ./dir/ 及其子目录中的所有模块包含到编译后的二进制文件中。这允许您在运行时导入这些文件中的任何一个。此更新通过确保在运行时提供所有动态引用的模块,简化了依赖项管理。

增强的语言服务器

在我们不断致力于改进 Deno 语言服务器 (LSP) 的过程中,此版本引入了显著的性能改进。

响应式键入体验:我们优化了在快速键入过程中处理快速请求突发的过程,确保了您的 IDE 中更流畅、更灵敏的编辑体验。

关闭超时:为了解决 LSP 实例持续存在“僵尸”问题,我们实施了关闭超时机制。此功能在您在设置超时后关闭编辑器时强行终止 LSP 进程,从而提高资源利用效率。

更新通知:语言服务器现在会积极通知您最新的 Deno 更新。此功能旨在简化 Deno 安装的更新过程,确保您始终可以使用最新的功能和修复。

增强型诊断:我们在 VSCode 中引入了新的 deno.logFile 设置。当与 deno.internalDebug 一起使用时,此功能允许捕获详细的诊断数据,有助于性能分析和故障排除。

我们致力于不断改进 Deno LSP。如果您遇到 LSP 或 VSCode Deno 中的任何性能问题,您的反馈非常宝贵。请 在这里 告诉我们您的体验。

Node.js 兼容性改进

松散导入

将现有的 TypeScript 代码库迁移到 Deno 可能是一项艰巨的任务。其中最大的障碍是 Deno 要求您在导入语句中显式指定文件扩展名。

假设一个 TypeScript 项目有一个文件 foo.ts 导入 bar.ts

// foo.ts
import { Example } from "./bar";
console.log(Example);

// bar.ts
export const Example = "Example";

在 Deno 的早期版本中,这会导致错误,并显示一条无用的消息。

# Deno v1.38.5
$ deno run foo.ts
error: Module not found "file:///dev/bar"
  at file:///dev/foo.ts:1:25

现在,在 Deno 1.39 中,该错误消息得到改进,并提供了一个紧急出口。

# Deno v1.39.0
$ deno run foo.ts
error: Module not found "file:///dev/bar". Maybe add a '.ts' extension or run with --unstable-sloppy-imports
    at file:///dev/foo.ts:1:25

使用 --unstable-sloppy-imports 标志运行将执行代码而无需任何更改。

# Deno v1.39.0
$ deno run --unstable-sloppy-imports foo.ts
Warning Sloppy imports are not recommended and have a negative impact on performance.
Warning Sloppy module resolution (hint: add .ts extension)
    at file:///dev/foo.ts:1:25
Example

除了解决没有文件扩展名的导入之外,此功能还允许解析“目录”导入,以及使用 .js 扩展名导入 .ts 文件。

// routes/index.ts
export default {
  "/": () => "Hello World",
  "/example": () => "Example",
};

// bar.ts
export const bar = "bar";

// foo.ts
import routes from "./routes";
import { bar } from "./bar.js";

console.log(routes);
console.log(bar);
$ deno run --unstable-sloppy-imports foo.ts
Warning Sloppy imports are not recommended and have a negative impact on performance.
Warning Sloppy module resolution (hint: specify path to index.ts file in directory instead)
    at file:///Users/ib/dev/deno/foo.ts:1:20
Warning Sloppy module resolution (hint: update .js extension to .ts)
    at file:///Users/ib/dev/deno/foo.ts:2:21
{ "/": [Function: /], "/example": [Function: /example] }
bar

如上所示,使用此标志将打印警告,引导您将代码迁移到推荐的导入语法。您还将在编辑器中获得诊断,以及“快速修复”,这将使您更容易应用必要的更改。

我们希望此功能将使您更容易尝试并将现有项目迁移到 Deno。

支持在 deno task 中运行 node_modules/.bin/ 可执行文件

deno task 可以运行在 deno.json 中定义的任务,以及在 package.json 中定义的 scripts。此版本添加了对在 deno task 中运行 node_modules/.bin/ 目录中的可执行文件的支持。

如果您的 package.json 看起来像这样

{
  "scripts": {
    "dev": "vite dev"
  }
}

您可以使用 deno task 运行 vite dev

$ deno task dev

Deno 将在 node_modules/.bin/ 目录中查找 vite 可执行文件并使用 Deno 运行它 - 即使 node_modules/.bin/vite 使用定义 Node.js 作为解释器的 shebang。

node_modules 中的 CommonJS 入口点

Deno 现在将正确处理 node_modules 中的 CommonJS 入口点,尊重相关包的 package.json 中的 type 设置。这将大大提高与尚未迁移到 ESM 的包的兼容性。

Object.prototype.__proto__ 的支持

Deno 为了安全原因而做出了一项明智的决定,即不支持 Object.prototype.__proto__。但是,npm 上仍然有许多包依赖此属性才能正常工作。

在此版本中,我们引入了一个新的 --unstable-unsafe-proto 标志,允许您启用此属性。不建议使用此标志,但如果您确实需要使用依赖它的包,现在可以为您提供紧急出口。

Node.js API 更新

以下 Node.js API 现在可用

  • crypto.createPrivateKey
  • http.ClientRequest.setTimeout
  • http.globalAgent
  • perf_hooks.performance
  • process.geteuid
  • process.report
  • util.parseArgs
  • vm.runInNewContext

此外,我们修复了已支持的 Node.js API 中的几个错误

  • child_process.spawnSyncstatus 的处理不正确
  • child_process.spawnSync 正确地规范化 stdio
  • child_process 可以处理 Unix 系统上的 IPC 管道(Windows 支持即将推出)
  • crypto.sign 现在可以与 PEM 私钥一起使用
  • fs.existsSync 在文件不存在时现在更快
  • process.exitCode 应该更改进程的退出代码
  • 允许 null 值用于 http.OutgoingMessage.setHeader
  • 修复 Buffer.copysourceStart > source.length
  • 修复 os.freemem
  • 修复 stream.Writable
  • 处理多次关闭 process.stdin

Deno API 的更改

此版本包含对 Deno API 的一些更改

Deno.serve() 对 Unix 套接字的支持现已稳定

Deno.serve(
  { transport: "unix", address: "/tmp/my.sock" },
  (req) => new Response("Hello!"),
);

Deno.HttpServer.shutdown() 现已稳定

const server = Deno.serve((req) => new Response("Hello!"));

// Shutdown the server gracefully when the process is interrupted.
Deno.addSignalListener("SIGINT", () => {
  server.shutdown();
});

await server.finished;

Deno.HttpClient 现在可以使用 using 关键字声明

显式资源管理 引入 Deno API 在先前版本中,此版本为 Deno.HttpClient 带来了对 using 关键字的支持。

{
  using client = Deno.createHttpClient({});
  const response = await fetch("https://127.0.0.1:4545/assets/fixture.json", {
    client,
  });
  const json = await response.json();
}
// `client` is closed here

KV 监视

新的不稳定 Deno.Kv.watch() API 用于监视给定数据库中给定键的更改。此 API 返回一个 ReadableStream,它在监视的键中的任何一个更改其版本戳时发出一个新值。发出的值是一个 Deno.KvEntryMaybe 对象数组,其长度和顺序与 keys 数组相同。 在此处详细了解 KV 监视功能。

const db = await Deno.openKv();

const stream = db.watch([["foo"], ["bar"]]);
for await (const entries of stream) {
  entries[0].key; // ["foo"]
  entries[0].value; // "bar"
  entries[0].versionstamp; // "00000000000000010000"
  entries[1].key; // ["bar"]
  entries[1].value; // null
  entries[1].versionstamp; // null
}

Cron

最近引入的 Deno.cron 函数在此版本中得到了激动人心的更新。它现在支持定义计划的直观 JSON 格式,使 cron 作业更易于理解和实现。有关更深入的信息,浏览我们关于 cron 功能的详细博客文章。

传统 Unix Cron 格式

以前,设置一个每 20 分钟运行一次的任务需要 Unix cron 格式

Deno.cron("myCron", "*/20 * * * *", () => {
  console.log("Running every 20 minutes");
});

新的 JSON 格式

现在,您可以使用更易读的 JSON 格式实现相同的目的

Deno.cron("myCron", {
  minutes: { every: 20 },
}, () => {
  console.log("Running every 20 minutes");
});

已弃用的 IO 接口

我们已经推动 Deno API 使用 Web Streams API 几个月了,为了最终完成此更改,v1.39 版本对 Deno 中的各种 IO 接口进行了弃用。

以下接口现在已弃用,将在 Deno 2 中删除。

  • Deno.Reader
  • Deno.ReaderSync
  • Deno.Writer
  • Deno.WriterSync
  • Deno.Closer

现在,在使用这些接口时,您将在编辑器中获得警告,并建议改用 Web Streams API。

对 Web API 的更改

AbortSignal.any()

此 API 允许监听多个 AbortSignals,如果任何指定的信号被中止,它也会中止。

如果您有一个提供 AbortSignal 的 API(例如,Request),并且您想另外依赖另一个 AbortSignal,这会很有用。

ImageData

ImageData Web API 是一个可以用来表示以标准格式表示图像的类。

创建一个 1x2 像素红色图像的快速示例

const rawImage = new Uint8ClampedArray([255, 0, 0, 1, 255, 0, 0, 1]);
new ImageData(rawImage, 1, 2);

感谢 @jamsinclair 为此功能做出的贡献。

ReadableStream.read min 选项

ReadableStreamBYOBReader.readmin 选项允许指定要读取的最小字节数。这对于需要读取 n 个字节的各种编码格式很有用,可以通过将 min 选项设置为与缓冲区的字节长度相同的长度来实现。

例如

const response = await fetch("https://example.com");
const reader = response.body!.getReader({ mode: "byob" });
const buffer = new Uint8Array(10);
// value will contain at least 5 bytes written to it
const { value } = await reader.read(buffer, { min: 5 });

改进的 URLPattern 性能

URLPattern API 已经获得了性能改进,使其在匹配多个模式时速度提高了 6 到 8 倍,这是一个针对使用 URLPattern 的常见场景进行的优化,即在 HTTP 路由器中使用 URLPattern

标准库更新

std/webgpu

除了在 CLI 中发布 WebGPU 外,我们还在此版本中添加了 std/webgpu

此模块添加了一些额外的功能,包括 createTextureWithData 用于创建带有现有数据的 GPUTexturedescribeTextureFormat 用于获取有关纹理格式的信息,以及 createCapture 用于使用纹理进行“捕获”,等等。

其中一些功能已移植/基于 wgpu 功能,并且正在计划进一步的功能。

std/expect

在此版本中,添加了 std/expect。该模块导出两个函数:expectfn。这些函数旨在与 Jest 兼容。

import { expect } from "https://deno.land/[email protected]/expect/mod.ts";

expect(Math.max(5, 8)).not.toEqual(5);
expect(Math.max(5, 8)).toEqual(8);

expect(Math.pow(3, 5)).toBeGreaterThan(100);
expect(Math.pow(3, 5)).toBeLessThan(300);

目前 expect 支持以下匹配器和修饰符 API:notresolvesrejectstoBetoEqualtoStrictEqualtoMatchtoMatchObjecttoBeDefinedtoBeUndefinedtoBeNulltoBeNaNtoBeTruthytoBeFalsytoContaintoContainEqualtoHaveLengthtoBeGreaterThantoBeGreaterThanOrEqualtoBeLessThantoBeLessThanOrEqualtoBeCloseTotoBeInstanceOftoThrowtoHavePropertytoHaveLength

该模块还提供与模拟相关的实用程序和断言

import { expect, fn } from "https://deno.land/[email protected]/expect/mod.ts";

const mockFn = fn();

mockFn();
mockFn();
mockFn();

expect(mockFn).toHaveBeenCalled();
expect(mockFn).not.toHaveBeenCalledTimes(1);
expect(mockFn).toHaveBeenCalledTimes(3);

已实现以下与模拟相关的匹配器:toHaveBeenCalledtoHaveBeenCalledTimestoHaveBeenCalledWithtoHaveBeenLastCalledWithtoHaveBeenNthCalledWithtoHaveReturnedtoHaveReturnedTimestoHaveReturnedWithtoHaveLastReturnedWithtoHaveNthReturnedWith

该模块与 Jest 的 expect API 不完全兼容。以下 API 尚未实现:toMatchSnapShottoMatchInlineSnapShottoThrowErrorMatchingSnapShottoThrowErrorMatchingInlineSnapShotexpect.anythingexpect.anyexpect.arrayContainingexpect.not.arrayContainingexpect.closedToexpect.objectContainingexpect.not.objectContainingexpect.stringContainingexpect.not.stringContainingexpect.stringMatchingexpect.not.stringMatchingexpect.assertionsexpect.hasAssertionsexpect.addEqualityTesterexpect.addSnapshotSerializerexpect.extend

标准库还有 std/testing/bdd 模块。现在您可以使用标准库以 BDD 样式编写测试用例。

import { describe, it } from "https://deno.land/[email protected]/testing/bdd.ts";
import { expect } from "https://deno.land/[email protected]/expect/mod.ts";

describe("Math", () => {
  describe("max", () => {
    it("returns max value from the given args", () => {
      expect(Math.max(1, 2, 3)).toEqual(3);
      expect(Math.max(-3, -2, -1)).toEqual(-1);
    });
  });
});

将此文件保存为 my_test.ts,您可以使用以下命令执行它

$ deno test my_test.ts
Check file:///path/to/my_test.ts
running 1 test from ./my_test.ts
Math ...
  max ...
    returns max value from the given args ... ok (1ms)
  max ... ok (1ms)
Math ... ok (1ms)

ok | 1 passed (2 steps) | 0 failed (1ms)

感谢 Thomas Cruveilher 为此功能做出的贡献。

std/ini

在此版本中,添加了 std/ini。该模块提供了用于处理 INI 文件 的解析器和序列化器。

感谢 Aaron Huggins 为此功能做出的贡献。

std/data_structures

std/data_structures 已在此版本中添加。此模块导出实现 数据结构 算法的类。目前 RedBlackTreeBinarySearchTreeBinaryHeap 类可用。

std/text

std/text 已在此版本中添加。此模块导出用于比较/排序/选择符合特定条件的文本的实用程序函数

import {
  closestString,
  levenshteinDistance,
} from "https://deno.land/[email protected]/text/mod.ts";

// Calculates levenshtein distance of 2 words
console.log(levenshteinDistance("hello", "halo")); // => prints 2
console.log(levenshteinDistance("hello", "world")); // => prints 4

console.log(closestString("puhs", ["commit", "pull", "push"])); // => prints "push"

感谢 Jeff Hykin 为这些功能做出的贡献。

std/cli

std/cli 已在此版本中添加。此模块目前导出 2 个 API:parseArgspromptSecret

import {
  parseArgs,
  promptSecret,
} from "https://deno.land/[email protected]/cli/mod.ts";

const parsedArgs = parseArgs(["--foo", "--bar=baz", "./quux.txt"]);
console.log(parsedArgs);
// prints: { foo: true, bar: "baz", _: ["./quux.txt"] }
// This is typically used as `parseArgs(Deno.args)`

const credential = promptSecret("Enter the credential ");
// This asks for the credential in the terminal and the user inputs are masked with '*'

注意:parseArgsstd/flags 中的 parse 相同,但我们意识到 std/flags 的范围太有限,因此决定在 std/cli 下探索更多与 CLI 相关的功能。

感谢 Alisue 为提出和实现 promptSecret 做出的贡献。

std/net

std/net 已在此版本中添加。此模块导出 1 个 API:getAvailablePort,它对于选择可用的 TCP 端口很有用。

import { getAvailablePort } from "https://deno.land/[email protected]/net/mod.ts";

const port = await getAvailablePort();

感谢 Harry Solovay 为此功能做出的贡献。

TypeScript 5.3

Deno v1.39 附带最新版本的 TypeScript 5.3,请在 宣布 TypeScript 5.3 博客文章中了解更多信息。

装饰器的即将发布的更改

目前,Deno 默认启用 实验性 TypeScript 装饰器。由于 TC39 装饰器提案 处于第 3 阶段,并且 一个受欢迎的请求,我们决定在即将发布的 Deno v1.40 版本中更改装饰器的默认设置。

在下一个版本中,实验性 TypeScript 装饰器将默认禁用,而 TC39 装饰器将默认启用。

如果您使用的是实验性 TS 装饰器,请通过将此配置添加到您的 deno.json 中来为此更改做好准备

{
  "compilerOptions": {
    "experimentalDecorators": true
  }
}

感谢我们的贡献者!

如果没有社区的帮助,我们无法构建 Deno!无论是通过在我们的社区 Discord 服务器 中回答问题还是 报告错误,我们都对您的支持表示衷心的感谢。特别是,我们要感谢以下人员为 Deno 1.39 做出的贡献:Aravind、Birk Skyum、Bolat Azamat、Chen Su、Daniel Mizerski、Florian Schwalm、Gasman、Ian Bull、Jacob Hummer、Jakub Jirutka、Jamie、Jesse Jackson、John Spurlock、Jordan Harband、Julien Cayzac、Jérôme Benoit、Kenta Moriuchi、Laurence Rowe、Max Goodhart、Raashid Anwar、Tareque Md Hanif、btoo、citrusmunch、lionel-rowe、liruifengv、pk、scarf、ud2 和 林炳权。

您想加入 Deno 贡献者的行列吗? 在此查看我们的贡献文档,我们将在下次看到您出现在名单上。

信不信由你,上面列出的更改仍然没有告诉你 1.39 中所有改进的地方。您可以在 GitHub 上查看 Deno 1.39 中合并的所有拉取请求

感谢您关注我们的 1.39 版本,我们希望您喜欢使用 Deno 进行构建!

🍋 Fresh 1.6 已发布。

Fresh v1.6 应用程序扩展了插件 API、更快的路由匹配以及官方 Tailwind CSS 支持。