跳到主要内容
Deno 2.4 发布,带来 deno bundle、字节/文本导入、OTel 稳定版等更多功能
了解更多
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 引擎积极实现,我们很高兴地宣布,它现在可以通过 --unstable-temporal 标志在 Deno 中使用。

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 字段视为一个普通的 导入映射 (import map)。现在,我们预处理 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() – 为了与其他异步迭代器保持一致,我们正在弃用此方法,转而使用显式关闭方法。

  • 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() – 这些函数现在作为 truncate()truncateSync()Deno.FsFile 上可用。

所有 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() 处理器被附加到一个已经拒绝的 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 窗口化 / “自带窗口”

我们正在引入一个新的不稳定 API Deno.UnsafeWindowSurface,以解决 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 的更多信息:here

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 语言服务 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 中所有改进。您可以在 GitHub 上查看 Deno 1.40 中合并的完整拉取请求列表

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

🍋 Fresh 1.6 已发布。

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