deno.com
本页内容

Web 平台 API

Deno 简化 Web 和云开发的一种方式是使用标准的 Web 平台 API(例如 fetch、WebSockets 等)而不是专有 API。这意味着如果您曾经为浏览器构建过应用,您可能已经熟悉 Deno,如果您正在学习 Deno,您也在投资您对 Web 的知识。

探索支持的 Web API

下面我们将重点介绍 Deno 支持的一些标准 Web API。

要检查 Web 平台 API 是否在 Deno 中可用,您可以点击 MDN 上的接口 并参考 其浏览器兼容性表

fetch 跳转到标题

fetch API 可用于发出 HTTP 请求。它按照 WHATWG fetch 规范 中的规定实现。

规范偏差 跳转到标题

  • Deno 用户代理没有 cookie jar。因此,不会处理响应中的 set-cookie 标头,也不会从可见的响应标头中过滤掉。
  • Deno 不遵循同源策略,因为 Deno 用户代理当前没有 origin 的概念,并且它没有 cookie jar。这意味着 Deno 不需要防止跨域泄露经过身份验证的数据。因此,Deno 未实现 WHATWG fetch 规范的以下部分
    • 3.1. 'Origin' header 节。
    • 3.2. CORS protocol 节。
    • 3.5. CORB 节。
    • 3.6. 'Cross-Origin-Resource-Policy' header 节。
    • 原子 HTTP 重定向处理.
    • opaqueredirect 响应类型。
  • redirect 模式为 manualfetch 将返回 basic 响应,而不是 opaqueredirect 响应。
  • 该规范对于如何处理 file: URL 比较模糊。Firefox 是唯一实现获取 file: URL 的主流浏览器,即使如此,它也不是默认工作的。截至 Deno 1.16,Deno 支持获取本地文件。有关详细信息,请参见下一节。
  • requestresponse 标头保护已实现,但与浏览器不同,它们对允许哪些标头名称没有任何约束。
  • RequestInit 中的 referrerreferrerPolicymodecredentialscacheintegritykeepalivewindow 属性及其相关行为未实现。相关字段不在 Request 对象上。
  • 支持请求体上传流式传输(在 HTTP/1.1 和 HTTP/2 上)。与当前的 fetch 提案不同,该实现支持双工流式传输。
  • headers 迭代器中迭代时,set-cookie 标头不会被连接。此行为正在 规范制定过程中

获取本地文件 跳转到标题

Deno 支持获取 file: URL。这使得编写在服务器和本地使用相同代码路径的代码更容易,并且更容易编写同时适用于 Deno CLI 和 Deno Deploy 的代码。

Deno 仅支持绝对文件 URL,这意味着 fetch("./some.json") 将不起作用。但应该注意的是,如果指定了 --location,则相对 URL 使用 --location 作为基础,但 file: URL 不能作为 --location 传递。

为了能够获取相对于当前模块的资源,如果模块是本地的或远程的,这应该有效,您应该使用 import.meta.url 作为基础。例如

const response = await fetch(new URL("./config.json", import.meta.url));
const config = await response.json();

关于获取本地文件的注意事项

  • 权限应用于读取资源,因此需要适当的 --allow-read 权限才能读取本地文件。
  • 本地获取仅支持 GET 方法,并将拒绝任何其他方法的 Promise。
  • 不存在的文件只会使用模糊的 TypeError 拒绝 Promise。这是为了避免指纹识别攻击的可能性。
  • 响应上未设置任何标头。因此,由消费者来确定内容类型或内容长度等信息。
  • 响应主体从 Rust 端流式传输,因此大文件以块的形式提供,并且可以取消。

CustomEvent 和 EventTarget 跳转到标题

DOM Event API 可用于分发和监听应用程序中发生的事件。它按照 WHATWG DOM 规范 中的规定实现。

规范偏差 跳转到标题

  • 事件不会冒泡,因为 Deno 没有 DOM 层次结构,因此没有树供事件冒泡/捕获。
  • timeStamp 属性始终设置为 0

类型定义 跳转到标题

已实现的 Web API 的 TypeScript 定义可以在 lib.deno.shared_globals.d.tslib.deno.window.d.ts 文件中找到。

特定于 worker 的定义可以在 lib.deno.worker.d.ts 文件中找到。

Location 跳转到标题

Deno 支持 Web 中的 location 全局变量。

Location 标志 跳转到标题

在 Deno 进程中没有“网页”,我们无法使用其 URL 作为 location。相反,我们允许用户通过在 CLI 上使用 --location 标志来模拟文档 location。它可以是 httphttps URL。

// deno run --location https://example.com/path main.ts

console.log(location.href);
// "https://example.com/path"

您必须传递 --location <href> 才能使其工作。如果您不这样做,任何对 location 全局变量的访问都将抛出错误。

// deno run main.ts

console.log(location.href);
// error: Uncaught ReferenceError: Access to "location", run again with --location <href>.

设置 location 或其任何字段通常会在浏览器中导致导航。这在 Deno 中不适用,因此在这种情况下会抛出错误。

// deno run --location https://example.com/path main.ts

location.pathname = "./foo";
// error: Uncaught NotSupportedError: Cannot set "location.pathname".

扩展用法 跳转到标题

在 Web 上,资源解析(不包括模块)通常使用 location.href 的值作为基础,任何相对 URL 都基于此基础。这会影响 Deno 采用的一些 Web API。

Fetch API 跳转到标题

// deno run --location https://api.github.com/ --allow-net main.ts

const response = await fetch("./orgs/denoland");
// Fetches "https://api.github.com/orgs/denoland".

如果未传递 --location 标志,则上面的 fetch() 调用将抛出错误,因为没有与 Web 类似的 location 可以作为基础。

Worker 模块 跳转到标题

// deno run --location https://example.com/index.html --allow-net main.ts

const worker = new Worker("./workers/hello.ts", { type: "module" });
// Fetches worker module at "https://example.com/workers/hello.ts".

注意

对于上述用例,最好传递完整的 URL,而不是依赖 --location。如果需要,您可以手动使用 URL 构造函数来设置相对 URL 的基础。

--location 标志旨在为那些有特定目的模拟文档 location 并且意识到这仅在应用程序级别有效的人而设计。但是,您也可以使用它来消除来自轻率访问 location 全局变量的依赖项的错误。

Web Storage 跳转到标题

Web Storage API 提供了一个用于存储字符串键和值的 API。持久化数据的工作方式类似于浏览器,并且具有 10MB 的存储限制。全局 sessionStorage 对象仅持久化当前执行上下文的数据,而 localStorage 持久化从一次执行到另一次执行的数据。

在浏览器中,localStorage 为每个 origin(实际上是协议 + 主机名 + 端口)唯一地持久化数据。截至 Deno 1.16,Deno 具有一组规则来确定什么是唯一的存储位置

  • 当使用 --location 标志时,location 的 origin 用于唯一地存储数据。这意味着 http://example.com/a.tshttp://example.com/b.tshttp://example.com:80/ 将共享相同的存储,但 https://example.com/ 将是不同的。
  • 如果没有 location 说明符,但指定了 --config 配置文件,则使用该配置文件的绝对路径。这意味着 deno run --config deno.jsonc a.tsdeno run --config deno.jsonc b.ts 将共享相同的存储,但 deno run --config tsconfig.json a.ts 将是不同的。
  • 如果没有配置或 location 说明符,Deno 使用主模块的绝对路径来确定共享哪些存储。Deno REPL 生成一个“合成”主模块,该模块基于启动 deno 的当前工作目录。这意味着从同一路径多次调用 REPL 将共享持久化的 localStorage 数据。

要从 localStorage 设置、获取和删除项目,您可以使用以下方法

// Set an item in localStorage
localStorage.setItem("myDemo", "Deno App");

// Read an item from localStorage
const cat = localStorage.getItem("myDemo");

// Remove an item from localStorage
localStorage.removeItem("myDemo");

// Remove all items from localStorage
localStorage.clear();

Web Workers 跳转到标题

Deno 支持 Web Worker API

Worker 可用于在多个线程上运行代码。Worker 的每个实例都在一个单独的线程上运行,该线程仅专用于该 worker。

目前 Deno 仅支持 module 类型的 worker;因此,在创建新 worker 时,必须传递 type: "module" 选项。

在主 worker 中使用相对模块说明符仅在使用 CLI 上传递的 --location <href> 时才受支持。不建议这样做以提高可移植性。您可以改为使用 URL 构造函数和 import.meta.url 来轻松地为附近的脚本创建说明符。但是,专用 worker 默认情况下具有 location 和此功能。

// Good
new Worker(import.meta.resolve("./worker.js"), { type: "module" });

// Bad
new Worker(import.meta.resolve("./worker.js"));
new Worker(import.meta.resolve("./worker.js"), { type: "classic" });
new Worker("./worker.js", { type: "module" });

与常规模块一样,您可以在 worker 模块中使用顶层 await。但是,您应注意始终在第一个 await 之前注册消息处理程序,否则可能会丢失消息。这不是 Deno 中的错误,这只是功能的不幸交互,它也发生在所有支持模块 worker 的浏览器中。

import { delay } from "jsr:@std/async@1/delay";

// First await: waits for a second, then continues running the module.
await delay(1000);

// The message handler is only set after that 1s delay, so some of the messages
// that reached the worker during that second might have been fired when no
// handler was registered.
self.onmessage = (evt) => {
  console.log(evt.data);
};

实例化权限 跳转到标题

创建新的 Worker 实例类似于动态导入;因此,Deno 需要对此操作的适当权限。

对于使用本地模块的 worker;需要 --allow-read 权限

main.ts
new Worker(import.meta.resolve("./worker.ts"), { type: "module" });
worker.ts
console.log("hello world");
self.close();
$ deno run main.ts
error: Uncaught PermissionDenied: read access to "./worker.ts", run again with the --allow-read flag

$ deno run --allow-read main.ts
hello world

对于使用远程模块的 worker;需要 --allow-net 权限

main.ts
new Worker("https://example.com/worker.ts", { type: "module" });
worker.ts
// This file is hosted at https://example.com/worker.ts
console.log("hello world");
self.close();
$ deno run main.ts
error: Uncaught PermissionDenied: net access to "https://example.com/worker.ts", run again with the --allow-net flag

$ deno run --allow-net main.ts
hello world

在 Worker 中使用 Deno 跳转到标题

main.js
const worker = new Worker(import.meta.resolve("./worker.js"), {
  type: "module",
});

worker.postMessage({ filename: "./log.txt" });
worker.js
self.onmessage = async (e) => {
  const { filename } = e.data;
  const text = await Deno.readTextFile(filename);
  console.log(text);
  self.close();
};
log.txt
hello world
$ deno run --allow-read main.js
hello world

指定 Worker 权限 跳转到标题

注意

这是一个不稳定的 Deno 功能。了解有关 不稳定功能 的更多信息。

worker 可用的权限类似于 CLI 权限标志,这意味着那里启用的每个权限都可以在 Worker API 级别禁用。您可以在 此处 找到每个权限选项的更详细描述。

默认情况下,worker 将从创建它的线程继承权限,但是为了允许用户限制此 worker 的访问权限,我们在 worker API 中提供了 deno.permissions 选项。

对于支持精细访问的权限,您可以传入 worker 将有权访问的所需资源列表,对于那些只有开/关选项的权限,您可以分别传递 true/false

const worker = new Worker(import.meta.resolve("./worker.js"), {
  type: "module",
  deno: {
    permissions: {
      net: [
        "deno.land",
      ],
      read: [
        new URL("./file_1.txt", import.meta.url),
        new URL("./file_2.txt", import.meta.url),
      ],
      write: false,
    },
  },
});

精细访问权限将绝对路由和相对路由都作为参数接收,但请注意,相对路由将相对于实例化 worker 的文件解析,而不是 worker 文件当前所在的路径

const worker = new Worker(
  new URL("./worker/worker.js", import.meta.url).href,
  {
    type: "module",
    deno: {
      permissions: {
        read: [
          "/home/user/Documents/deno/worker/file_1.txt",
          "./worker/file_2.txt",
        ],
      },
    },
  },
);

deno.permissions 及其子项都支持选项 "inherit",这意味着它将借用其父权限

// This worker will inherit its parent permissions
const worker = new Worker(import.meta.resolve("./worker.js"), {
  type: "module",
  deno: {
    permissions: "inherit",
  },
});
// This worker will inherit only the net permissions of its parent
const worker = new Worker(import.meta.resolve("./worker.js"), {
  type: "module",
  deno: {
    permissions: {
      env: false,
      hrtime: false,
      net: "inherit",
      ffi: false,
      read: false,
      run: false,
      write: false,
    },
  },
});

不指定 deno.permissions 选项或其子项之一将导致 worker 默认继承

// This worker will inherit its parent permissions
const worker = new Worker(import.meta.resolve("./worker.js"), {
  type: "module",
});
// This worker will inherit all the permissions of its parent BUT net
const worker = new Worker(import.meta.resolve("./worker.js"), {
  type: "module",
  deno: {
    permissions: {
      net: false,
    },
  },
});

您可以通过将 "none" 传递给 deno.permissions 选项来完全禁用 worker 的权限

// This worker will not have any permissions enabled
const worker = new Worker(import.meta.resolve("./worker.js"), {
  type: "module",
  deno: {
    permissions: "none",
  },
});

其他 API 与规范的偏差 跳转到标题

Cache API 跳转到标题

仅实现了以下 API

与浏览器相比,有几点不同

  1. 您不能将相对路径传递给 API。请求可以是 Request 或 URL 的实例,也可以是 URL 字符串。
  2. match()delete() 尚不支持查询选项。

您找到您需要的信息了吗?

隐私政策