Deno 中的所有 Web 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 字符串。它们很古老了——2004 年 11 月 9 日发布的 Firefox 1 就已经支持了。
atob("SGVsbG8gV29ybGQ="); // "Hello World"
btoa("Hello World"); // "SGVsbG8gV29ybGQ="
setTimeout
和 clearTimeout
setTimeout
是另一个非常古老的 Web 特性。它用于安排一个函数在经过一定时间后被调用。它返回一个数字 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
- 使用
crypto.subtle.sign()
和crypto.subtle.verify()
以及 RSA、ECDSA 和许多其他算法来签名和验证数据。 - 使用
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 也为原生 HTTP 服务器使用与 fetch
相同的 Request
和 Response
对象,因此可以通过调用 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 按照 WHATWG 的规范实现了 URL
和 URLSearchParams
API。这意味着我们的 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/comlink@4.3.1?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/comlink@4.3.1?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 也为原生 HTTP 服务器上的服务器端(传入)WebSocket 连接使用相同的 API。
const ws = new WebSocket("ws://: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
之外,Web 还有一个用于转换流式数据的通用 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。这有时使用起来相当痛苦。作为替代方案,你可以使用基于流和 Promise 的 WebSocketStream
API。当你使用 async/await 时,它会更容易使用。
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 使得将 `fetch` 调用的响应流转换为字符串块变得非常容易。
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 是一个新的 Web 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 为此目的实现了 Web 平台中简单的对话框 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 可以被认为是“Web 上的 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
一个鲜为人知的 Web API 是 MessageChannel
。它是一对流,可用于在两个 Worker 之间通信。每个通道都有两个“端口”,每个端口都可以保留在当前 Worker 中或传输到另一个 Worker。
这允许 Worker 之间建立非常复杂的通信通道,而无需单个集中式 Worker 充当“消息代理”。
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 类似,它也是一个用于 Worker 之间通信的通道。然而,与 MessageChannel 不同的是,它是一个一对多的通道,而不是一对一的通道。
const channel = new BroadcastChannel("my-channel");
channel.onmessage = (event) => {
console.log(event.data);
};
channel.postMessage("Hello, world!");
注意:此 API 仍处于实验阶段,未来可能会更改。你需要使用 --unstable 标志才能使用它。