要升级到 Deno 2.2,请在终端中运行以下命令:

如果尚未安装 Deno,请运行以下命令之一进行安装,或在此处了解如何安装

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

iwr https://deno.land/install.ps1 -useb | iex

Deno 2.2 中的新功能

此版本包含_大量_内容。这是一个快速概览,可帮助您深入了解最关心的内容:

内置 OpenTelemetry 集成

Deno 2.2 包含内置的 OpenTelemetry,用于监控日志、指标和追踪。

Deno 自动检测 console.logDeno.servefetch 等 API。您还可以使用 npm:@opentelemetry/api 检测您自己的代码。

让我们看一下 Deno.serve API 的一些日志和追踪:

server.ts

Deno.serve((req) => {
  console.log("Received request for", req.url);
  return new Response("Hello world");
});

要捕获可观测性数据,您需要提供一个 OTLP 端点。如果您已经设置了可观测性系统,则可以使用它。如果还没有,最简单的运行方式是启动 Docker 中的本地 LGTM 堆栈

$ docker run --name lgtm -p 3000:3000 -p 4317:4317 -p 4318:4318 --rm -ti \
    -v "$PWD"/lgtm/grafana:/data/grafana \
    -v "$PWD"/lgtm/prometheus:/data/prometheus \
    -v "$PWD"/lgtm/loki:/data/loki \
    -e GF_PATHS_DATA=/data/grafana \
    docker.io/grafana/otel-lgtm:0.8.1

现在我们准备好运行服务器并捕获一些数据了:

OpenTelemetry 集成的 API 仍可能发生更改。因此,它被指定为“不稳定 API”,需要使用 --unstable-otel 标志才能使用它。

$ OTEL_DENO=true deno run --unstable-otel --allow-net server.ts
Listening on http://localhost:8000/

现在,使用浏览器或 curl 连接到我们的服务器:

$ curl http://localhost:8000
Hello world

您现在可以在可观测性系统中查看日志和追踪。如果您正在使用 LGTM 堆栈,则可以在 http://localhost:3000 访问 Grafana 仪表板。

Image 1: OTEL 日志演示

服务器请求的日志示例

Image 2: OTEL 追踪演示

Deno.serve() API 服务的请求追踪

我们在这里只触及了表面。Deno 还导出了自动检测的指标,您可以使用 npm:@opentelemetry/api 包创建自己的指标和追踪 span。要了解更多信息,请访问 Deno 文档

您可以在 v2.2 演示视频的此视频中观看 OpenTelemetry 集成的演示

Linter 更新

Deno 2.2 对 deno lint 进行了重大升级,包括新的插件系统和 15 个新规则,特别是对于 React 和 Preact 用户。

新的内置 lint 规则

此版本添加了新的 lint 规则,主要针对 JSX 和 React 最佳实践。

为了补充这些规则,添加了两个新标签:jsxreact

请参阅 Deno 文档 中可用的 lint 规则和标签的完整列表。

JavaScript 插件 API

deno lint 的最大更新是能够使用新的插件系统扩展其功能。

注意:插件 API 仍处于 API 可能更改的阶段,因此目前标记为不稳定功能。

虽然有很多内置规则,但在某些情况下,您可能需要针对特定项目量身定制的规则。

插件 API 模仿 ESLint 插件 API,但并非 100% 兼容。在实践中,我们期望 某些 现有的 ESLint 插件可以与 deno lint 一起使用,而不会出现问题。

这是一个简单的 lint 插件的示例。我们将创建一个插件,如果将变量命名为 foo,则报告错误:

deno.json

{
  "lint": {
    "plugins": ["./my-plugin.ts"]
  }
}

my-plugin.ts

export default {
  name: "my-lint-plugin",
  rules: {
    "my-lint-rule": {
      create(context) {
        return {
          VariableDeclarator(node) {
            if (node.id.type === "Identifier" && node.id.name === "foo") {
              context.report({
                node,
                message: "Use more descriptive name than `foo`", // 使用比 `foo` 更具描述性的名称
              });
            }
          },
        };
      },
    },
  },
} satisfies Deno.lint.Plugin;

main.js

const foo = "foo";
console.log(foo);
$ deno lint main.js
error[my-lint-plugin/my-lint-rule]: Use more descriptive name than `foo` // 使用比 `foo` 更具描述性的名称
 --> /dev/main.js:1:7
  |
1 | const foo = "foo";
  |       ^^^^^^^^^^^


Found 1 problem
Checked 1 file

除了基于访问者的 API 之外,您还可以使用类似 CSS 的选择器来定位特定节点。让我们使用选择器语法重写上面的规则。

my-plugin.ts

export default {
  name: "my-lint-plugin",
  rules: {
    "my-lint-rule": {
      create(context) {
        return {
          'VariableDeclarator[id.name="foo"]'(node) {
            context.report({
              node,
              message: "Use more descriptive name than `foo`", // 使用比 `foo` 更具描述性的名称
            });
          },
        };
      },
    },
  },
} satisfies Deno.lint.Plugin;

Lint 插件可以使用 TypeScript 编写,Deno 在 Deno.lint 命名空间下提供了开箱即用的完整类型声明。

您可以使用本地 lint 插件,以及来自 npm 和 JSR 的插件:

deno.json

{
  "lint": {
    "plugins": [
      "./my-plugin.ts",
      "jsr:@my-scope/lint-plugin",
      "npm:@my-scope/other-plugin"
    ]
  }
}

Deno 文档 中阅读有关 deno lint 插件 API 的更多信息。

deno lint--rules 标志的更新行为

此版本中更改了 deno lint --rules,使其始终打印所有可用的 lint 规则,并标记当前配置中启用的规则。

此外,deno lint --rules --json 不再打印原始 Markdown 文档,而是链接到 Deno 文档 中的相关规则页面。

您可以在 v2.2 演示视频的此视频中观看 lint 插件 API 的更详细演示

deno check 的改进

deno check,Deno 的类型检查工具,在此版本中获得了两项重大改进:

  • 现在支持 JSDoc 标签
  • 现在可以为每个工作区成员配置 compilerOptions 的设置

让我们详细了解一下每个方面:

现在支持 JSDoc @import 标签

类型检查时,现在支持 @import JSDoc 标签。这使您可以内联定义导入,从而改进 JavaScript 文件中的类型检查。

add.ts

export function add(a: number, b: number): number {
  return a + b;
}

main.js

export function addHere(value) {
  return value(1, 2);
}

addHere("");
$ deno check main.js
Check file:///main.js
error: TS2345 [ERROR]: Argument of type 'string' is not assignable to parameter of type '(a: number, b: number) => number'.
addHere("");
        ~~
    at file:///main.js:10:9

工作区范围的 compilerOptions 设置

以前,deno.json 将相同的 compilerOptions 应用于所有工作区成员,这使得很难分别配置前端和后端。现在,工作区成员可以定义自己的设置。

由于新的 每个工作区成员支持 compilerOptions,现在可以在目录中为您的前端代码指定不同的 compilerOptions.lib 设置。

deno.json

{
  "workspace": [
    "./server",
    "./client"
  ],
  "compilerOptions": {
    "checkJs": true
  }
}

client/deno.json

{
  "compilerOptions": {
    "lib": ["dom", "esnext"]
  }
}

client/main.js

document.body.onload = () => {
  const div = document.createElement("div");
  document.body.appendChild(div);
  document.body.appendChild("not a DOM element");
};
$ deno check client/main.js
Check file:///client/main.js
TS2345 [ERROR]: Argument of type 'string' is not assignable to parameter of type 'Node'.
  document.body.appendChild("not a DOM node");
                            ~~~~~~~~~~~~~~~~
    at file:///client/main.js:4:29

error: Type checking failed.

您可以在 v2.2 演示视频的此视频中观看 deno check 更新的演示

deno lsp 的改进

Deno 2.2 使 deno lsp 更快、响应更快,并为 Web 框架用户带来了重大改进。

这里有太多细节无法一一介绍,但让我们看一下一些亮点:

deno task 的实用更新

此版本为 deno task 带来了一些更新。第一个将帮助 deno task 更加健壮和可预测:

还有两个更新使 deno task 更加实用和方便使用。我们将更详细地了解这些内容:

  • 任务名称中的通配符
  • 运行没有命令的任务

任务名称中的通配符

您现在可以将 deno task 与任务名称中的通配符一起使用,如下所示:

deno.json

{
  "tasks": {
    "start-client": "echo 'client started'",
    "start-server": "echo 'server started'"
  }
}
$ deno task "start-\*"
Task start-client echo 'client started'
client started
Task start-server echo 'server started'
server started

确保使用通配符引用任务名称,否则您的 shell 将尝试扩展此字符,并且您将遇到错误。

通配符 (*) 可以放置在任何位置以匹配任务名称。所有与通配符匹配的任务都将并行运行。

运行没有命令的任务

任务依赖项在 v2.1 中变得流行。现在,您可以通过定义没有命令的任务来 更轻松地对任务进行分组

deno.json

{
  "tasks": {
    "dev-client": "deno run --watch client/mod.ts",
    "dev-server": "deno run --watch sever/mod.ts",
    "dev": {
      "dependencies": ["dev-client", "dev-server"]
    }
  }
}

在上面的示例中,dev 任务用于对 dev-clientdev-server 任务进行分组,但它本身没有命令。这是一种方便的方式,可以将任务分组在一起,以便从单个任务名称运行。

您可以在 v2.2 演示视频的此视频中观看 deno task 更新的演示

依赖管理

Deno 2.2 附带了对 deno outdated 工具的更改,该工具 添加了一种新的交互方式 来更新依赖项:

deno outdated --update --interactive 的演示

除了此改进之外,还进行了一些错误修复,使 deno installdeno outdated 更加健壮和快速。包括但不限于:

支持 node:sqlite

此版本为 Deno 带来了备受期待的 node:sqlite 模块,使其可以轻松地处理内存中或本地数据库:

db.ts

import { DatabaseSync } from "node:sqlite";

const db = new DatabaseSync("test.db");

db.exec(`
CREATE TABLE IF NOT EXISTS people (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  name TEXT,
  age INTEGER
);`);

const query = db.prepare(`INSERT INTO people (name, age) VALUES (?, ?);`);
query.run("Bob", 40);

const rows = db.prepare("SELECT id, name, age FROM people").all();
console.log("People:");
for (const row of rows) {
  console.log(row);
}

db.close();
$ deno run --allow-read --allow-write db.ts
People:
[Object: null prototype] { id: 1, name: "Bob", age: 40 }

请参阅我们文档中的 示例 以及 完整的 API 参考

放宽 Deno.cwd() 的权限检查

Deno 2.2 取消了使用 Deno.cwd() API 时对完整 --allow-read 权限的要求。

main.js

console.log(Deno.cwd());

Deno v2.1

$ deno main.js
 ⚠️  Deno requests read access to <CWD>.
┠─ Requested by `Deno.cwd()` API.
┠─ To see a stack trace for this prompt, set the DENO_TRACE_PERMISSIONS environmental variable.
┠─ Learn more at: https://docs.deno.com/go/--allow-read
┠─ Run again with --allow-read to bypass this prompt.
┗ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all read permissions) > y
/dev

Deno v2.2

$ deno main.js
/dev

在此更改之前,已经可以在没有权限的情况下获取 CWD 路径,例如,通过创建错误并检查其堆栈追踪。

此更改最初计划在 Deno 2.0 中发布,但错过了时机。我们很高兴在 v2.2 中迎来它。

更小、更快的 deno compile

deno compile 的许多性能和生活质量改进:

Image 3: deno compile 摘要

使用 npm:cowsaydeno compile 的摘要输出

更精确的 deno bench

deno bench 是内置工具,可让您快速轻松地对代码进行基准测试。Deno v1.21 更改了 deno bench 的行为,以自动执行基准测试的预热,并自动决定要执行的迭代次数,当后续运行之间的时间差异在统计上不显着时停止。

在大多数情况下,这效果很好,但有时,您希望对执行多少次预热运行和测量运行进行精细控制。为此,Deno v2.2 重新引入了 Deno.BenchDefinition.nDeno.BenchDefinition.warmup 选项。指定它们将使 deno bench 执行请求的运行次数:

url_bench.ts

Deno.bench({ warmup: 1_000, n: 100_000 }, () => {
  new URL("./foo.js", import.meta.url);
});

上面的基准测试将精确执行 1000 次“预热运行” - 这些运行不被测量,仅用于“预热” V8 引擎的 JIT 编译器。然后,基准测试将进行 100,000 次测量运行,并显示基于这些迭代的指标。

WebTransport 和 QUIC APIs

Deno 2.2 附带了 WebTransport API实验性支持 以及新的、不稳定的 Deno.connectQuicDeno.QuicEndpoint APIs。如果您不熟悉,QUIC (Quick UDP Internet Connections) 是一种旨在取代 TCP+TLS 的现代传输协议,并且是 HTTP/3 的基础。

由于这些是实验性的,它们的 API 在未来可能会发生变化,因此它们需要使用 --unstable-net 标志才能使用。

让我们看看这些 API 的实际应用。这是一个 QUIC 回显服务器和 WebTransport 客户端的示例。

请注意,WebTransport 需要使用 HTTPS。这些示例使用证书/密钥对;您可以使用 OpenSSL 生成自签名证书:openssl req -x509 -newkey rsa:4096 -keyout my_key.pem -out my_cert.pem -days 365

server.js

const cert = Deno.readTextFileSync("my_cert.crt");
const key = Deno.readTextFileSync("my_cert.key");

const server = new Deno.QuicEndpoint({
  hostname: "localhost",
  port: 8000,
});
const listener = server.listen({
  cert,
  key,
  alpnProtocols: ["h3"],
});

for await (const conn of listener) {
  const wt = await Deno.upgradeWebTransport(conn);

  handleWebTransport(wt);
}

async function handleWebTransport(wt) {
  await wt.ready;

  (async () => {
    for await (const bidi of wt.incomingBidirectionalStreams) {
      bidi.readable.pipeTo(bidi.writable).catch(() => {});
    }
  })();

  (async () => {
    for await (const stream of wt.incomingUnidirectionalStreams) {
      const out = await wt.createUnidirectionalStream();
      stream.pipeTo(out).catch(() => {});
    }
  })();

  wt.datagrams.readable.pipeTo(wt.datagrams.writable);
}

client.js

import { decodeBase64 } from "jsr:@std/encoding/base64";
import { assertEquals } from "jsr:@std/assert";

const cert = Deno.readTextFileSync("my_cert.crt");
const certHash = await crypto.subtle.digest(
  "SHA-256",
  decodeBase64(cert.split("\n").slice(1, -2).join("")),
);

const client = new WebTransport(
  `https://localhost:8000/path`,
  {
    serverCertificateHashes: [{
      algorithm: "sha-256",
      value: certHash,
    }],
  },
);

await client.ready;

const bi = await client.createBidirectionalStream();

{
  const writer = bi.writable.getWriter();
  await writer.write(new Uint8Array([1, 0, 1, 0]));
  writer.releaseLock();
  const reader = bi.readable.getReader();
  assertEquals(await reader.read(), {
    value: new Uint8Array([1, 0, 1, 0]),
    done: false,
  });
  reader.releaseLock();
}

{
  const uni = await client.createUnidirectionalStream();
  const writer = uni.getWriter();
  await writer.write(new Uint8Array([0, 2, 0, 2]));
  writer.releaseLock();
}

{
  const uni =
    (await client.incomingUnidirectionalStreams.getReader().read()).value;
  const reader = uni!.getReader();
  assertEquals(await reader.read(), {
    value: new Uint8Array([0, 2, 0, 2]),
    done: false,
  });
  reader.releaseLock();
}

await client.datagrams.writable.getWriter().write(
  new Uint8Array([3, 0, 3, 0]),
);
assertEquals(await client.datagrams.readable.getReader().read(), {
  value: new Uint8Array([3, 0, 3, 0]),
  done: false,
});
$ deno run -R --unstable-net server.js
...
$ deno run -R --unstable-net client.js
...

Node.js 和 npm 兼容性改进

与往常一样,Deno 2.2 带来了大量的 Node.js 和 npm 兼容性改进。以下是亮点列表:

process 更改:

fs 更改:

http 模块更改:

zlib 模块更改:

worker_threads 模块更改:

crypto 模块更改:

v8 模块更改:

其他更改:

性能改进

性能改进是每个 Deno 版本的组成部分,此版本也不例外。以下是一些改进列表:

WebGPU 的改进

我们的 WebGPU 实现进行了重大改进,修复了许多遇到的问题,并且还应提高可用 API 的整体性能。

除了这些修复之外,我们的 Jupyter 集成现在能够将 GPUTexture 显示为图像,并将 GPUBuffer 显示为文本:

Image 4: Jupyter GPUTexture 和 GPUBuffer 演示

查看一些将 WebGPU 与 Deno 结合使用的示例

更小的 Linux 二进制文件

由于使用了完整的 链接时优化,我们 设法节省了近 15Mb 的二进制文件大小。这使 deno 从 137Mb 缩小到 122Mb。

TypeScript 5.7 和 V8 13.4

Deno 2.2 升级到 TypeScript 5.7V8 13.4,带来了新的语言功能和性能改进。

TypedArrays 现在是泛型的

TypeScript 5.7 的一项重大更改是 Uint8Array 和其他 TypedArrays 现在是 ArrayBufferLike 的泛型。这允许在使用 SharedArrayBufferArrayBuffer 时获得更好的类型安全性,但这可能需要更新某些代码库。

const buffer: Uint8Array = new Uint8Array(new ArrayBuffer(8));

const buffer: Uint8Array<SharedArrayBuffer> = new Uint8Array(
  new SharedArrayBuffer(8),
);

此更改可能会引入类型错误。如果您看到如下错误:

error TS2322: Type 'Buffer' is not assignable to type 'Uint8Array<ArrayBufferLike>'.
error TS2345: Argument of type 'Buffer' is not assignable to parameter of type 'Uint8Array<ArrayBufferLike>'.

您可能需要将 @types/node 更新到最新版本。

Microsoft 的公告TypeScript PR 中阅读有关此更改的更多信息。

长期支持

Deno v2.1 仍然是长期支持版本,并且将在未来 6 个月内定期接收错误修复、安全更新和关键性能改进。