跳至主要内容
Deno 2 终于来了 🎉️
了解更多

Deno 中所有网络 API 的列表

您是否曾经想知道 Deno 与 Web 的兼容性如何?可能没有,但我今天想知道。为了回答这个问题,我正在撰写这篇博客文章:我将列出并解释在 Deno 中实现的每个 Web API。给自己倒杯饮料,因为要浏览完这篇文章需要一段时间。

不过,在我们深入研究之前,我想设定一些基本规则

  1. 我不包括任何 JS 语言特性。只包括 Web 平台特性。
  2. 我将包括一些仍然标记为 --unstable 的特性,但会说明它们尚未稳定。
  3. 我不包括任何不是具体 API 的特性。例如,JSON 模块是在 Deno 中实现的,但更像是一种抽象概念,而不是具体 API,因此不包含在此列表中。

目录

atobbtoa

这两个函数用于对 base64 字符串进行编码和解码。它们很古老—— Firefox 1 于 2004 年 11 月 9 日发布,当时就已经支持了。

atob("SGVsbG8gV29ybGQ="); // "Hello World"
btoa("Hello World"); // "SGVsbG8gV29ybGQ="

setTimeoutclearTimeout

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.");

setIntervalclearInterval

setIntervalclearIntervalsetTimeoutclearTimeout 非常相似。区别在于 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);

fetchRequestResponseHeaders

Deno 实现了可能最流行的现代 Web API:fetch。它用于发出 HTTP 请求。使用方法非常简单:第一个参数是要请求的 URL,第二个参数是包含选项(如 methodheadersbody)的可选对象。

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 的所有对象:RequestResponseHeaders。这些对象分别代表 HTTP 请求、HTTP 响应和 HTTP 标头列表。由于 Deno 是一个以 Web 为中心的运行时,因此我们的 HTTP 服务器使用这些相同的对象来表示请求和响应。 这使得代理请求变得非常容易。

const resp = await fetch("https://whats-my-ip.deno.dev/");
const text = await resp.text();
console.log(text);

BlobFile

BlobFile 都表示二进制数据。它们与 Uint8Array 的最大区别在于它们可以将数据存储在内存或磁盘中,因此非常适合大型二进制 Blob。由于有可能将数据存储在磁盘中,因此 BlobFile 支持的数据只能异步访问。

可以使用 .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));

TextEncoderTextDecoder

有时您需要将字符串编码或解码为二进制表示形式,或从二进制表示形式解码为字符串。Deno 提供了 TextEncoderTextDecoder 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 的相同 RequestResponse 对象用于本机 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

URLURLSearchParams

URL 是任何与 Web 相关事物不可或缺的一部分。Deno 实现了 URLURLSearchParams 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);

EventEventTarget

在浏览器中,事件和事件目标是所有交互式体验的基础。它们允许您注册一个回调,以便在发生某些事件时调用该回调。例如,FileReader 是一个 EventTarget:它在读取文件块、完全读取文件或发生错误时发出事件。

现代 API 通常不再使用事件和回调,而是使用 Promise。由于 async/await,这些具有更好的可用性和可读性。尽管如此,许多 API 仍然使用 EventEventTarget,因此 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();
};

ReadableStreamWritableStream

流式传输是现代 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` 标志才能使用它。

TextEncoderStreamTextDecoderStream

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);
}

CompressionStreamDecompressionStream

另一个经常需要对流数据执行的操作是压缩和解压缩。您可以使用 `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);

alertconfirmprompt

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}!`);

localStoragesessionStorage

在编写 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);

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` 标志才能使用它。