跳到主要内容
Deno 1.43

Deno 1.43: 改进的 Language Server 性能

编程应该很简单。这就是为什么我们创建了 Deno,一个零配置、自带电池的 JavaScript 运行时,原生支持 TypeScript,能够立即提高生产力。

在 1.43 版本中,我们通过将大型代码库中的自动完成时间从 6-8 秒缩短到一秒以下,并显著降低内存使用量,从而增强了 Deno 在 IDE 中的性能。通过重构我们的 node:vm 和 node:worker_threads 实现,我们在 npm 兼容性方面也取得了更大的进展,这两个模块广泛用于 JavaScript CLI 工具(如测试运行器)。

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

deno upgrade

如果尚未安装 Deno,请点击此处了解如何安装


Deno 1.43 的新特性

加速 Deno 的 Language Server

一些 Deno 用户联系我们,告知我们的 Language Server 在处理包含大量文件的较大项目时遇到困难。Language Server(通常称为 Deno LSP)在编辑器中提供自动完成等功能。当他们分享在这些项目中编辑器的体验时,我们意识到我们可以大幅提高响应速度和降低内存消耗。

在这个周期中,我们花费了大部分时间来重构 LSP 的许多方面,使其更快、更高效。在大型项目中,自动完成以前大约需要 6-8 秒,现在我们已将其缩短到一秒以下。同样,内存消耗也得到了显著改善,使得以前在 LSP 中导致内存不足错误的项目现在可以正常运行。

之前

之后

Node.js 和 npm 兼容性

此版本包括围绕 node:worker_threadsnode:vm 模块的许多关键改进。这两个模块都经常用于测试运行器(如 Jest 和 Vitest)以及 Docusaurus 等工具。事实上,我们现在正在运行 Docusaurus 来驱动我们的一些文档站点,并使用 Deno。

  • 在 Rust 中实现 process.kill 以避免运行权限提示。这使得流行的 CLI 进度条库 ora 可以在 Deno 中工作。
  • 添加缺少的 http.maxHeaderSize 值以使 undici 工作。
  • fs.cpSync: 始终确保 SolidStart 的父目录存在。
  • node:worker_threads 中支持 env 选项。这使得调用 sveltekit build 可以成功完成。
  • 正确地在节点 TLS 连接上发送 ALPN,这曾使上游服务器感到困惑
  • 修复 AsyncResource 借用崩溃,该崩溃破坏了 ng servevitest
  • 修复 node:vm 上下文中的 Promise 拒绝,该拒绝用于 docusaurus build
  • 实现用于 @angular/cli serveMessagePort.unref()
  • 修复 fs.createWriteStream 的乱序写入,该问题发生在 koa-body 中。
  • 确保 node:http 中的 hostname 是有效的 IPv4 地址,这修复了 docusaurus serve
  • docusaurus buildmodule 添加到 builtinsModule
  • 修复 node:worker_thread worker 过早退出的问题。
  • polyfill node:domain 模块以修复 web-ext 中的配置发现。
  • 修复传输的 MessagePort 没有 .on 处理程序的问题,这是 Angular 中使用的 piscina 所需要的。
  • 修复 node:util 中的 parseArgs 不支持 default 选项的问题。
  • 添加缺少的 fs.readvfs.readvSync 函数。

其他框架(如 SolidStart)也正在逐步接近完全支持。我们已经掌握了基本知识,但要完全支持其 https 服务器和 auth-js 仍需完成一些工作

Short clip showing launching a SolidStart project in Deno

SolidStart 和其他框架兼容性改进正在新的 DENO_FUTURE=1 环境变量背后进行,您可以在下方阅读相关内容。

我们解决的另一个领域是改进 AsyncResource,这对于使 vitest 工作是必要的,因为它依赖于 tinypool 包,而 tinypool 包又在底层大量使用了 Node 的 AsyncResource

当然,我们也在努力支持 Next.js。由于我们在处理 node_modules 目录中的布局方面进行了一些改进,我们使初始化向导完全正常工作。

~/my-app $ DENO_FUTURE=1 deno task dev
Task dev next dev
   ▲ Next.js 14.1.3
   - Local:        http://localhost:3000

 ✓ Ready in 2.2s
 ○ Compiling / ...
 ✓ Compiled / in 5.5s (511 modules)
 ✓ Compiled in 381ms (241 modules)

目前运行 Next.js 需要在 Deno 中启用一些不稳定的标志,我们正在稳步推进稳定这些标志的过程。

在 deno.json 任务中使用 npm 命令

可执行的 npm 命令(如 vite),通常通过 package.json 脚本进行管理,现在也可以直接在 deno.json 中定义的任务中引用。

// deno.json
{
  "tasks": {
    "start": "vite"
  }
}

更快的 ES 和 CommonJS 模块加载

此版本增加了对 V8 代码缓存(也称为字节码缓存)的支持,这可以显著缩短应用程序在解析和编译 JavaScript 模块上花费的时间。编译后的字节码在模块首次加载时会自动缓存在本地磁盘上,然后在后续加载时重复使用。

在我们的测试中,我们观察到启动时间提高了 5%240%,具体取决于应用程序。

JSX 预编译改进

v1.38 版本以来,Deno 开箱即用地提供了一个针对服务器端渲染性能优化的 precompile JSX 转换。在某些情况下,框架可能希望阻止元素被预编译,以允许向其传递其他属性。我们的转换学习了一种新技巧,可以通过 jsxPrecompileSkipElements 编译器选项来实现这一点。

// deno.json
{
  "compilerOptions": {
    "jsx": "precompile",
    "jsxImportSource": "preact",
    // Don't precompile <a>, <body>, and <img> elements
    "jsxPrecompileSkipElements": ["a", "body", "img"]
  }
}

您可以将元素数组传递给该选项,以将其从预编译中排除。

// Example: Input
const a = <a href="#">click me</a>;

// ...precompiled output
const $$_tpl_1 = ['<a href="#">click me</a>'];
const a = jsxTemplate($$_tpl_1);

// ...non-precompiled output
const a = jsx("a", {
  href: "#",
  children: "click me",
});

jsxImportSourceTypes

新的 jsxImportSourceTypes 编译指示和编译器选项允许指定自动 JSX 转换的类型。这对于与不提供其类型的库一起使用很有用。

/** @jsxImportSource npm:react@^18.3 */
/** @jsxImportSourceTypes npm:@types/react@^18.3 */

export function Hello() {
  return <div>Hello!</div>;
}

或者,这也可以在 deno.json 文件中指定

{
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "npm:react@^18.3",
    "jsxImportSourceTypes": "npm:@types/react@^18.3"
  }
}

引入 deno serve 子命令

在此版本中,我们添加了 deno serve 子命令,允许您以声明方式编写服务器

export default {
  fetch(request) {
    return new Response("Hello world");
  },
};
$ deno serve server.ts
deno serve: Listening on http://localhost:8000/
$ curl http://localhost:8000/
Hello world

请注意,您不必传递任何权限标志 - deno serve 会自动应用适当的 --allow-net 权限,使其能够监听传入的 HTTP 连接。当然,您可以根据需要传递额外的权限标志。

此外,您可以使用 --host--port 标志来配置服务器绑定的接口

$ deno serve --host 0.0.0.0 --port 3000 server.ts
deno serve: Listening on http://0.0.0.0:3000/
$ curl http://0.0.0.0:3000/
Hello world

我们的计划是在未来引入自动负载均衡,这将在多个 CPU 核心上运行服务器,从而更好地利用您的系统资源。

Deno.serve() 更新

响应完成

现在,您可以使用 Deno.ServeHandlerInfo.completed promise 获取有关响应是否成功发送或是否发生故障的信息

Deno.serve((req, info) => {
  info.completed.then(() => {
    console.log("Response sent successfuly!");
  }).catch(() => {
    console.error("Failed sending the response.");
  });
  return new Response("Hello world");
});

此外,当事务完成时,无论是由客户端关闭连接还是服务器发送响应,附加到 Request 参数的 AbortSignal 始终会被中止。

Deno.serve((req, info) => {
  req.signal.addEventListener("abort", () => {
    console.log("Response finished");
  });
  return new Response("Hello world");
});

更简单地访问服务器地址

我们添加了一个小的生活质量改进,使从 Deno.serve 获取服务器地址变得更容易。以前,您必须编写类似这样的代码

let listenPort: number | null = null;

Deno.serve(
  {
    onListen: ({ port }) => (listenPort = port),
  },
  () => new Response("hello world"),
);

通过直接在服务器实例上添加新的 addr 属性,这变得更加简单

const server = Deno.serve(() => new Response("hello world"));
const port = server.addr.port;

URL.parse() Web API

新的 Web API URL.parse() 在您需要解析 URL 时提供了更简单的控制流。

在此添加之前,解析方法是构造一个新的 URL 实例。关键是,如果您解析的 URL 无效,new URL(input, base) 会抛出错误;而 URL.parse(input, base) 对于无效的 URL 只会返回 null

因此,如果您正在解析 URL 并且需要在解析失败的情况下提供回退,则可以将此代码替换为

let url;
try {
  url = new URL(userProvidedValue, "http://deno.land");
} catch {
  url = new URL("http://deno.land");
}

替换为此代码

const url = URL.parse(userProvidedValue, "http://deno.land") ??
  new URL("https://deno.land");

感谢 Kenta Moriuchi 实现了此 API。

标准库正朝着稳定化迈进

Deno 标准库 (deno_std) 提供了一组高质量的软件包,这些软件包由核心团队审核并保证与 Deno 协同工作。

我们将在未来几天发布一篇关于此的完整博客文章,但从 Deno 1.43 开始,标准库将专门发布到 JSR,位于 @std 作用域下。标准库的现有版本将继续存在于 https://deno.land/std。此举与 Deno 新的工作区功能一起,是 Deno 2 中即将发生的变化的一部分。有关更多详细信息,请查看 标准库的稳定化路线图

rusty_v8 的 Android 构建

虽然我们自己不提供 Android 构建,但我们收到了一个很棒的补丁,使为 Android 构建 rusty_v8 变得更加容易。

感谢 @Taknok 的贡献。

V8 12.4

Deno 1.43 搭载了 V8 12.4,它增加了对新的 TypedArray 类型:Float16Array 的支持。

此 API 在 多个 GPU 相关应用中被证明非常有用。

使用 DENO_FUTURE=1 试用 Deno 2 功能

为了预见即将到来的 Deno 2 版本,我们将 Deno 2 的重大更改放在了 DENO_FUTURE=1 环境变量之后。启用它允许您测试您的项目与 Deno 2 的兼容性。

这包括以下内容

  • 使用 nodeModulesDir 设置覆盖 BYONM
  • 删除 Web Workers 中已弃用的 Deno.* API
  • 当存在 package.json 时,默认启用 BYONM
  • 删除 Deno.ConnectTlsOptions.certFile
  • 删除 Deno.ConnectTlsOptions.certChain
  • 删除 Deno.ConnectTlsOptions.privateKey
  • 删除 Deno.ListenTlsOptions.keyFile
  • 删除 Deno.ListenTlsOptions.certFile
  • 删除 Deno.customInspect
  • 删除 Deno.Conn.prototype.rid
  • 删除 Deno.TlsConn.prototype.rid
  • 删除 Deno.Listener.prototype.rid
  • 删除 Deno.TlsListener.prototype.rid
  • 删除 Deno.UnixConn.prototype.rid
  • 删除 Deno.FsWatcher.prototype.rid
  • 使 Deno.FsFile 构造函数非法

致谢

没有我们社区的帮助,我们就无法构建 Deno!无论是回答社区 Discord 服务器 中的问题,还是报告错误,我们都非常感谢您的支持。特别地,我们要感谢以下人员对 Deno 1.43 的贡献:Alex Yang、Carlos Precioso、JOTSR、Javier Viola、Kenta Moriuchi、MAKS11060、nokazn、welfuture、youngwendy、林炳权、chirsz-ever。

您想加入 Deno 贡献者的行列吗?请查看我们的贡献文档,我们下次在榜单上见!

信不信由你,上面列出的更改仍然没有告诉您 1.43 中改进的所有内容。您可以在 GitHub 上此处查看 Deno 1.43 中合并的完整 pull request 列表。

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

🍋 Fresh 2.0 即将到来。

我们的下一个 Fresh 主要版本将更加简单,并具有更可组合的路由 API。在此处阅读更多内容