跳到主要内容
Deno 2.1

Deno 2.1:Wasm 导入和其他增强功能

Deno 继续简化编程,推出了首个 2.x 次要版本,最值得注意的是允许直接 Wasm 导入。2.1 包括对deno compile 中的资产文件权限提示中的堆栈跟踪、改进的 deno task 等的支持。此版本还启动了我们的首个长期支持分支以及更多功能。

要升级到 Deno 2.1,请在您的终端中运行以下命令

deno upgrade

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

# Using Shell (macOS and Linux):
curl -fsSL https://deno.land/install.sh | sh

# Using PowerShell (Windows):
iwr https://deno.land/install.ps1 -useb | iex

Deno 2.1 中的新功能

Deno 团队还将举办直播,于太平洋时间 12 月 3 日上午 8 点/美国东部时间上午 11 点/世界标准时间下午 4 点演示和讨论 2.1 版本,并回答您的任何问题。

一流的 Wasm 支持

Wasm,或 WebAssembly,是一种二进制编译目标,允许您使用其他语言编写代码并将其编译为浏览器使用。

虽然 Deno 一直以来都支持 Wasm,但 Deno v2.1 使导入 Wasm 模块变得更加简单、符合人体工程学且性能更高。

以前,您必须手动加载 Wasm 模块,这需要指定 --allow-read--allow-net 权限

// previously
const wasmInstance = WebAssembly.instantiateStreaming(fetch("./add.wasm"));
const { add } = wasmInstance;

console.log(add(1, 2));
// $ deno --allow-read main.ts
// 3
// Now in Deno 2.1
import { add } from "./add.wasm";

console.log(add(1, 2));
// $ deno main.ts
// 3

Wasm 模块现在是“模块图”的一部分,Deno 可以分析和缓存该模块图以加快使用速度。

Deno 还了解导出 Wasm 模块,并且将对其使用进行类型检查

假设我们尝试从之前的示例中错误地调用 add 函数

import { add } from "./add.wasm";

console.log(add(1, ""));
$ deno check main.ts
Check file:///main.ts
error: TS2345 [ERROR]: Argument of type 'string' is not assignable to parameter of type 'number'.
console.log(add(1, ""));
                   ~~
    at file:///main.ts:3:20

Wasm 模块还可以导入其他模块。让我们创建一个 Wasm 模块(使用 WebAssembly 文本格式

(module
  (import "./time.ts" "getTimeInSeconds" (func $get_time (result i32)))
  (func (export "getValue") (result i32)
    call $get_time
  )
)
// time.ts
export function getTimeInSeconds() {
  return Date.now() / 1000;
}
// main.ts
import { getValue } from "./toolkit.wasm";

console.log(getValue());

现在让我们运行它

> wat2wasm toolkit.wat
> deno main.ts
1732147633

> deno main.ts
1732147637

阅读 Deno 文档,了解如何利用 Wasm 模块。

长期支持版本

Deno 每六周发布一个新的次要版本,几乎每周发布一个新的补丁版本。但是,大型组织中的开发团队需要在生产中使用新版本之前仔细审核它们。如此快节奏的发布节奏使这些团队难以跟上最新的 Deno 版本。

为了让这些团队更容易使用 Deno,Deno v2.1 是 Deno 的第一个 LTS 版本v2.1 分支将在未来六个月内收到重要的错误修复和性能改进。

Schedule for Deno v2.x releases

Deno v2.x 版本的计划

deno init --npm vite

使用 npm init(或 npm create)是为许多基于现有模板的框架搭建新项目的流行方式。

在 Deno v2.1 之前,这有点复杂,需要对该程序的工作原理有深入的了解。在此版本中,我们简化了此命令:deno init 获得了一个新的 --npm 标志,可以像使用 npmpnpm 和其他流行的包管理器一样启动新项目

# Previously
$ deno run -A npm:create-vite
# Now in Deno v2.1
$ deno init --npm vite

当您运行此子命令时,Deno 将提示您是否要使用所有权限运行项目初始化脚本。

依赖管理

此版本中 Deno 的依赖管理有很多改进,但最重要的部分是您现在可以使用新的 deno outdated 子命令管理 JSR 和 npm 依赖更新

$ deno outdated
┌────────────────┬─────────┬────────┬────────┐
│ Package        │ Current │ Update │ Latest │
├────────────────┼─────────┼────────┼────────┤
│ jsr:@std/fmt   │ 1.0.0   │ 1.0.3  │ 1.0.3  │
├────────────────┼─────────┼────────┼────────┤
│ jsr:@std/async │ 1.0.1   │ 1.0.1  │ 1.0.8  │
├────────────────┼─────────┼────────┼────────┤
│ npm:chalk      │ 4.1.2   │ 4.1.2  │ 5.3.0  │
└────────────────┴─────────┴────────┴────────┘

此命令还支持 --update--latest 等标志

$ deno outdated --update --latest
Updated 3 dependencies:
 - jsr:@std/async 1.0.1 -> 1.0.8
 - jsr:@std/fmt   1.0.0 -> 1.0.3
 - npm:chalk      4.1.2 -> 5.3.0

deno outdated 命令允许您查看项目中哪些依赖项有较新的版本可用。请注意,此命令同时理解 deno.jsonpackage.json,并同时修改这两个文件。

默认情况下,它尊重配置文件中指定的 semver 范围,但您可以使用 --latest 标志强制其更新到最新的可用版本(即使它可能与 semver 不兼容)。

您还可以过滤要检查的包

$ deno outdated "@std/*"
┌────────────────┬─────────┬────────┬────────┐
│ Package        │ Current │ Update │ Latest │
├────────────────┼─────────┼────────┼────────┤
│ jsr:@std/fmt   │ 1.0.0   │ 1.0.3  │ 1.0.3  │
├────────────────┼─────────┼────────┼────────┤
│ jsr:@std/async │ 1.0.1   │ 1.0.1  │ 1.0.8  │
└────────────────┴─────────┴────────┴────────┘

$ deno outdated "!@std/fmt"
┌────────────────┬─────────┬────────┬────────┐
│ Package        │ Current │ Update │ Latest │
├────────────────┼─────────┼────────┼────────┤
│ jsr:@std/async │ 1.0.1   │ 1.0.1  │ 1.0.8  │
├────────────────┼─────────┼────────┼────────┤
│ npm:chalk      │ 4.1.2   │ 4.1.2  │ 5.3.0  │
└────────────────┴─────────┴────────┴────────┘

或更新到特定版本

$ deno outdated --update [email protected] @std/[email protected]
Updated 2 dependencies:
 - jsr:@std/async 1.0.1 -> 1.0.6
 - npm:chalk      4.1.2 -> 5.2.0

deno outdated 也适用于工作区 - 使用 --recursive 标志

$ deno outdated --recursive --update --latest

交互式更新已在路线图上,并计划在即将发布的版本中推出。

阅读更多关于保持依赖项更新或关于Deno 的包管理能力的常规信息。

deno compile 中嵌入资产文件

v1.6 以来,deno compile 允许您将项目编译为适用于任何主要平台的单个二进制可执行文件,从而更轻松地分发和执行,而无需安装 Deno 或依赖项。

deno compile 最受请求的功能之一是能够嵌入可由编译后的程序读取的任意文件。由于对 deno compile 的内部基础设施进行了重大改造,现在这成为可能。

您现在可以使用 --include 标志来告诉 Deno 包含其他文件或目录

$ deno compile --include ./names.csv --include ./data/ main.ts

然后在您的程序中,您可以像在本地开发时一样读取它们

// main.ts

import { parse } from "jsr:@std/csv/parse";
const names = Deno.readTextFile(import.meta.dirname + "/names.csv");
const csvData = parse(names);

const dataFiles = Deno.readDir(import.meta.dirname + "/data");
for (const file of dataFiles) {
  // ...do something with each file
}

请记住,您只能包含本地文件。不支持远程文件。

此外,使用 deno compile 创建的程序现在可以利用 V8 代码缓存来加快启动速度。缓存在首次运行时创建,并放置在临时目录中,以便在后续运行时使用。

我们的基准测试显示后续运行的性能提高了 2 倍以上,但您的结果可能会有所不同

$ hyperfine --warmup 2 scratch.exe scratch_new.exe
Benchmark 1: scratch.exe
  Time (mean ± σ):     282.9 ms ±   6.5 ms    [User: 78.1 ms, System: 15.6 ms]
  Range (min … max):   276.5 ms … 295.2 ms    10 runs

Benchmark 2: scratch_new.exe
  Time (mean ± σ):     129.7 ms ±   6.8 ms    [User: 17.1 ms, System: 9.7 ms]
  Range (min … max):   120.9 ms … 138.4 ms    21 runs

Summary
  scratch_new.exe ran
    2.18 ± 0.13 times faster than scratch.exe

了解更多关于 deno compile 的各种用例,包括从 JavaScript、HTML 和 CSS 创建桌面游戏。

权限提示中的堆栈跟踪

Deno 的权限系统 是其最受欢迎的功能之一。使用交互式权限提示是了解程序正常运行所需的权限的有用方法。这些提示的一个缺点是它们仅显示哪个 API 需要什么权限,但您看不到确切在何处请求该权限。

Deno v2.1 解决了这个缺点

Demo of stack trace in permission prompt

权限提示中堆栈跟踪的演示。

使用 DENO_TRACE_PERMISSIONS 环境变量运行任何程序都将启用在请求权限时收集堆栈跟踪,从而使您可以轻松查明程序的哪个部分需要特定权限。

请记住,此功能开销很大,并且会导致明显的性能下降 - 每次可能请求权限时都收集堆栈跟踪速度很慢,不应在生产中使用。

增强的 deno task

此版本为 deno task 带来了重大改进。

首先,您现在可以将任务编写为对象并为它们提供信息丰富的描述

{
  "tasks": {
    "build": "deno run -RW build.ts",
    "serve": {
      "description": "Start the dev server",
      "command": "deno run -RN server.ts"
    }
  }
}
$ deno task
Available tasks:
- build
    deno run -RW build.ts
- serve
    // Start the dev server
    deno run -RN server.ts

添加了 "description" 选项,以取代(现在已删除的)对 deno.jsonc 文件中注释的处理。

任务依赖项

Deno 任务现在可以具有依赖项

{
  "tasks": {
    "build": "deno run -RW build.ts",
    "generate": "deno run -RW generate.ts",
    "serve": {
      "command": "deno run -RN server.ts",
      "dependencies": ["build", "generate"]
    }
  }
}

在上面的示例中,运行 deno task serve 将首先并行执行 buildgenerate 任务,并且在它们都成功完成后,将执行 serve 任务

$ deno task serve
Task build deno run -RW build.ts
Task generate deno run -RW generate.ts
Generating data...
Starting the build...
Build finished
Data generated
Task serve deno run -RN server.ts
Listening on https://127.0.0.1:8000/

deno task 处理依赖项中的循环,并将确保您不会陷入无限循环

{
  "tasks": {
    "a": {
      "command": "deno run a.js",
      "dependencies": ["b"]
    },
    "b": {
      "command": "deno run b.js",
      "dependencies": ["a"]
    }
  }
}
$ deno task a
Task cycle detected: a -> b -> a

“菱形依赖项”(其中两个依赖项共享一个共同的依赖项)被正确理解,并且 deno task 将确保仅执行一次被多个其他任务依赖的任务

{
  //   a
  //  / \
  // b   c
  //  \ /
  //   d
  "tasks": {
    "a": {
      "command": "deno run a.js",
      "dependencies": ["b", "c"]
    },
    "b": {
      "command": "deno run b.js",
      "dependencies": ["d"]
    },
    "c": {
      "command": "deno run c.js",
      "dependencies": ["d"]
    },
    "d": "deno run d.js"
  }
}
$ deno task a
Task d deno run d.js
Running d
Task c deno run c.js
Running c
Task b deno run b.js
Running b
Task a deno run a.js
Running a

当前不支持任务中的跨包依赖项,但计划在未来支持。

工作区支持

通过添加 --filter--recursive 标志,您现在可以使用 deno task您的工作区的所有或某些成员运行任务

// deno.json
{
  "workspace": [
    "client",
    "server"
  ]
}

// client/deno.json
{
  "tasks": {
    "dev": "deno run -RN build.ts"
  }
}

// server/deno.json
{
  "tasks": {
    "dev": "deno run -RN server.ts"
  }
}
$ deno task --recursive dev
Task dev deno run -RN build.ts
Task dev deno run -RN server.ts
Bundling project...
Listening on https://127.0.0.1:8000/
Project bundled
$ deno task --filter "client/" dev
Task dev deno run -RN build.ts
Bundling project...
Project bundled

请注意,工作区支持尚处于早期阶段,并且在某些情况下,deno task 的行为可能不是您所期望的。如果出现这种情况,我们将感谢您打开 错误报告

deno task --eval

deno task 由定制的跨平台 shell 实现提供支持,使您可以访问常见的命令,如 catechosleep 等,这些命令可在所有平台上运行。

您现在可以直接利用此 shell,通过显式评估任务,而无需在 deno.json 文件中指定它们

$ deno task --eval "echo $(pwd)"
Task echo $(pwd)
/dev/

运行 deno task --eval "<task>" 等效于 npm exec -cpnpm exec


诸如工作区拓扑排序、基于输入/输出文件的缓存和通配符支持等功能已在路线图上。

阅读 Deno 文档,了解关于 deno task 所有新功能的更多信息。

Node.js 和 npm 兼容性改进

更好的 CommonJS 支持

Deno v2.0 对 CommonJs 支持进行了重大改进,使得使用 Deno 消费现有项目变得更加容易。虽然情况已经好转了很多,但我们的用户提供了很多反馈,表示运行这些项目仍然不是那么容易。Deno v2.1 通过带来更好的 CJS 支持来满足这些需求,从而使较旧的项目可以在 Deno 下运行。

Node.js 默认将 .js 视为 CommonJS,除非最近的 package.json 指定 "type": "esm"。Deno 一直将所有文件都视为 ESM,并将继续这样做,但为了更容易运行或迁移现有代码库,Deno 现在将查找最近的 package.json 文件,并在文件指定其 type 时将其视为 CommonJS

{
+ "type": "commonjs",
  "dependencies": {
    "express": "^4"
  }
}
// main.js
const express = require("express");
const app = express();
const port = 3000;

app.get("/", (req, res) => {
  res.send("Hello World!");
});

app.listen(port, () => {
  console.log(`Example app listening on port ${port}`);
});
# Deno v2.0
$ deno run -REN main.js
error: Uncaught (in promise) ReferenceError: require is not defined
const express = require("express");
                ^
    at file:///main.js:1:17

    info: Deno supports CommonJS modules in .cjs files, or when there's a package.json
          with "type": "commonjs" option and --unstable-detect-cjs flag is used.
    hint: Rewrite this module to ESM,
          or change the file extension to .cjs.
# Deno v2.1
$ deno run -REN main.js
Example app listening on port 3000

如果您尝试运行一个使用 CommonJS 的项目,但未在 package.json 中指定 "type": "commonjs" 选项,Deno 将提供有用的提示来解决问题

$ deno run -REN main.js
error: Uncaught (in promise) ReferenceError: require is not defined
const express = require("express");
                ^
    at file:///main.js:1:17

    info: Deno supports CommonJS modules in .cjs files, or when the closest
          package.json has a "type": "commonjs" option.
    hint: Rewrite this module to ESM,
          or change the file extension to .cjs,
          or add package.json next to the file with "type": "commonjs" option.
    docs: https://docs.deno.org.cn/go/commonjs

最后,我们改进了 CommonJS 模块的静态分析,这意味着在许多情况下,您不再需要 --allow-read 权限来执行 CJS 模块。

改进了对 Node.js 全局变量的支持

Deno 为所有 npm 包提供了所有 Node.js 全局变量,如 BufferglobalsetImmediate 等。与 CommonJS 支持类似,默认情况下不提供这些全局变量可能会在尝试运行或迁移现有项目到 Deno 时带来挑战和障碍。

// main.js
console.log(global.btoa("Hello!"));
# Deno v2.0
$ deno main.js
error: Uncaught (in promise) ReferenceError: global is not defined
console.log(global.btoa("Hello!"));
            ^
    at file:///Users/ib/dev/scratch_express/main.js:1:13

    info: global is not available in the global scope in Deno.
    hint: Use globalThis instead, or assign globalThis.global = globalThis.

在 Deno v2.1 中,添加了一个新的 --unstable-node-globals 标志,该标志添加了以下全局变量

  • Buffer
  • global
  • setImmediate
  • clearImmediate
# Deno v2.1
$ deno main.js
error: Uncaught (in promise) ReferenceError: global is not defined
console.log(global.btoa("Hello!"));
            ^
    at file:///main.js:1:13

    info: global is not available in the global scope in Deno.
    hint: Use globalThis instead, or assign globalThis.global = globalThis,
          or run again with --unstable-node-globals flag to add this global.

$ deno --unstable-node-globals main.js
SGVsbG8h

其他兼容性修复

--allow-env 通配符

您现在可以在 --allow-env(或 -E)标志中指定后缀通配符,以允许“作用域”访问环境变量。您通常需要为程序提供多个环境变量,这些变量以公共前缀分组,例如 AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEYAWS_DEFAULT_REGION。在终端中指定所有这些变量可能很麻烦,因此 Deno v2.1 使其变得更容易

# Deno v2.0
$ deno run --allow-env=AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY,AWS_DEFAULT_REGION main.ts
# Deno v2.1
$ deno run --allow-env="AWS_*" main.ts

现在您的程序可以读取和写入所有以 AWS_ 前缀开头的环境变量。

格式化和 linting 改进

deno fmt 是 Deno 工具链中一个非常有用的工具,并且此版本通过支持 .sql 文件使其更加有用

-- query.sql
SELECT   *
from users WHERE   email = "[email protected]"
$ deno fmt --unstable-sql
query.sql
Formatted 1 file

$ cat query.sql
SELECT
  *
FROM
  users
WHERE
  email = "[email protected]"

此支持仍然不稳定,因此您需要使用 --unstable-sql 标志,或将 "fmt-sql" 添加到 deno.json 文件中的 unstable 选项。

您可以通过在文件顶部添加忽略注释来选择忽略格式化 SQL 文件

-- deno-fmt-ignore-file
SELECT   *
from users WHERE   email = "[email protected]"
$ deno fmt --unstable-sql
Checked 1 file

$ cat query.sql
-- deno-fmt-ignore-file
SELECT   *
from users WHERE   email = "[email protected]"

您现在还可以通过在 YAML 文件顶部添加 # deno-fmt-ignore-file 指令来忽略格式化 YAML 文件

# deno-fmt-ignore-file
-    Test

此版本中修复了许多与格式化 HTML、CSS、YAML 和组件文件相关的问题,但如果您遇到任何问题,我们将感谢您打开 错误报告


最后,deno fmtdeno lint 变得更智能,并且不再尝试格式化/linting .gitignore 文件中列出的文件。

在以前的版本中,您可能遇到过 Deno 格式化或标记流行框架生成的构建工件中的问题的情况。从 Deno v2.1 开始,这个问题应该会消失。

在 Jupyter 笔记本中显示图像

新的 Deno.jupyter.image() API 允许您轻松地在笔记本中显示图像

Deno.jupyter.image("./logo.png");

Deno of `Deno.jupyter.image` API

Deno 的 `Deno.jupyter.image` API

您还可以显示您之前生成或读取的图像

const data = Deno.readFileSync("./cat.jpg");
Deno.jupyter.image(data);

该 API 支持 JPEG 和 PNG 图像。

deno publish 中覆盖版本

您现在可以在 deno publish 中使用 --set-version 标志来覆盖 deno.json 文件中指定的包版本

{
  "name": "@deno/otel",
  "version": "0.1.0",
  "exports": "./main.ts"
}
$ deno publish --set-version 0.2.0
Publishing @deno/[email protected] ...
Successfully published @deno/[email protected]

此标志只能在发布单个包时使用,并且在工作区设置中不起作用。

LSP 改进

Deno 附带的内置语言服务器收到了多项改进

性能改进

几项值得注意的性能改进使 Deno 再次变得更快

生活质量改进

V8 13.0

Deno 2.1 搭载最新的 V8 13.0。

致谢

没有我们社区的帮助,我们就无法构建 Deno!无论是通过在我们的社区 Discord 服务器 中回答问题还是 报告错误,我们都非常感谢您的支持。 特别感谢以下人员为 Deno 2.1 做出的贡献:Benjamin Swerdlow, Bhuwan Pandit, Chris Veness, Cornelius Krassow, Eli Uriegas, HasanAlrimawi, Jeremy Tuloup, João Baptista, Kaveh, Keith Maxwell, Keith Tan, Kenta Moriuchi, Kyle Kelley, LongYinan, Lucas Nogueira, Mayank Kumar, McSneaky, Meir Blachman, Miguel Rodrigues, Mohammad Sulaiman, Nicola Bovolato, Patrick Uftring, Pig Fang, Richard Carson, Ronny Chan, Sahand Akbarzadeh, Scott Twiname, Soc Virnyl S. Estela, Taku Amano, Toby Ealden, Volker Schlecht, Yazan AbdAl-Rahman, familyboat, haturau, jiang1997, reczkok, tsukasa-ino, Łukasz Czerniawski, 和 林炳权。

您想加入 Deno 贡献者的行列吗? 请查看我们的贡献文档,我们期待下次在列表中见到您。

信不信由你,上面列出的更改仍然没有告诉您 2.1 版本中所有改进之处。 您可以在 GitHub 上查看 Deno 2.1 中合并的拉取请求的完整列表:在 GitHub 上查看

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

TwitterDiscordBlueSkyMastodon 上获取技术支持、分享您的项目或 просто 打个招呼。

请收看我们 12 月 3 日太平洋时间上午 8 点 / 东部时间上午 11 点 / 世界协调时间下午 4 点的 2.1 直播,我们将在直播中演示和讨论最大的变化以及这些变化对您的意义。