首页 / 文章

Node.js 26中悄然取代多年临时解决方案的新功能。

Node.js 26带来了多项新功能,取代了旧的解决方案:用于处理日期的Temporal API、原生TypeScript执行环境、内置的缓存辅助工具,以及一些其他小改进。

发布者:REACTAPP.TOP阅读约 8 分钟1739 词

来源: You’re Still Writing Node.js Like It’s 2020

1. 我们终于可以不再假装 Date 是一个真正的日期 API 了

以下是一些非常普通的 JavaScript 代码:

const date = new Date("2026-09-08");
console.log(date);

现在试着回答一个稍难一些的问题:

在纽约召开的会议的本地日期是什么?

或者:

在这个账单日期的基础上加上三个月。

又或者:

日历日期与精确时间点之间到底有什么区别?

就在这时,Date 开始暴露出问题。

这正是 Temporal 如此重要的原因。

Node.js 26 默认启用了 Temporal API,终于为 JavaScript 提供了用于表示瞬间、日历日期、持续时间以及时区的独立概念。

不必再这样编写:

const date = new Date();

现在你可以这样写:

const today = Temporal.Now.plainDateISO();

const invoiceDate =
  Temporal.PlainDate.from("2026-09-08");
const dueDate =
  invoiceDate.add({ days: 30 });

而当你确实需要一个精确的时间点而非日历日期时:

const now = Temporal.Now.instant();

这种区别的重要性远超表面所见。

计费系统其实并不需要“JavaScript Date对象”。

它需要的是日历日期。

分布式事件日志也不需要“日历日期”,它需要的是时间点。

Temporal强制要求你明确指出自己究竟指的是哪一种时间值。

对于任何涉及支付、订阅、预订、市场或跨时区用户的场景,这不仅仅是一种更美观的语法——它还能避免一类错误,即使用错误类型的时间值。

2. 你的`.ts`文件不一定需要构建步骤

多年来,在Node环境中运行TypeScript一直遵循这样的流程:

app.ts
   ↓
TypeScript
   ↓
app.js
   ↓
Node

那种工作流程至今仍然完全有效。

不过现在 Node 可以直接运行 TypeScript 文件,只要这些文件使用的是可删除的语法即可。

以这个文件为例:

interface User {
  id: number;
  name: string;
}

function greet(user: User) {
  return `Hello ${user.name}`;
}
console.log(
  greet({
    id: 1,
    name: "Priyanshu"
  })
);

你可以直接用以下方式执行它:

node app.ts

Node 会直接去掉类型注解,然后运行剩下的纯 JavaScript 代码。

需要再次强调的一点是:Node 并不会为你进行任何类型检查。

因此对于大型生产级代码库,你仍然需要在工作流程中使用 tsc 或类似的类型检查工具。

但考虑一下较小的场景:内部脚本、简单的命令行工具或单文件实用程序。

以前的处理方式是这样的:

script.ts
   ↓
configure compiler
   ↓
compile
   ↓
run

现在则可以变成这样:

script.ts
   ↓
node

这只是一个小变化而已。

但这又是 Node 在默默移除许多项目原本就并不真正需要的那一层功能的一个例子。

3. 您的缓存代码看起来过于复杂了

您几乎肯定已经多次编写过这种模式:

let user = cache.get(userId);

if (user === undefined) {
  user = await fetchUser(userId);
  cache.set(userId, user);
}

这段代码本身没有问题。

但实际意图被检查、获取和存储等操作所掩盖。

您真正想表达的是:

把这个值给我。如果它还不存在,就去创建它。

现代 JavaScript 开始允许您直接这样表达。

作为 Node.js 26 组件发布的 V8 14.6 添加了以下功能:

Map.prototype.getOrInsert()
Map.prototype.getOrInsertComputed()

借助这些功能,同步缓存查询可以简化为类似这样的形式:

const value = cache.getOrInsert(
  key,
  createValue()
);

而当该值只需在需要时才延迟计算时:

const value = cache.getOrInsertComputed(
  key,
  () => createValue()
);

这些功能单独来看都算不上什么突破性创新。

但语言设计很少通过突破性的功能来进步。

只有当足够多的开发者反复编写相同的五行代码时,语言才会最终做出回应:

“好吧。你们不再需要编写那五行代码了。”

这正是那种能悄然融入日常代码的改进。

一年后,几乎没人会再谈论它。

人们会直接使用它而不会多想。

4. Node.js 现在可以深入到 JavaScript 之下

接下来的这项功能意义更为重大。

Node.js 26 引入了一个实验性的外部函数接口:

node:ffi

FFI 允许 JavaScript 调用原生库直接提供的函数。

其大致结构如下:

JavaScript
     ↓
node:ffi
     ↓
Native library
     ↓
C / C++ / system APIs

想象一个提供类似以下功能的原生库:

double calculate(double value);

至少从理论上讲,JavaScript现在可以加载该库并直接调用其中的函数。

这为以下应用打开了可能性:

  • 专业的数值计算
  • 重用现有的原生库
  • 更深入的系统集成
  • 原生级别的加密技术
  • 图像与视频处理

但这也存在一个严重的隐患。

你正在跨越托管JavaScript与原始原生内存之间的界限。

如果函数签名出错,不会得到友好的JavaScript异常提示。

这可能会导致整个进程崩溃。

正因如此,该API仍处于实验阶段,并且需要通过特定标志才能使用。

所以请不要将此内容理解为:

“是时候用C语言重写所有npm包了。”

其实可以这样理解:

“Node现在提供了更直接的途径来操作原生环境。”

这是两种截然不同的说法。

5. 你的Node CLI可能无需安装Node

还有另一个值得质疑的假设:

如果你用Node开发工具,那么用户也必须安装Node。

单可执行文件应用则颠覆了这一假设。

Node的SEA工具允许你将应用打包为独立的二进制文件。

因此,与其分发:

npm install
node index.js

你可以分发更接近以下形式的内容:

./my-cli

以团队日常使用的内部工具为例。

它可能是一个部署CLI:

deploy production

或者一个数据库辅助工具:

db-migrate

或者使用项目脚手架工具:

project init

如果你提供的只是一个可执行文件,用户无需知道也不必关心其背后运行的是 JavaScript。

这会悄然改变 Node 的自我定位。

Node 最初的定位是:

在浏览器之外运行的 JavaScript。

现在它越来越像:

一个恰好能运行 JavaScript 的通用应用程序运行时环境。

这显然是一个更宏大的宣称。

6. 如果你的应用不再需要所有权限怎么办?

安全模型通常遵循以下模式:

Application
     ↓
Gets permissions
     ↓
Keeps permissions
     ↓
Runs

但如果某个权限仅在启动时短暂需要呢?

以一种在初始化时会读取配置文件及某些证书的服务为例。

一旦程序开始运行,它可能就完全不再需要访问文件系统了。

Node 的权限模型正朝着允许应用程序在运行时通过诸如 permission.drop() 这样的 API 主动缩小自身功能的方向发展。

其工作流程大致如下:

Start
  ↓
Read configuration
  ↓
Initialize
  ↓
Drop unnecessary permissions
  ↓
Run

这体现了一个有用的安全原则:

不要只询问应用程序被允许做什么,而要问它未来实际上还需要哪些权限。

API 接口的变动其实很小。

但人们对应用程序安全的思维方式却在发生重大变化。

7. Node.js 终于愿意删除某些功能了

添加 API 是让运行时看起来更出色的简单方法。

而删除它们才是困难的部分。

Node.js 26 不断移除那些存在已久的旧 API 和过时的内部机制。

这比看上去要重要得多。

每一个旧API迟早都会成为他人无法避免的依赖。

如果不加以管控,运行时最终会背负数十年来积累的种种历史包袱:

old API
   +
new API
   +
replacement API
   +
deprecated API
   +
"don't use this anymore" API

一个成熟的平台需要具备清理这些积压内容的决心。

Node.js正逐渐展现出这样的决心。

对于那些不想记住每项旧有任务处理方式的人来说,这是个好消息。

有趣之处并不在于某个单一功能

Temporal本身并无出众之处。

getOrInsert()也是如此。

不经过构建步骤直接运行也不例外。

将CLI打包为可执行文件同样不算什么特别之处。

但把它们放在一起看的话……

Node正在同时在抽象层面上不断上升:

Temporal
Better collections
TypeScript
Modern JavaScript

同时也向更低层次发展:

FFI
Native libraries
Permissions
Executable packaging

这才是真正的关键所在。

Node.js 不仅在执行 JavaScript 方面越来越出色,

它还逐渐成为支撑应用程序周边所有功能的更优秀的运行时环境。

这也就改变了在新项目开始时应提出的问题。

不必直接问:

“我需要哪个 npm 包来完成这个功能?”

如今更值得先问的是:

“Node.js 能不能直接实现这个功能?”

有时诚实的答案仍然是“不能”。

但越来越多的情况下,答案却是:

“其实……可以。”

这可以说是 Node.js 26 最令人惊叹的特点。

运行时环境已经向前发展了,

而我们的思维模式却尚未跟上。