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

我们很高兴地宣布发布 Deno 1.40,这是 Deno 发展史上的一个重要里程碑。这个新版本包含了许多增强 Deno 体验的功能,引入了强大的 时间 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 第 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:[email protected]",
    "preact/": "npm:/[email protected]/"
  }
}

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

在这个版本中,我们简化了这一点,所以您现在只需要这样做即可。

{
  "imports": {
    "preact": "npm:[email protected]"
  }
}

在 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 流,并将弃用一些 Deno.Reader/ Deno.Writer 流函数。弃用的函数仍然可以通过 Deno 标准库访问

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

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

在 Deno 2 中,我们将删除“资源 ID”的概念。资源 ID 是对套接字、文件或其他在 JavaScript 之外管理的资源的整数引用。它们类似于文件描述符。但是,大多数用户不会直接接触这些资源,我们希望开始引入由原生 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()
  • Deno.connect() 用于 unix 传输
  • 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() 处理程序附加到一个已经被拒绝的承诺上。此外,此事件仅在您拥有一个 unhandledrejection 事件侦听器并调用 event.preventDefault() 时才会被发出(否则承诺将被拒绝,并且进程将退出并显示错误)。

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,可以被像 sdl2glfwraylibwinit 等 FFI 窗口化库使用,以使用本地窗口和显示句柄创建 WebGPU 表面。

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

import {
  EventType,
  WindowBuilder,
} from "https://deno.land/x/[email protected]/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 中的若干错误

  • child_process.ChildProcess.send() 在 Windows 上
  • 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 的嵌入式 语言服务 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的类型定义。这消除了为访问不稳定API类型定义而需要指定--unstable的必要性。但是,请记住在运行程序时启用特定的不稳定功能标志。省略这些标志仍然会导致错误,确保您知道正在使用的不稳定功能。

此更改简化了开发,为使用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中所有改进的地方。你可以查看在Deno 1.40中合并的全部拉取请求列表在GitHub上

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

🍋 新鲜出炉的1.6版本已发布。

Fresh v1.6应用程序扩展了插件API,实现了更快的路由匹配,并正式支持Tailwind CSS。