大家好,这里是大家的林语冰。
Deno 团队十分鸡冻地官宣 Deno 1.4 正式发布,这是 Deno 发展的一个里程碑。Deno 1.4 包含增强 Deno 体验的功能,引入了用于高级日期和时间操作的强大 Temporal API,并采用最新的装饰器语法来实现更具表现力的代码。除了这些进步之外,我们还实施了一系列弃用、稳定和删除,旨在简化 Deno 的功能,并为 Deno 2 未雨绸缪。
以下是 Deno 1.4 新增功能的概述:
Temporal
APIimport.meta.filename/.dirname
- 装饰器
deno.json
中更精简的imports
- 弃用、稳定和移除的功能
- Web API:
rejectionhandled
事件 - WebGPU 窗口/"自带窗口"
- Node 相关 API 更新
- LSP 改进
- 颜值更高的调试
deno lint
更新- 改变我们处理不稳定功能的方式
免责声明
本文属于是语冰的直男翻译了属于是,略有删改,仅供粉丝参考。英文原味版请传送 Deno 1.40: Temporal API。
Temporal
API
Temporal
API 旨在解决与 JS 中现有 Date
对象相关的某些缺陷和复杂性。
Temporal
提案已被所有主流 JS 引擎积极实现,我们十分鸡冻地宣布 Temporal
现在可以在 Deno 中通过 --unstable-temporal
标志使用。
js
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)
// 7
Temporal
API 不太可能发生更改,我们的目标是在 Deno 2 中稳定它。我们鼓励大家深度学习 Temporal
API 的文档。
import.meta.filename/.dirname
Deno 现在支持 import.meta.filename
和 import.meta.dirname
属性。
这些属性映射了 CJS 模块系统中的 __filename
和 __dirname
属性:
import.meta.filename
提供当前模块文件的绝对路径import.meta.dirname
提供包含当前模块文件的目录的绝对路径
这两个属性都知道操作系统特定的路径分隔符,并为当前平台提供正确的分隔符:
js
console.log(import.meta.filename)
console.log(import.meta.dirname)
在 Unix 中:
bash
deno run /dev/my_module.ts
/dev/my_module.ts
/dev/
在 Windows 中:
bash
$ deno run C:\dev\my_module.ts
C:\dev\my_module.ts
C:\dev\
这些属性能且仅能用于本地模块,即从文件系统加载的模块,并且用于远程模块会得到 undefined
,比如从 http://
或 https://
导入的模块。
装饰器
Deno 现在支持 TC39 第 3 阶段装饰器提案,该提案很快将在所有浏览器中实现。
装饰器是一种扩展 JS 类的提案,在转译器环境中被开发者广泛采用,对标准化有着广泛的兴趣。五年多来,TC39 一直在迭代装饰器提案。本文档基于所有过去提案的元素描述了装饰器的新提案。
装饰器功能在 .ts/.jsx/.tsx
文件中可用。对纯 JS 的支持正在等待 V8 中的实现。
下面是 @trace
装饰器的示例,每当调用函数以及返回时,该装饰器都会注销:
ts
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()
如果我们依赖旧的"实验性 TS 装饰器",我们仍然可以通过以下配置使用它们:
json
{
"compilerOptions": {
"experimentalDecorators": true
}
}
粉丝请注意 :Deno 1.4 被砍掉后发现了一个 bug,TS 的 experimentalDecorators
在 LSP 中仍然打开。请及时升级到 Deno 1.40.1。
deno.json
中更精简的 imports
deno.json
中的 imports
字段现在支持更精简的语法,从而指定具有子路径导出的依赖。以前,当想要使用 npm
中的 preact
时,我们必须将其添加到 deno.json
中的 imports
对象中:
json
{
"imports": {
"preact": "npm:preact@10.5.13",
"preact/": "npm:/preact@10.5.13/"
}
}
这允许我们导入 preact
和子路径导出,比如 preact/hooks
。
在此版本中,我们对此进行了简化,以便我们现在可以执行以下操作:
json
{
"imports": {
"preact": "npm:preact@10.5.13"
}
}
在 Deno 1.4 中,这允许我们从 npm
导入 preact
和 preact/hooks
。
此前,Deno 认为 deno.json
中的 imports
字段只是常规导入映射。现在,我们预处理 deno.json
中的 imports
字段,并将右侧带有 npm:
前缀的所有条目扩展为内部导入映射中我们用于解析的两个条目。
弃用、稳定和删除的功能
当我们为 Deno 2 未雨绸缪时,我们致力于改进运行时,同时确保从 Deno 1 的平稳过渡。虽然大多数 Deno 1 代码将保持兼容,但我们正在简化某些 API,保证平台的长期健康。
弃用功能
我们引入了弃用功能来逐步淘汰旧版 API,并提供警告来辅助大家进行迁移:
window
:全局变量window
在 JS 生态系统中经常使用,用于测试代码是否在浏览器中运行。使用globalThis
或self
代替是一个简单粗暴的修复方案。目前还没有针对此弃用的运行时警告,但在启动此版本时它会显示在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.iter()
和Deno.iterSync()
Deno.copy()
Deno.readAll()
和Deno.readAllSync()
Deno.writeAll()
和Deno.writeAllSync()
Deno.Buffer
Deno.FsWatcher.return()
:为了与其他异步迭代器保持一致,我们不赞成使用显式关闭方法。Deno.customInspect
:为了鼓励使用与浏览器兼容的代码,我们已切换到Symbol.for("Deno.customInspect")
。
在 Deno 2 中,我们删除了"资源 id"(resource ids)的概念。资源 ID 是对套接字、文件或 JS 外部管理的其他资源的整数引用。它们类似于文件描述符。然而大多数用户不会直接接触这些,我们想开始引入原生 JS 对象的资源引用。因此,我们将弃用这些 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()
:这些函数现在在Deno.FsFile
上可用,比如truncate()
和truncateSync()
。
所有 rid
属性现已弃用,并将在 Deno 2 中删除:
Deno.Conn.rid
Deno.FsWatcher.rid
Deno.TcpConn.rid
Deno.TlsConn.rid
Deno.UnixConn.rid
Deno.Listener.rid
现在已弃用从文件加载证书,推荐阅读:
Deno.ListenTlsOptions.certFile
:请使用Deno.ListenTlsOptions.cert
和Deno.readTextFile
代替。Deno.ListenTlsOptions.keyFile
:请使用Deno.ListenTlsOptions.key
和Deno.readTextFile
代替。Deno.ConnectTlsOptions.certFile
:请使用Deno.ConnectTlsOptions.cert
和Deno.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()
移除
最后,不稳定的 Deno.upgradeHttp
API 已被删除。这个 API 容易出错并且容易被误用。我们鼓励每个人使用 Deno.serve()
和 Deno.upgradeWebsocket()
。
Web API:rejectionhandled
事件
我们添加了对 rejectionhandled
事件的支持,每当 .catch()
处理程序附加到已被拒绝的 Promise
时就会触发该事件。此外,当且仅当我们有一个调用 event.preventDefault()
的 unhandledrejection
事件侦听器时,才会发出此事件,否则 Promise
会被拒绝,并且进程将因错误而退出。
js
globalThis.addEventListener('unhandledrejection', event => {
// 调用 preventDefault 防止进程退出。
event.preventDefault()
console.log('unhandledrejection', event.reason)
})
globalThis.addEventListener('rejectionhandled', event => {
console.log('Promise 实例被拒绝之后会被添加一个 .catch 句柄', event.reason)
})
// 创建一个被拒绝的 Promise
const a = Promise.reject(new Error('boom!'))
// 定时器之后附加一个 .catch 句柄
setTimeout(async () => {
a.catch(() => console.log('将 catch 句柄添加给 Promise 实例'))
}, 10)
WebGPU 窗口/"自带窗口"
我们引入了一个新的不稳定的 Deno.UnsafeWindowSurface
API 来解决 Deno 中的窗口问题。我们的目标是为 WebGPU 提供窗口解决方案,而无需链接到 X11 等本机窗口系统。
这是一个低级 API,可供 FFI 窗口库使用,比如 sdl2
、 glfw
、raylib
等来创建使用本机窗口和显示句柄的 WebGPU 表面。
这是使用 deno.land/x/sdl2
的示例:
js
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()
/* 返回一个 Deno.UnsafeWindowSurface */
const surface = win.windowSurface()
/* 返回一个 a WebGPU GPUCanvasContext */
const context = surface.getContext('webgpu')
context.configure({
/* ... */
})
for (const event of win.events()) {
if (event.type == EventType.Quit) break
// 正弦波
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
运行。
Node 相关 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 API 中的若干错误:
- Windows 上的
child_process.ChildProcess.send()
crypto.createDeciperiv()
现在支持aes-192-ecb
和aes-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 改进
自 Deno 1.39 以来,我们加强了与 TS 语言服务 API 的嵌入式实例的集成,实现了显著的性能提升和某些错误修复。由于更智能的项目状态同步和缓存,Rust 和 TS 隔离之间的数据交换更加高效且频率更低。
jsxImportSource
编译器选项的用户的生活质量提高:保存包含的 deno.json
文件时,会自动缓存指定的远程资源。这是必要的,因为与未缓存的导入不同,由于缺少资源而产生的诊断是模糊的,并且没有指出问题的原因,无论是从用户的角度还是从语言服务器的快速修复生成器的角度。
自动导入完成会更加一致。由于 TS 隔离中更好的状态保存,某些完成解析会默默出错的情况已得到修复。带有子路径的导入映射 NPM 说明符已正确替换为预期的别名。
颜值更高的调试
deno lint
和 deno doc
有一个新的调试打印机。我们将在即将发布的版本中将此扩展到其他子命令。
deno lint
更新
deno lint
中提供了三个新规则:
no-console
no-self-compare
no-window
默认情况下启用 no-window
规则,而其他两个规则是可选的,我们需要在配置文件中启动它们:
json
{
"lint": {
"rules": ["no-console", "no-self-compare"]
}
}
保持代码质量对于项目的成功至关重要,我们都发现自己处于需要抑制 linter 警告的情况。
在大多数情况下,我们只是无视警告并继续前进,但这种方法无助于团队成员或未来的自己理解为什么特定警告被抑制。
deno lint
现在支持 // deno-lint-ignore
和 // deno-lint-ignore-file
指令中的附加说明:
js
// deno-lint-ignore-file :此文件自动生成,无需 lint
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...
改变我们处理不稳定功能的方式
我们正在改进管理不稳定功能的方法。--unstable
标志虽然有用,但有些不精确,会同时激活所有不稳定的功能。在 Deno 1.38 中,我们引入了更细粒度的标志,这样我们可以更好地控制特定的不稳定功能,比如 --unstable-webgpu
启动新的 WebGPU API。在此基础上,Deno 1.4 标志着广泛的 --unstable
标志弃用阶段的开始,为 Deno 2 中删除它奠定了基础。
此外,我们还改进了 deno check
和 LSP 中不稳定 API 的类型检查。现在,在类型检查期间会自动包含稳定和不稳定 API 的类型定义。这样就无需指定 --unstable
来访问不稳定的 API 类型定义。但是,请记住在运行程序时启用特定的不稳定功能标志。无视这些标志仍然会导致错误,请确保自己了解正在使用的不稳定功能。
这一变化简化了开发,为 Deno 功能集的使用提供了更多的清晰度和控制力。
本期话题是 ------ 你了解 JS 最新的 Temporal
API 吗?
欢迎在本文下方自由言论,文明共享。谢谢大家的点赞,掰掰~
《前端猫猫教》每日 9 点半更新,坚持阅读,自律打卡,每天一次,进步一点。