Deno 1.33:Deno 2 即将到来
正如最近在 Node Congress 2023 上“强制优化”演示中提到的那样,我们正在努力争取在未来几个月内发布 Deno 2 的一个重要版本。尽管我们对 Deno 2 的愿景雄心勃勃,但自项目启动以来,我们的目标从未改变。
轻松编码:无论是删除配置、样板代码还是构建步骤,我们都致力于让您轻松投入代码并立即提高生产力。此版本使我们的 LSP 更加健壮,支持 LSP 的任何代码编辑器都能与 Deno 项目完美协作。
一流的性能:速度和效率对开发者和用户都很重要。此版本提高了 HTTP 和 WebSocket 服务器的性能,并为进一步的性能工作奠定了基础。
毫不妥协的安全性。Deno 内置了安全功能,采用选择性加入的权限模型,因此您始终知道您的代码可以访问什么。在未来几个月,我们将为 Deno 的权限系统引入新功能,使其更易于使用且更灵活。
Deno 1.33 是向这些理想迈进的又一步。此版本带来了:
- 内置 KV 数据库
- 更扁平化的
deno.json
配置 - 动态导入的权限检查更少
- npm 和 Node 兼容性改进
- 性能改进
- CLI 改进
- LSP 文档预加载
Deno
API 变更- 标准库变更
- V8 11.4
随着 Deno 2 的临近,接下来的次要版本将专注于提高性能、创造一流的开发者体验、增强安全性以及更强大的 Node/npm 兼容性。
内置 KV 数据库
Deno KV 是 Deno 中无缝集成的数据库。无需安装任何依赖项,您可以立即开始构建应用程序。此外,当您部署到 Deno Deploy 时,您的数据将由一个一致的、地理复制的全球数据库提供支持。请记住,KV 目前是一个不稳定的 API,因此您需要使用 --unstable
标志才能使用它。
立即开始使用 KV,无需任何设置
const kv = await Deno.openKv();
const key = ["users", crypto.randomUUID()];
const value = { name: "Alice" };
await kv.set(key, value);
const result = await kv.get(key);
result.value; // { name: "Alice" }
当然,数据在磁盘上是持久且可维护的,即使在程序重新启动后也能确保可靠的存储和访问。
有关详细文档,请查阅手册。
deno.json
配置
更扁平化的 deno.json
模式已扁平化,使其更易于阅读和写入。
嵌套选项,例如 "lint.files.exclude"
或 "fmt.options.lineWidth"
,现在可在其各自部分的顶层使用。
以前这样写:
{
"lint": {
"files": {
"exclude": ["gen.ts"]
}
},
"fmt": {
"options": {
"lineWidth": 80
}
}
}
现在您可以这样写,效果相同:
{
"lint": {
"exclude": ["gen.ts"]
},
"fmt": {
"lineWidth": 80
}
}
所有更改
之前 | 之后 |
---|---|
bench.files.include |
bench.include |
bench.files.exclude |
bench.exclude |
fmt.files.include |
fmt.include |
fmt.files.exclude |
fmt.exclude |
fmt.options.useTabs |
fmt.useTabs |
fmt.options.lineWidth |
fmt.lineWidth |
fmt.options.indentWidth |
fmt.indentWidth |
fmt.options.singleQuote |
fmt.singleQuote |
fmt.options.proseWrap |
fmt.proseWrap |
fmt.options.semiColons |
fmt.semiColons |
lint.files.include |
lint.include |
lint.files.exclude |
lint.exclude |
test.files.include |
test.include |
test.files.exclude |
test.exclude |
此更改向后兼容,但我们倾向于在未来弃用旧模式。
感谢 @scarf005 实现了此更改。
动态导入的权限检查更少
此版本在使用动态导入时带来了巨大的用户体验改进。
如果您在 import()
调用中使用字符串字面量(例如 import("https://deno.land/std/version.ts")
),Deno 将不再需要权限来执行此导入。我们长期以来一直能够下载和分析这类导入,经过讨论,我们决定最好不要要求权限来执行此代码——它已经是您程序“模块图”的一部分,并显示在 deno info main.ts
的输出中。
此更改将使在某些情况下有条件地执行代码变得更容易——例如,如果您有一个包含许多子命令的 CLI 工具,您可能希望仅在调用子命令时有条件地加载其相应的处理程序。这大大缩短了您工具的启动时间。另一个例子是仅在需要时加载 polyfill,或者仅在存在环境变量时在服务器应用程序中执行调试代码。
请记住,对于无法静态分析的动态导入(即,未将字符串字面量用于说明符),仍将检查权限。
import("" + "https://deno.land/std/version.ts");
import(`https://deno.land/std@${STD_VERSION}/version.ts`);
const someVariable = "./my_mod.ts";
import(someVariable);
感谢 Nayeem Rahman 实现了此更改。
npm 和 Node 兼容性改进
自上次发布以来,node:crypto
、node:http
和 node:vm
模块已得到极大改进。我们为大多数 node:crypto
API 提供了 polyfill,这解除了许多流行 npm 包(包括云提供商 SDK)的阻塞。对 node:http
和 node:vm
的更改对 Vite 用户特别有帮助,Vite 现在与 Deno 一起使用时应该更加稳定和高性能。
此版本中实现的所有 API 的完整列表:
crypto.checkPrime
crypto.checkPrimeSync
crypto.createSecretKey
crypto.createVerify
crypto.ECDH
crypto.generateKey
crypto.generateKeyPair
crypto.generateKeyPairSync
crypto.generateKeySync
crypto.generatePrime
crypto.generatePrimeSync
crypto.getCurves
crypto.hkdf
crypto.hkdfSync
crypto.sign
crypto.Sign
crypto.verify
crypto.Verify
crypto.X509Certificate
http.ClientRequest.setTimeout
http.IncomingMessage.socket
module.Module._preloadModules
vm.runInThisContext
在 npm 支持方面,我们大大改进了 npm 包的缓存处理。从这个版本开始,Deno 在缓存中遇到缺失版本(或版本不匹配)的包时,将尽力从注册表检索信息。这应该会大大减少建议使用 --reload
标志来检索最新注册表信息的提示消息。
性能改进
在此版本中,我们彻底改进了 HTTP 服务器以及 WebSocket 客户端和服务器的实现。在过去几个月里,我们收到了关于 Deno.serve
和 WebSocket
API 性能和可靠性不如预期的反馈。
我们认真对待这些反馈,并正在不懈努力改进它们。尽管 RPS 基准测试中的变化可能尚未在您的应用程序中显现,但我们已采取根本性措施来提高这些 API 的整体性能,我们将在未来几个月内进行展示。
CLI 改进
deno bench
- --no-run
标志
此版本为 deno bench
子命令添加了一个新的 --no-run
标志,用于缓存所有解析的基准测试文件而不运行它们。这使 deno bench
与 deno test
对齐。
> deno bench --no-run
Download https://deno.land/std@0.185.0/path/mod.ts
Download ...etc...
Check file:///home/user/project/main_bench.ts
感谢 Geert-Jan Zwiers 贡献了此功能。
deno task
- unset
命令
跨平台的 unset
命令已添加到 deno task
中的 shell,允许删除环境变量和 shell 变量。这与 POSIX unset
命令的功能相同,并且现在执行 MY_VAR=
可以正确地将变量设置为空。
{
"tasks": {
// `deno task example` outputs "1" then "false"
"example": "export VAR=1 && echo $VAR && deno task next",
"next": "unset VAR && deno eval 'console.log(Deno.env.has(\"VAR\"))'"
}
}
LSP 文档预加载
在 Deno 的早期版本中,您可能会发现某些功能除非您之前打开过文件,否则无法正常工作。例如,在某些代码上执行“查找引用”可能不会显示它在测试文件中被使用,除非该测试文件之前已被打开。现在,通过在初始化语言服务器时预加载文件,此问题已得到缓解,这将带来更好的体验。
请注意,作为此预加载的一部分,语言服务器将最多遍历 1000 个文件系统条目,并在达到此限制时输出一条日志消息。如果这成为问题并且工作区包含大量与 Deno 无关的文件,您可能希望利用 "deno.enablePaths"
选项来部分启用工作区。
Deno
API 更改
我们正在弃用 Deno.run
API。随着 v1.31 中 Deno.Command
API 的稳定,它是我们推荐的生成子进程的方式。Deno.run
存在一些难以解决的怪癖,我们将大部分精力集中在设计一个更易于使用的更好 API 上。Deno.run
将在 v2.0 中移除,因此我们强烈建议您将代码迁移到 Deno.Command
。
不稳定的 Deno.serve
API 引入了一项重大更改,移除了其中一个 API 重载,为该 API 的稳定做准备。Deno.serve(handler: Deno.ServeHandler, options: Deno.ServeOptions)
重载已被移除,不再在 v1.33 中可用。
请更新您的代码以使用其中一个可用的重载。
Deno.serve((_req) => new Response("Hello, world"));
Deno.serve({ port: 3000 }, (_req) => new Response("Hello, world"));
Deno.serve({
onListen({ port, hostname }) {
console.log(`Server started at http://${hostname}:${port}`);
// ... more info specific to your server ..
},
handler: (_req) => new Response("Hello, world"),
});
我们计划在下个月稳定 Deno.serve
,它将成为比 Deno.serveHttp
更推荐使用的 API。
标准库更改
std/encoding
模块的重大更改
正如上一篇发布文章中宣布的那样,以下 6 个编码模块已移至顶层:
std/encoding/csv
已移至std/csv
std/encoding/yaml
已移至std/yaml
std/encoding/toml
已移至std/toml
std/encoding/json
已移至std/json
std/encoding/jsonc
已移至std/jsonc
std/encoding/front_matter
已移至std/front_matter
在此版本中,这些已弃用的模块已完全移除。如果您尚未迁移到新路径,请更新您的导入说明符。
注意:如果您不直接依赖这些已移除的路径,但您的传递依赖项通过非版本化模块 URL 错误地依赖这些路径,您可能无法通过更新自己的源代码来修复此问题。在这种情况下,请使用以下模式的导入映射:
{
"imports": {
"https://deno.land/std/encoding/yaml.ts": "https://deno.land/std@0.179.0/encoding/yaml.ts"
}
}
这将强制将整个依赖图中的非版本化标准库 URL(在本例中为 https://deno.land/std/encoding/yaml.ts
)重新映射到版本化 URL(https://deno.land/std@0.179.0/encoding/yaml.ts
),从而修复您的依赖项中的问题。
fs.exists
已取消弃用
std/fs
模块中的 exists
和 existsSync
曾一度在 v0.111.0
中被弃用,但在此版本中已取消弃用。
这些 API 因其易错性而被弃用。它们很容易导致竞态条件错误,称为 TOCTOU。然而,在它们被弃用后,关于其正确/合理用法进行了长时间的讨论。在重新考虑了它们的便利性和易错性之间的平衡后,决定恢复它们。
std/csv
模块更新
在此版本中,添加了 CsvStringifyStream
API。此 API 将源流式输入转换为 CSV 行流。
import { CsvStringifyStream } from "https://deno.land/std@0.185.0/csv/mod.ts";
import { readableStreamFromIterable } from "https://deno.land/std@0.185.0/streams/mod.ts";
const file = await Deno.open("data.csv", { create: true, write: true });
const readable = readableStreamFromIterable([
{ id: 1, name: "one" },
{ id: 2, name: "two" },
{ id: 3, name: "three" },
]);
await readable
.pipeThrough(new CsvStringifyStream({ columns: ["id", "name"] }))
.pipeThrough(new TextEncoderStream())
.pipeTo(file.writable);
继上述添加之后,CsvStream
已重命名为 CsvParseStream
以明确其用途。
感谢 Yuki Tanaka 的贡献。
V8 11.4
此版本升级到最新的 V8 版本 (11.4,之前是 11.2)。
此次升级带来了一些令人兴奋的新 JavaScript 功能: