Deno 中所有网络 API 的列表
您是否曾经想知道 Deno 与 Web 的兼容性如何?可能没有,但我今天想知道。为了回答这个问题,我正在撰写这篇博客文章:我将列出并解释在 Deno 中实现的每个 Web API。给自己倒杯饮料,因为要浏览完这篇文章需要一段时间。
不过,在我们深入研究之前,我想设定一些基本规则
- 我不包括任何 JS 语言特性。只包括 Web 平台特性。
- 我将包括一些仍然标记为
--unstable
的特性,但会说明它们尚未稳定。 - 我不包括任何不是具体 API 的特性。例如,JSON 模块是在 Deno 中实现的,但更像是一种抽象概念,而不是具体 API,因此不包含在此列表中。
目录
atob
和btoa
setTimeout
和clearTimeout
setInterval
和clearInterval
crypto
fetch
、Request
、Response
和Headers
Blob
和File
TextEncoder
和TextDecoder
FormData
performance
structuredClone
URL
和URLSearchParams
console
Worker
Event
和EventTarget
WebSocket
ReadableStream
和WritableStream
TransformStream
WebSocketStream
TextEncoderStream
和TextDecoderStream
CompressionStream
和DecompressionStream
URLPattern
alert
、confirm
和prompt
localStorage
和sessionStorage
navigator
- WebGPU
MessageChannel
BroadcastChannel
atob
和 btoa
这两个函数用于对 base64 字符串进行编码和解码。它们很古老—— Firefox 1 于 2004 年 11 月 9 日发布,当时就已经支持了。
atob("SGVsbG8gV29ybGQ="); // "Hello World"
btoa("Hello World"); // "SGVsbG8gV29ybGQ="
setTimeout
和 clearTimeout
setTimeout
是另一个非常古老的 Web 特性。它用于安排一个函数在经过一定时间后被调用。它返回一个数字 ID,可以使用该 ID 通过 clearTimeout
取消超时。
请注意,Deno 与浏览器一样实现了此 API:返回一个数字。Node.js 也实现了此 API,但有一个非标准行为,它返回一个对象而不是一个数字。
setTimeout(() => {
console.log("This prints after 1 second.");
}, 1000);
const timerId = setTimeout(() => {
console.log("This doesn't print at all.");
}, 500);
clearTimeout(timerId);
console.log("This prints immediately.");
setInterval
和 clearInterval
setInterval
和 clearInterval
与 setTimeout
和 clearTimeout
非常相似。区别在于 setInterval
每 X 毫秒调用回调函数一次,而不是只在 X 毫秒后调用一次。
与 setTimeout
相同的免责声明也适用于此处:Deno 与浏览器一样实现了此 API,而 Node 则具有一个非标准对象作为返回值。
crypto
Deno 完全实现了 Web 密码学 API。此 API 可用于执行各种低级和高级加密操作。例如,您可以
- 使用
crypto.randomUUID()
生成随机 UUID - 使用
crypto.getRandomValues()
生成一个填充有随机字节的Uint8Array
- 使用 RSA、ECDSA 和许多其他算法使用
crypto.subtle.sign()
和crypto.subtle.verify()
签署和验证数据。 - 使用
crypto.subtle.digest()
生成哈希值。 - 使用
crypto.subtle.encrypt()
和crypto.subtle.decrypt()
加密和解密数据。 - 使用
crypto.subtle.generateKey()
生成 RSA、ECDSA、HMAC 或 AES 密钥
Deno 对 Web 密码学 API 的实现是在几周前完成的,但这绝对是值得的。Deno 通过的 Web 平台测试 比 Chrome 和 Firefox 都多。只有 Safari 在这一点上略胜一筹
引擎 | 通过的测试百分比 |
---|---|
Safari | 99.8% |
Deno | 98.1% |
Chrome/Edge | 94.5% |
Firefox | 93.4% |
您可以在 wpt.fyi 上查看最新的数据。
const uuid = crypto.randomUUID();
const bytes = await crypto.getRandomValues(new Uint8Array(16));
const digest = await crypto.subtle.digest("SHA-256", bytes);
fetch
、Request
、Response
和 Headers
Deno 实现了可能最流行的现代 Web API:fetch
。它用于发出 HTTP 请求。使用方法非常简单:第一个参数是要请求的 URL,第二个参数是包含选项(如 method
、headers
或 body
)的可选对象。
Deno 中的 fetch
API 是在运行时本地实现的,由用 Rust 编写的速度极快的 hyper
HTTP 实现提供支持。它支持 HTTP/1.1 和 HTTP/2 服务器、全双工流、原生 gzip/deflate/brotli 解码,并且与 Web 平台高度兼容。
与我们所有其他 Web API 一样,我们的 fetch
实现使用与所有浏览器相同的 Web 平台测试 测试套件进行测试。这确保了 Deno 的实现与 Web 平台兼容。据我们所知,我们是目前唯一一个针对规范的 Web 平台测试对 fetch
进行测试的服务器端运行时。
Deno 还实现了围绕 fetch
的所有对象:Request
、Response
和 Headers
。这些对象分别代表 HTTP 请求、HTTP 响应和 HTTP 标头列表。由于 Deno 是一个以 Web 为中心的运行时,因此我们的 HTTP 服务器使用这些相同的对象来表示请求和响应。 这使得代理请求变得非常容易。
const resp = await fetch("https://whats-my-ip.deno.dev/");
const text = await resp.text();
console.log(text);
Blob
和 File
Blob
和 File
都表示二进制数据。它们与 Uint8Array
的最大区别在于它们可以将数据存储在内存或磁盘中,因此非常适合大型二进制 Blob。由于有可能将数据存储在磁盘中,因此 Blob
和 File
支持的数据只能异步访问。
可以使用 .arrayBuffer()
、.text()
和 .stream()
方法或 FileReader
API 检索数据。
const blob = new Blob(["Hello World"]);
const text = await blob.text();
console.log(text);
const file = new File(["Hello World"], "hello.txt");
console.log(file.name);
console.log(file.size);
const bytes = await file.arrayBuffer();
console.log(new Uint8Array(bytes));
TextEncoder
和 TextDecoder
有时您需要将字符串编码或解码为二进制表示形式,或从二进制表示形式解码为字符串。Deno 提供了 TextEncoder
和 TextDecoder
API 来实现这一点。它们可用于将字符串编码为 Uint8Array
,并将 Uint8Array
解码为字符串。所有在浏览器中可用的文本编码在 Deno 中也可用。
const encoder = new TextEncoder();
const bytes = encoder.encode("Hello World");
console.log(bytes);
const decoder = new TextDecoder();
const text = decoder.decode(bytes);
console.log(text);
FormData
在与 HTTP API 交互时,有时您需要将数据作为 multipart/form-data
发送。这可以使用 FormData
API 完成。它是一个 JavaScript 结构,让您可以轻松地将键值对或文件追加到表单中,以作为多部分数据发送。
由于 Deno 也使用来自 fetch
的相同 Request
和 Response
对象用于本机 HTTP 服务器,因此 FormData
可用于通过调用 await request.formData()
从传入请求中轻松解码多部分表单数据。很酷吧?
const formData = new FormData();
formData.append("name", "Deno");
formData.append("age", "3");
formData.append("file", new File(["Hello World"], "hello.txt"));
const resp = await fetch("https://httpbin.org/post", {
method: "POST",
body: formData,
});
performance
performance
允许精确的时间测量,例如使用 performance.now()
。它还可用于使用 performance.mark()
和 performance.measure()
直接测量某些操作所花费的时间。
const start = performance.now();
await fetch("https://httpbin.org/delay/1");
const end = performance.now();
console.log("Took", end - start, "milliseconds");
structuredClone
structuredClone
API 用于深度克隆对象。它是一个非常新的 API,但已经在所有主要浏览器中发布。
与浅克隆相比,深度克隆不仅复制最外层对象的形状,还复制所有嵌套对象和数组的形状。它还可以克隆具有循环引用的对象。
const obj = {
foo: "bar",
baz: {
qux: "quux",
},
};
const clone = structuredClone(obj);
console.log(obj === clone); // false
console.log(obj.baz === clone.baz); // false
obj.baz.qux = "quuz";
console.log(obj.baz.qux); // quuz
console.log(clone.baz.qux); // quux
URL
和 URLSearchParams
URL 是任何与 Web 相关事物不可或缺的一部分。Deno 实现了 URL
和 URLSearchParams
API,如 WHATWG 所指定。这意味着我们的 URL 和查询字符串解析与浏览器完全相同。这可以防止围绕一个系统以略微不同的方式解析 URL 的另一个系统的安全问题。
const url = new URL("https://example.com/foo");
console.log(url.href); // https://example.com/foo
console.log(url.hostname); // example.com
console.log(url.searchParams.get("name")); // undefined
url.searchParams.append("name", "bar");
console.log(url.href); // https://example.com/foo?name=bar
console
console
可能是最有用的 Web API。Printf 调试?您可能已经了解 console.log()
,所以这里有一些您可能不知道的 Deno 实现的 console
的其他很酷的功能
%c
标记可用于使用 CSS 为日志记录输出添加颜色。Deno 原生理解这一点——无需再记住 ANSI 转义码来进行彩色日志记录
console.log("%cHello, world!", "color: red");
console.log("%cHello, %cworld!", "font-weight: bold", "font-style: italic");
我们实现的另一个很酷的 console
功能是 console.time
API。它使找出某些操作花费了多长时间变得超级简单
console.time("foo");
await new Promise((resolve) => setTimeout(resolve, 1000));
console.timeEnd("foo");
Worker
JavaScript 执行始终是单线程的——该语言没有内置并发原语。为了支持需要多个线程的工作负载,Web 具有 Worker
的概念。Worker 是在单独线程上运行的附加 JavaScript 执行上下文,与主线程完全隔离。
Deno 与浏览器一样,原生实现 Worker。由于 API 相同,因此您可以将为浏览器编写的库用于 Deno,而无需进行任何更改。此类库的一个例子是 comlink
——它通过抽象跨 Worker 通信的细节,使使用 Worker 变得超级简单。
// main.js
import * as Comlink from "https://cdn.skypack.dev/[email protected]?dts";
const url = new URL("./worker.js", import.meta.url);
const worker = new Worker(url, { type: "module" });
const obj = Comlink.wrap(worker);
console.log(`Counter: ${await obj.counter}`);
await obj.inc();
console.log(`Counter: ${await obj.counter}`);
worker.terminate();
// worker.js
import * as Comlink from "https://cdn.skypack.dev/[email protected]?dts";
const obj = {
counter: 0,
inc() {
this.counter++;
},
};
Comlink.expose(obj);
Event
和 EventTarget
在浏览器中,事件和事件目标是所有交互式体验的基础。它们允许您注册一个回调,以便在发生某些事件时调用该回调。例如,FileReader
是一个 EventTarget
:它在读取文件块、完全读取文件或发生错误时发出事件。
现代 API 通常不再使用事件和回调,而是使用 Promise
。由于 async/await,这些具有更好的可用性和可读性。尽管如此,许多 API 仍然使用 Event
和 EventTarget
,因此 Deno 与浏览器一样实现了它们。
const target = new EventTarget();
target.addEventListener("foo", (event) => {
console.log(event);
});
target.dispatchEvent(new Event("foo"));
WebSocket
正如 Deno 如何实现 fetch
以执行与浏览器中相同的 HTTP 请求一样,Deno 还实现了 WebSocket
API。WebSocket 是在客户端和服务器之间进行双向通信的好方法。
由于 WebSocket 协议在连接的双方(客户端和服务器)上以相同的方式工作,因此 Deno 也使用相同的 API 用于本机 HTTP 服务器上的服务器端(传入)WebSocket 连接。
const ws = new WebSocket("ws://127.0.0.1:4500");
ws.onopen = () => {
ws.send("Hello, world!");
};
ws.onmessage = (event) => {
console.log(event.data);
ws.close();
};
ReadableStream
和 WritableStream
流式传输是现代 Web 应用程序的关键部分。在处理超出可用内存一次可以容纳的数据大小时,它是必需的。在处理大型文件时,它也是减少 TTFB 的好方法。
Deno 支持与浏览器相同的流式传输 API。这非常强大,因为它允许 Deno 用户使用为浏览器编写的流式传输转换器。它还允许 Web 开发人员在其网站上使用为 Deno 编写的流式传输转换器。例如,Deno 标准库中的 std/streams
模块可用于 Deno 和浏览器,以执行诸如在换行符处分割文本流之类的操作。
可读流是最常见的流类型,在您想要从源读取数据时使用。例如,您可能想要流式传输 HTTP 请求的响应主体或磁盘上的文件内容。
可写流是相反的——当您想要将数据分块写入目标时使用它们。一种情况是 WebSocketStream
的发送端(稍后将详细介绍)。
const body = new ReadableStream({
start(controller) {
controller.enqueue(new Uint8Array([1, 2, 3]));
controller.enqueue(new Uint8Array([4, 5, 6]));
controller.close();
},
});
const resp = await fetch("https://httpbin.org/anything", { body });
for await (const chunk of resp.body) {
console.log(chunk);
}
TransformStream
除了低级流基元 `ReadableStream` 和 `WritableStream` 之外,网络还拥有一个通用 API 来转换流数据,称为 `TransformStream`。它们具有可读和可写端,以及一个“转换器”函数。对于写入可写端的每个块,都会调用转换器函数。然后,它可以将转换后的块排队到可读端,供使用者读取。
const input = new ReadableStream({
start(controller) {
controller.enqueue("Hello, ");
controller.enqueue("world!");
controller.close();
},
});
const transformer = new TransformStream({
transform(chunk, controller) {
controller.enqueue(chunk.toUpperCase());
},
});
const output = input.pipeThrough(transformer);
for await (const chunk of output) {
console.log(chunk);
}
WebSocketStream
如前所述,Deno 实现了建立在 `EventTarget` 之上的 `WebSocket` API。有时使用起来会很痛苦。作为替代方案,您可以使用基于流和承诺的 `WebSocketStream` API。当您使用异步/等待时,它更容易使用。
const wss = new WebSocketStream("wss://example.com");
const { writable, readable } = await wss.connection;
const writer = writable.getWriter();
await writer.write("Hello server!");
for await (const message of readable) {
console.log("new message:", message);
}
注意:此 API 仍处于实验阶段,将来可能会更改。您需要使用 `–unstable` 标志才能使用它。
TextEncoderStream
和 TextDecoderStream
TextEncoderStream 和 TextDecoderStream 是转换流,它们可以将字符串以块的形式编码和解码为字节。与通常用于完全同步操作的 TextEncoder 和 TextDecoder 不同,TextEncoderStream 和 TextDecoderStream 是完全流式的。
此 API 使将来自获取调用的响应流转换为字符串块变得非常容易。
const response = await fetch("https://example.com");
const body = response.body.pipeThrough(new TextDecoderStream());
for await (const chunk of body) {
console.log(chunk);
}
CompressionStream
和 DecompressionStream
另一个经常需要对流数据执行的操作是压缩和解压缩。您可以使用 `CompressionStream` 在将某些数据上传到存储桶之前对其进行 gzip 压缩,例如。当再次下载该数据时,您可以使用 `DecompressionStream` 对其进行解压缩。
与 Chrome 一样,Deno 支持 `gzip` 和 `deflate` 压缩。对 `brotli` 压缩的支持即将到来。
const response = await fetch("https://example.com");
const body = response.body.pipeThrough(new CompressionStream("gzip"));
const file = await Deno.create("./file.gz");
for await (const chunk of body) {
await file.write(chunk);
}
`CompressionStream` 在最新的 Deno canary 构建中可用,但尚未在 1.18 稳定构建中可用。它将在 Deno 1.19 中首次在稳定构建中可用。
URLPattern
URLPattern 是一个新的网络 API,用于将 URL 与 path-to-regexp
样式模式进行匹配。它非常适用于创建 HTTP 服务器的路由系统,例如。该 API 在 Chrome 和 Deno 中可用,Firefox 也对实现它感兴趣。
const pattern = new URLPattern({ pathname: "/hello/:name" });
const match = pattern.exec("https://example.com/hello/Deno");
console.log(match.pathname.groups);
alert
、confirm
和 prompt
CLI 应用程序通常需要在一定程度上与用户交互。Deno 实现了来自网络平台的简单对话框 API `alert`、`confirm` 和 `prompt`,用于此目的。您可以使用 `alert` 向用户显示一条消息并等待确认,询问用户是否要 `confirm` 一个是/否问题,或者 `prompt` 用户输入一个字符串响应。
let name;
do {
name = prompt("What is your name?");
} while (!confirm(`Is your name ${name}?`));
alert(`Hello, ${name}!`);
localStorage
和 sessionStorage
在编写 CLI 应用程序时,通常也需要在运行之间持久化少量状态(例如 API 访问令牌)。这可以使用 `localStorage` API 轻松完成。它是一个持久化的键值存储,用于每个 Deno 应用程序。
const count = parseInt(localStorage.getItem("count") || "0");
console.log(`You have started the application ${count} times previously.`);
localStorage.setItem("count", count + 1);
navigator
window.navigator
对象包含有关当前系统的一些有用信息,例如可用 CPU 内核/线程的数量。`navigator.hardwareConcurrency` 包含逻辑 CPU 内核的数量。
console.log("This system has", navigator.hardwareConcurrency, "CPU cores.");
WebGPU
所有以前的 API 都与 CPU 相关,或者是一些 I/O 原语。Deno 还支持通过 WebGPU API 访问 GPU。此低级 API 可以被认为是“网络的 vulkan”。它允许高效地低级访问 GPU 以执行渲染和计算任务。
Deno 将其 WebGPU 实现基于与 Firefox 使用的相同 Rust 库:wgpu
.
const adapter = await navigator.gpu.requestAdapter();
console.log("GPU adapter:", adapter.name);
// this blog post is already long enough, I won't go into
// low level GPU programming here :-)
注意:此 API 仍处于实验阶段,将来可能会更改。您需要使用 `–unstable` 标志才能使用它。
MessageChannel
一个不太为人知的网络 API 是 `MessageChannel`。它是一对流,可用于在两个工作线程之间进行通信。每个通道都有两个“端口”,每个端口都可以保留在当前工作线程中或传输到另一个工作线程。
这允许在工作线程之间建立非常复杂的通信通道,而无需单个集中式工作线程充当“消息代理”。
const channel = new MessageChannel();
const { port1, port2 } = channel;
port1.onmessage = (event) => {
console.log(event.data);
port1.close();
};
port2.postMessage("Hello, world!");
port2.close();
BroadcastChannel
BroadcastChannel 与 MessageChannel 类似,它也是一个在工作线程之间进行通信的通道。但是,与 MessageChannel 不同,它是一个一对多通道,而不是一对一通道。
const channel = new BroadcastChannel("my-channel");
channel.onmessage = (event) => {
console.log(event.data);
};
channel.postMessage("Hello, world!");
注意:此 API 仍处于实验阶段,将来可能会更改。您需要使用 `–unstable` 标志才能使用它。