跳到主要内容
Deno 1.40

我们很高兴地宣布 Deno 1.40 发布,这是 Deno 发展历程中的一个重要里程碑。这个新版本包含许多增强 Deno 体验的功能,引入了强大的 Temporal API,用于高级日期和时间操作,并采用了最新的 装饰器语法,以实现更具表现力的代码。除了这些进步之外,我们还实施了一系列 弃用、稳定化和移除,旨在简化 Deno 的功能,并为 Deno 2 做准备。

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

deno upgrade

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

MacOS / Linux 安装

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

Windows 安装

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

以下是 Deno 1.40 中的新功能概述

Temporal API

Temporal API 旨在解决 JavaScript 中现有 Date 对象的一些缺点和复杂性。

Temporal 提案正在被所有主要的 JavaScript 引擎积极实施,我们很高兴地宣布它现在已在 Deno 中可用,需使用 --unstable-temporal 标志。

const birthday = Temporal.PlainMonthDay.from("12-15");
const birthdayIn2030 = birthday.toPlainDate({ year: 2030 });
console.log(birthdayIn2030.toString());
// 2030-12-15

console.log("day of week", birthdayIn2030.dayOfWeek);
// day of week 7
Temporal 的用户友好型 API 使解析和操作日期和时间变得容易。

Temporal API 不太可能更改,我们的目标是在 Deno 2 中将其稳定化。我们鼓励您探索 Temporal API 文档

import.meta.filenameimport.meta.dirname

Deno 现在支持 import.meta.filenameimport.meta.dirname 属性。

这些属性镜像了 CommonJS 模块系统中的 __filename__dirname 属性

  • import.meta.filename 提供当前模块文件的绝对路径
  • import.meta.dirname 提供包含当前模块文件的目录的绝对路径

这两个属性都感知操作系统特定的路径分隔符,并为当前平台提供正确的分隔符

console.log(import.meta.filename);
console.log(import.meta.dirname);

在 Unix 中

$ deno run /dev/my_module.ts
/dev/my_module.ts
/dev/

在 Windows 中

$ deno run C:\dev\my_module.ts
C:\dev\my_module.ts
C:\dev\

这些属性仅适用于本地模块(即从文件系统加载的模块),对于远程模块(从 http://https:// 导入的模块)则为 undefined

装饰器

Deno 现在支持 TC39 stage 3 装饰器提案,该提案很快将在所有浏览器中实现。

装饰器是扩展 JavaScript 类的提案,在转译器环境中被开发者广泛采用,并且在标准化方面具有广泛的兴趣。TC39 已经迭代装饰器提案五年多了。本文档描述了一个基于所有过去提案元素的新装饰器提案。

此功能在 .ts.jsx.tsx 文件中可用。对纯 JavaScript 的支持正在等待 V8 中的实现。

这是一个 @trace 装饰器的示例,它会在函数被调用时以及返回时记录日志

function trace(fn: any, ctx: ClassMethodDecoratorContext) {
  return function (...args: unknown[]) {
    console.log("ENTERED", ctx.name);
    const v = fn(...args);
    console.log("EXITED", ctx.name);
    return v;
  };
}

class App {
  @trace
  static start() {
    console.log("Hello World!");
  }
}

App.start();

如果您依赖于传统的“实验性 TypeScript 装饰器”,您仍然可以使用以下配置来使用它们

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

注意: 在 v1.40.0 发布后,发现了一个错误,TypeScript 的 experimentalDecorators 在 LSP 中仍然处于启用状态。请升级到 v1.40.1。

deno.json 中更简单的 imports

deno.json 中的 imports 字段现在支持更简单的语法,用于指定具有子路径导出的依赖项。以前,当想从 npm 使用 preact 时,您必须将此添加到 deno.json 中的 imports 对象中

{
  "imports": {
    "preact": "npm:preact@10.5.13",
    "preact/": "npm:/preact@10.5.13/"
  }
}

这允许您导入 preact 和子路径导出,例如 preact/hooks

在此版本中,我们简化了此操作,以便您现在只需执行以下操作即可

{
  "imports": {
    "preact": "npm:preact@10.5.13"
  }
}

在 Deno 1.40.0 中,这将允许您从 npm 导入 preactpreact/hooks

以前,Deno 认为 deno.json 中的 imports 字段只是一个常规的 导入映射。现在,我们预处理 deno.json 中的 imports 字段,并将右侧带有 npm: 前缀的任何条目扩展为我们用于解析的内部导入映射中的两个条目。

弃用、稳定化和移除

当我们为 Deno 2 做准备时,我们致力于改进运行时,同时确保从 Deno 1 平滑过渡。虽然大多数 Deno 1 代码将保持兼容,但我们正在简化某些 API,以确保平台的长期健康发展。

弃用

我们正在引入弃用以逐步淘汰旧的 API,同时提供警告以帮助您进行迁移

  • window – 全局变量 window 在整个 JavaScript 生态系统中经常用于测试代码是否在浏览器中运行。使用 globalThisself 来代替很容易修复。此弃用尚无运行时警告,但从本版本开始,它将显示在 deno lint 中。

  • Deno.run() – 这是旧的且容易出错的子进程 API。此后,我们引入了功能更强大的 Deno.Command API,该 API 在一年前已稳定化

  • Deno.serveHttp() – 已被更快更简单的 Deno.serve() 替代。文档在此

  • Deno.metrics() – 为了专注于性能,我们正在转向更有针对性的命令行标志和 API。自定义指标实现可通过 op_metrics_factory_fn 和新的 --strace-ops 标志(用于运行时操作跟踪)获得。

  • 流相关函数:在过渡到 Web Streams 的过程中,我们正在弃用几个 Deno.Reader/ Deno.Writer 流函数。弃用的函数仍然可以通过 Deno 标准库访问

  • Deno.FsWatcher.return() – 为了与其他异步迭代器保持一致,我们正在弃用此方法,而改用显式的 close 方法。

  • Deno.customInspect – 为了鼓励浏览器兼容的代码,我们已切换到 Symbol.for("Deno.customInspect")

在 Deno 2 中,我们将删除“资源 ID”的概念。资源 ID 是对套接字、文件或其他在 JavaScript 之外管理的资源的整数引用。它们类似于文件描述符。但是,大多数用户不会直接接触这些资源 ID,我们希望开始引入由原生 JavaScript 对象引用的资源。因此,我们正在弃用以下 API

  • Deno.isatty() – 请改用 isTerminal() 方法,例如 Deno.stdout.isTerminal()

  • Deno.close() – 此 API 也对 rid 进行操作。建议用户改用相关对象上的 .close() 方法。例如,file.close() 而不是 Deno.close(file.rid)

  • Deno.resources() – 此 API 也暴露资源 ID,实用性不大。

  • Deno.ftruncate()Deno.ftruncateSync() – 这些函数现在在 Deno.FsFile 上作为 truncate()truncateSync() 提供。

所有 rid 属性现在都已弃用,将在 Deno 2.0 中删除

  • Deno.Conn.rid
  • Deno.FsWatcher.rid
  • Deno.TcpConn.rid
  • Deno.TlsConn.rid
  • Deno.UnixConn.rid
  • Deno.Listener.rid

从文件加载证书现在已弃用,请自行读取证书

  • Deno.ListenTlsOptions.certFile - 请改用 Deno.ListenTlsOptions.certDeno.readTextFile
  • Deno.ListenTlsOptions.keyFile - 请改用 Deno.ListenTlsOptions.keyDeno.readTextFile
  • Deno.ConnectTlsOptions.certFile - 请改用 Deno.ConnectTlsOptions.certDeno.readTextFile

稳定化

以下 Deno API 已稳定化并取消标记

  • Deno.Conn.ref()
  • Deno.Conn.unref()
  • 用于 unix 传输的 Deno.connect()
  • Deno.connectTls
  • Deno.stderr.isTerminal()
  • Deno.stdin.isTerminal()
  • Deno.stdout.isTerminal()
  • Deno.TlsConn.handshake()

移除

最后,不稳定的 API Deno.upgradeHttp 已被移除。此 API 容易出错且容易被误用。我们鼓励大家使用 Deno.serve()Deno.upgradeWebsocket()

Web API:rejectionhandled 事件

我们添加了对 rejectionhandled 事件的支持,该事件在任何时候当 .catch() 处理程序附加到已经拒绝的 promise 时都会发出。此外,只有当您有一个调用 event.preventDefault()unhandledrejection 事件监听器时,才会发出此事件(否则,promise 将被拒绝,并且进程将以错误退出)。

globalThis.addEventListener("unhandledrejection", (event) => {
  // Call `preventDefault()` to prevent the process from exiting.
  event.preventDefault();
  console.log("unhandledrejection", event.reason);
});

globalThis.addEventListener("rejectionhandled", (event) => {
  console.log(
    "A .catch() handler was added to the promise after it has already rejected.",
    event.reason,
  );
});

// Create a rejected promise...
const a = Promise.reject(new Error("boom!"));

// ...then attach a `.catch()` handler to after a timeout.
setTimeout(async () => {
  a.catch(() => console.log("Added catch handler to the promise"));
}, 10);
$ deno run main.js
unhandledrejection Error: boom!
    at file:///dev/main.js:12:26
Added catch handler to the promise
A .catch() handler was added to the promise after it has already rejected. Error: boom!
    at file:///dev/main.js:12:26

WebGPU 窗口化 / “自带窗口”

我们正在引入一个新的不稳定的 Deno.UnsafeWindowSurface API,以解决 Deno 中的窗口化问题。我们的目标是为 WebGPU 提供窗口化解决方案,而无需链接到 X11 等原生窗口系统。

这是一个底层 API,可以被 FFI 窗口库(如 sdl2glfwraylibwinit 等)使用,以使用原生窗口和显示句柄创建 WebGPU 表面。

这是一个使用 deno.land/x/sdl2 的示例

import {
  EventType,
  WindowBuilder,
} from "https://deno.land/x/sdl2@0.8.0/mod.ts";

const win = new WindowBuilder("Hello, World!", 800, 600).build();

const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();

/* Returns a Deno.UnsafeWindowSurface */
const surface = win.windowSurface();
/* Returns a WebGPU GPUCanvasContext */
const context = surface.getContext("webgpu");

context.configure({/* ... */});

for (const event of win.events()) {
  if (event.type == EventType.Quit) break;

  // Sine wave
  const r = Math.sin(Date.now() / 1000) / 2 + 0.5;
  const g = Math.sin(Date.now() / 1000 + 2) / 2 + 0.5;
  const b = Math.sin(Date.now() / 1000 + 4) / 2 + 0.5;

  const textureView = context.getCurrentTexture().createView();
  const renderPassDescriptor = {
    colorAttachments: [
      {
        view: textureView,
        clearValue: { r, g, b, a: 1.0 },
        loadOp: "clear",
        storeOp: "store",
      },
    ],
  };

  const commandEncoder = device.createCommandEncoder();
  const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
  passEncoder.end();

  device.queue.submit([commandEncoder.finish()]);
  surface.present();
}

使用 deno run --allow-ffi --unstable-webgpu --unstable-ffi 运行

这是一个使用此 API 构建的演示音乐播放器应用程序:https://github.com/littledivy/wgui

有关底层使用的更多详细信息,请查看 PR:https://github.com/denoland/deno/pull/21835

在此处阅读有关 GPUCanvasContext 的更多信息:此处

Node.js API 更新

以下内置 Node API 现在可用

  • crypto.pseudoRandomBytes()
  • fs.contants
  • fs.cp()
  • fs.cpSync()
  • fs.promises.cp()
  • net.ClientRequest.setNoDelay()
  • net.UdpSocket.ref()net.UdpSocket.unref()
  • net.WriteStream.isTTY
  • os.cpus()
  • os.machine()
  • process.abort()
  • process.on("rejectionHandled")

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

  • Windows 上的 child_process.ChildProcess.send()
  • crypto.createDeciperiv() 现在支持 aes-192-ecbaes-256-ecb
  • fs.promises.readFile()
  • http.ClientRequest.socket.remoteAddress
  • http.ClientRequest.socket.remotePort
  • querystring.stringify()
  • test 模块支持嵌套测试
  • zlib.brotliCompress()
  • zlib.brotliCompressSync()
  • zlib.gzipSync()
  • zlib.unzipSync()

LSP 改进

自 v1.39.0 以来,我们加强了与 TypeScript 嵌入式实例的 Language Service API 的集成,以实现显著的性能提升和一些错误修复。由于更智能的项目状态同步和缓存,Rust 和 TypeScript 隔离实例之间的数据交换更高效且频率更低。

针对 jsxImportSource 编译器选项用户的质量改进:当包含 deno.json 文件被保存时,指定的远程资源将被自动缓存。这是必要的,因为与未缓存的导入不同,由此缺失资源产生的诊断信息含糊不清,并且没有指出问题的根源(无论是从用户的角度还是从语言服务器的快速修复生成器的角度来看)。

自动导入补全将更加一致地工作。由于 TypeScript 隔离实例中更好的状态保存,某些补全解析会静默错误的情况已得到修复。带有子路径的导入映射 NPM 说明符已正确替换为预期的别名。

更好看的外观诊断信息

deno lintdeno doc 中有一个新的诊断打印器。在即将发布的版本中,我们将把它扩展到其他子命令。

deno lint 更新

deno lint 中提供了三个新规则

  • no-console
  • no-self-compare
  • no-window

no-window 规则默认启用,而其他两个是可选的,您需要在配置文件中启用它们

{
  "lint": {
    "rules": ["no-console", "no-self-compare"]
  }
}

保持代码质量对于项目的成功至关重要,我们都发现自己处于需要抑制来自 linter 的警告的情况。

在大多数情况下,我们只是忽略警告并继续前进,但是这种方法无助于团队成员或未来的自己理解为什么某个特定警告被抑制。

deno lint 现在在 // deno-lint-ignore// deno-lint-ignore-file 指令中支持额外的解释

// deno-lint-ignore-file -- This file is autogenerated, no need to lint it.

var __global$ = globalThis || (typeof window !== "undefined" ? window : self);
var cu=Object.create;var R=Object.defineProperty;var lu=Object.getOwnPropertyDescriptor;var iu=Object.getOwnPropertyNames;var nu=Object.getPrototypeOf...
// deno-lint-ignore no-empty -- I really need this fn have no body
export function noop() {}

我们处理不稳定功能的方式变更

我们正在改进管理不稳定功能的方法。--unstable 标志虽然有用,但有点不精确,会同时激活所有不稳定功能。在 Deno 1.38 中,我们引入了更细化的标志,以便您可以更精细地控制特定的不稳定功能,例如 --unstable-webgpu 启用新的 WebGPU API。在此基础上,Deno 1.40 标志着广泛的 --unstable 标志的弃用阶段的开始,为在 Deno 2 中移除它奠定了基础。

此外,我们改进了关于 deno check 和 LSP 中不稳定 API 的类型检查。现在,稳定和不稳定 API 的类型定义在类型检查期间都会自动包含在内。这消除了指定 --unstable 来访问不稳定 API 类型定义的需要。但是,请记住在运行程序时启用特定的不稳定功能标志。省略这些标志仍然会导致错误,确保您意识到正在使用的不稳定功能。

此更改简化了开发,在 Deno 功能集的使用方面提供了更高的清晰度和控制力。

感谢我们的贡献者!

没有我们社区的帮助,我们就无法构建 Deno!无论是通过回答我们社区 Discord 服务器 中的问题还是报告错误,我们都非常感谢您的支持。特别是,我们要感谢以下人员对 Deno 1.40 的贡献:Anwesh, Dean Srebnik, Joel Walker, Jovi De Croock, Julien Cayzac, Jérôme Benoit, Kenta Moriuchi, Kitson Kelly, Lino Le Van, Raashid Anwar, cions, king8fisher, nokazn, 林炳权。

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

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

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

🍋 Fresh 1.6 已发布。

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