Node.js的第 20 版于 2023 年 4 月 18 日发布。它解决了Deno和Bun已经"解决"的一些问题和批评,包括新的权限模型和稳定的本机测试运行器。本文探讨了使用世界上最常用的 JavaScript 运行时的开发人员可用的新选项。
Node.js 发布时间表 {#thenodejsreleaseschedule}
Node.js 有六个月的发布时间表:
-
4 月的偶数版本(14、16、18 等)稳定并获得三年的长期支持 (LTS) 更新。
-
10 月的奇数版本(15、17、19 等)更具实验性,更新通常在一年后结束。
通常,您应该选择偶数 LTS 版本,除非您需要实验版本中的特定功能并打算稍后升级。也就是说,Node.js 20 是新的,网站建议您继续使用版本 18,同时开发团队会修复所有最新问题。
Node.js 20 具有以下新特性......
新权限模型 {#newpermissionmodel}
跑步node somescript.js
并非没有风险。脚本可以做任何事情:删除基本文件、将私人数据发送到服务器或在子进程中运行加密货币挖掘程序。很难保证您自己的代码不会破坏某些东西:您能确定所有模块及其依赖项都是安全的吗?
新的(实验性的)Node.js权限模型限制了脚本可以做什么。要使用它,请在--experimental-permission
您的node
命令行中添加标志,然后:
-
--allow-fs-read
授予对文件的读取访问权限。您可以将读取权限限制为:-
具体目录:
--allow-fs-read=/tmp/
-
具体文件:
--allow-fs-read=/home/me/data.json
-
或通配符文件模式:
--allow-fs-read=/home/me/*.json
-
-
--allow-fs-write
授予对具有相同目录、文件或通配符模式的文件的写访问权。 -
--allow-child-process
允许子进程执行其他可能用其他语言编写的脚本。 -
--allow-worker
允许工作线程,它与主处理线程并行执行 Node.js 代码。
在下面的例子中,somescript.js
可以读取/home/me/data/
目录中的文件:
node --experimental-permission --allow-fs-read=/home/me/data/ somescript.js
任何写入文件、执行另一个进程或启动 web worker 的尝试都会引发错误ERR_ACCESS_DENIED
。
您可以使用新process.permission
对象检查应用程序中的权限。例如,检查脚本是否可以写入文件的方法如下:
process.permission.has('fs.write');
以下是检查脚本是否可以写入特定文件的方法:
if ( !process.permission.has('fs.write', '/home/me/mydata.json') ) {
console.error('Cannot write to file');}
JavaScript 权限管理首先由Deno引入,它提供对文件、环境变量、操作系统信息、时间测量、网络、动态加载库和子进程的访问的细粒度控制。默认情况下,Node.js 是不安全的,除非你添加标志--experimental-permission
。这不太有效,但可确保现有脚本无需修改即可继续运行。
本机测试运行器 {#nativetestrunner}
从历史上看,Node.js 一直是最小运行时,因此开发人员可以选择他们需要的工具和模块。运行代码测试需要第三方模块,例如Mocha、AVA或Jest。虽然这导致了很多选择,但可能很难做出最佳决定,并且切换工具可能并不容易。
其他运行时采取了另一种观点,并提供了被认为对开发必不可少的内置工具。Deno、Bun、Go和Rust都提供内置的测试运行器。开发人员有一个默认选择,但当他们的项目有特定要求时可以选择其他选择。
Node.js 18 引入了一个实验性的测试运行器,现在在版本 20 中已经稳定。无需安装第三方模块,您可以创建测试脚本:
-
在你的项目
/test/
目录中 -
通过命名文件
test.js
,test.mjs
, 或test.cjs
-
test-
在文件名的开头使用------例如test-mycode.js
-
test
在文件名末尾使用句点 (.
)、连字符 (-
) 或下划线 (_
) --- 例如mycode-test.js
,mycode_test.cjs
, 或mycode.test.mjs
然后您可以导入node:test
和node:assert
编写测试函数:
// test.mjs
import { test, mock } from 'node:test';
import assert from 'node:assert';
import fs from 'node:fs';
test('my first test', (t) => {
assert.strictEqual(1, 1);
});
test('my second test', (t) => {
assert.strictEqual(1, 2);
});
// asynchronous test with mocking
mock.method(fs, 'readFile', async () => 'Node.js test');
test('my third test', async (t) => {
assert.strictEqual( await fs.readFile('anyfile'), 'Node.js test' );
});
运行测试并node --test test.mjs
检查输出:
✔ my first test (0.9792ms)
✖ my second test (1.2304ms)
AssertionError: Expected values to be strictly equal:
1 !== 2
at TestContext.<anonymous> (test.mjs:10:10)
at Test.runInAsyncScope (node:async_hooks:203:9)
at Test.run (node:internal/test_runner/test:547:25)
at Test.processPendingSubtests (node:internal/test_runner/test:300:27)
at Test.postRun (node:internal/test_runner/test:637:19)
at Test.run (node:internal/test_runner/test:575:10)
at async startSubtest (node:internal/test_runner/harness:190:3) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: 1,
expected: 2,
operator: 'strictEqual'
}
✔ my third test (0.1882ms)
ℹ tests 3
ℹ pass 2
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 72.6767
您可以添加一个--watch
标志以在文件更改时自动重新运行测试:
node --test --watch test.mjs
您还可以运行项目中找到的所有测试:
node --test
本机测试是对 Node.js 运行时的一个受欢迎的补充。不再需要学习不同的第三方 API,我再也没有忘记为较小的项目添加测试的借口了!
编译单个可执行应用程序 {#compilingasingleexecutableapplication}
Node.js 项目需要运行时才能执行。在将应用程序分发给无法轻松安装或维护 Node.js 的平台或用户时,这可能是一个障碍。
版本 20 提供了一项实验性功能,允许您创建一个可以在没有依赖项的情况下部署的单一可执行应用程序 (SEA) 。该手册解释了该过程,尽管它有点令人费解:
您必须有一个带有单一入口脚本的项目。它必须使用 CommonJS 而不是 ES 模块。
创建一个 JSON 配置文件,用于将脚本构建为在运行时内运行的 blob。例如sea-config.json
:
{
"main": "myscript.js",
"output": "sea-prep.blob"
}
生成 blob node --experimental-sea-config sea-config.json
。
根据您的操作系统,您必须随后复制node
可执行文件、删除二进制文件的签名、将 blob 注入二进制文件、重新签名并测试生成的应用程序。
虽然它有效,但您仅限于较旧的 CommonJS 项目,并且只能针对您正在使用的相同操作系统。鉴于卓越的Deno 编译器可以通过来自 JavaScript 或 TypeScript 源文件的单个命令为任何平台创建可执行文件,它肯定会有所改进。
您还应该了解生成的可执行文件的文件大小。单个console.log('Hello World');
生成一个 85MB 的文件,因为 Node.js(和 Deno)需要附加整个 V8 JavaScript 引擎和标准库。正在考虑减小文件大小的选项,但不太可能低于 25MB。
编译对于小型命令行工具不实用,但对于大型项目(如完整的 Web 服务器应用程序)来说,它是一个更可行的选择。
更新的 V8 JavaScript 引擎 {#updatedv8javascriptengine}
Node.js 20 包含最新版本的V8 引擎,其中包含以下 JavaScript 功能:
-
String.prototype.isWellFormed()
true
:当字符串格式正确且不包含单独(不成对的)代理字符时返回。 -
String.prototype.toWellFormed():返回一个格式正确的字符串,修复了单独的代理字符问题。
-
一个新的正则表达式
v
标志,它解决了Unicode 字符的大小写问题。
杂项更新 {#miscellaneousupdates}
还提供以下更新和改进:
-
对URL、本机fetch()和EventTarget API 的性能改进
-
ES 模块加载改进,包括对import.meta.resolve()的实验性支持,它可以将模块的文件路径引用范围限定为 URL 字符串
-
Web Crypto API互操作性改进
-
WebAssembly 系统接口 (WASI)的进一步进展,它授予沙盒 WASM 应用程序访问操作系统的权限
-
Windows 上对 ARM64 的官方支持
概括 {#summary}
Node.js 20 是向前迈出的重要一步。这是一个更重要的版本,并实现了 Deno 的一些更好的功能。
德诺很棒。它很稳定,原生支持 TypeScript,缩短了开发时间,需要的工具更少,并且会定期更新。不利的一面是,它出现的时间更少,模块更少,而且它们通常是对 Node.js 库的较浅模仿。
Deno 和 Bun 值得考虑用于新项目,但现有的 Node.js 应用程序有数千个。Deno 和 Bun 使转换代码变得更加容易,但离开 Node.js 并不总是有明显的优势。
好消息是我们有一个蓬勃发展的 JavaScript 生态系统。运行时团队相互学习,快速发展使开发人员受益。