共享存储 API

可用性有限

此特性不是基线特性,因为它在一些最广泛使用的浏览器中不起作用。

实验性: 这是一项实验性技术
在生产中使用此技术之前,请仔细检查浏览器兼容性表格

警告: 此功能目前遭到一家浏览器厂商的反对。详见下方的标准立场部分。

共享存储 API 是一种客户端存储机制,它支持未分区、跨站的数据访问,同时保护隐私(即不依赖跟踪 Cookie)。

概念与用法

Web 上隐私安全问题的一个主要来源是使用在网站中嵌入的第三方内容上设置的 Cookie(例如通过<iframe>元素)。这些 Cookie 可用于跟踪和分析用户,并在网站之间共享信息。

为了防止跨站跟踪,浏览器正在努力分区所有存储类型,包括CookieWeb 存储IndexedDBCache API。然而,实现这一目标的一个主要障碍是存在一些合法的用例,它们依赖于跨站信息共享。此类用例的示例包括广告商希望衡量其广告在网站上的覆盖范围并生成报告,以及网站所有者希望根据用户所属的群组或其之前的网站互动来定制用户体验。

共享存储 API 为此类用例提供了灵活的解决方案。它旨在提供所需的数据存储、处理和共享功能,同时不具备跟踪和分析用户的功能。

与其他存储 API 一样,您可以随时写入共享存储。但是,您只能在 worklet 中读取共享存储数据。Worklet 提供了一个安全的环境,您可以在其中处理共享存储数据并返回有用的结果,但您无法将数据直接共享给关联的浏览上下文。

要从共享存储 worklet 中提取有用的结果,您需要使用输出门。这些门具有特定的用途,例如根据共享存储数据从提供的列表中选择一个 URL 显示给用户。面向用户的结果会安全地显示在围栏框架中,并且无法从嵌入页面访问。

输出门

共享存储 API 当前可用的输出门将在以下部分中讨论。在每个部分中,我们列出了每个门的典型用例,并提供了包含更多信息和代码示例的指南链接。

注意: 未来可能会添加更多输出门以支持其他用例。

URL 选择

URL 选择输出门通过selectURL()方法访问,用于根据共享存储数据,从提供的列表中选择一个 URL 显示给用户。此门可用于以下目的:

  • 创意轮播:使用存储的数据(例如创意 ID、查看次数和用户互动)来确定用户在不同网站上看到哪些创意内容。这种方法有助于平衡查看次数并防止某些内容过度曝光,从而避免负面用户体验。
  • A/B 测试:将用户分配到一个实验组,然后将组详细信息存储在共享存储中以供跨站访问。
  • 定制用户体验:根据用户的注册状态或其他用户状态共享定制内容和行动号召。

运行

运行输出门,通过run()方法访问,旨在以通用方式处理某些共享存储数据。

私有聚合 API 可以使用 Run 输出门来处理共享存储数据并生成聚合报告。这些报告可用于以下用例:

  • 独立覆盖率报告:内容生产者和广告商通常希望了解其内容的独立观看者数量。您可以使用共享存储来报告用户首次看到您的广告或嵌入式出版物的时间,并防止在不同网站上对同一用户进行重复计数,从而为您提供一个近似独立覆盖率的聚合噪音报告。
  • 用户人口统计报告:内容生产者通常希望了解其受众的人口统计数据。您可以使用共享存储在您的主站点上记录用户人口统计数据,并使用聚合报告在嵌入式上下文中报告其他站点上的数据。
  • K+ 频率测量:有时被称为“有效频率”,K+ 频率是指用户识别或回忆某些内容所需的最小查看次数(通常用于广告查看的上下文中)。您可以使用共享存储来构建至少看过 K 次内容的独立用户报告。

了解共享存储的工作原理

使用共享存储 API 分为两个部分——向存储写入数据和读取/处理数据。为了让您了解这些部分是如何处理的,我们将通过 developer.chrome.com 上基本的A/B 测试示例来引导您。在这个示例中,用户被分配到一个实验组,并且组详细信息存储在共享存储中。其他网站在选择要在围栏框架中显示的 URL 时能够使用这些数据。

写入共享存储

写入共享存储很简单——您可以使用SharedStorage接口上定义的方法来设置追加删除/清除数据。

此功能在两种不同的上下文中可用:

  • 在运行您的网站或应用程序的主浏览上下文中,在WindowSharedStorage上。这可以通过window.sharedStorage获得。
  • 在您的共享存储工作线程的上下文中,在WorkletSharedStorage上。这可以通过this.sharedStorage获得。

在我们的 A/B 测试示例中,我们在应用程序上下文中定义了一个函数,它生成一个随机数——0 或 1——来表示一个实验组。然后我们运行window.sharedStorage.set()函数将用户分配到一个组并将结果保存在共享存储中:

js
// Randomly assigns a user to a group 0 or 1
function getExperimentGroup() {
  return Math.round(Math.random());
}

async function injectContent() {
  // Assign user to a random group (0 or 1) and store it in shared storage
  window.sharedStorage.set("ab-testing-group", getExperimentGroup(), {
    ignoreIfPresent: true,
  });
}

注意: ignoreIfPresent: true选项导致如果共享存储中已经包含具有指定键的数据项,则set()函数中止。

从共享存储读取和处理数据

如上所述,要从共享存储 worklet 中提取有用的结果,您需要使用输出门。在此示例中,我们将使用URL 选择输出门读取用户的实验组,然后根据其组在围栏框架中显示一个 URL。

要使用输出门,您需要:

  1. 在 worklet 模块脚本中定义并注册一个操作来处理 URL 选择。
  2. 将模块添加到您的共享存储 worklet。
  3. 使用 worklet 操作选择 URL,并将其加载到围栏框架中。

下面我们将逐一查看这些步骤。

在工作线程模块中定义一个操作

URL 选择是基于存储在共享存储中的实验组。为了检索该值并根据它选择一个 URL,我们需要在SharedStorageWorklet上下文中定义一个操作。这确保了原始数据对其他上下文隐藏,从而保护了隐私。

URL 选择操作是一个 JavaScript 类,它必须遵循以下规则(这些规则因每个输出门而异,具体取决于其预期用例):

  • 实际功能必须包含在一个异步的run()方法中,该方法将一个包含 URL 的对象数组作为其第一个参数,并将一个数据对象作为其第二个参数(调用时,数据参数是可选的)。
  • run()方法必须返回一个数字,该数字将等于所选 URL 的序号。

注意: 每个输出门都有一个对应的接口,定义其类和run()方法所需的结构。对于 URL 选择,请参阅SharedStorageSelectURLOperation

一旦操作被定义,就需要使用SharedStorageWorkletGlobalScope.register()进行注册。

js
// ab-testing-worklet.js
class SelectURLOperation {
  async run(urls, data) {
    // Read the user's experiment group from shared storage
    const experimentGroup = await this.sharedStorage.get("ab-testing-group");

    // Return the group number
    return experimentGroup;
  }
}

register("ab-testing", SelectURLOperation);

请注意,我们主应用程序上下文中设置的值是如何使用WorkletSharedStorage.get()检索的。重申一下,为了保护隐私和减少数据泄露,您只能在工作线程中从共享存储中读取值。

注意: 可以在同一个共享存储工作线程模块脚本中定义和注册多个具有不同名称的操作;有关示例,请参阅SharedStorageOperation

将模块添加到共享存储工作线程

要使用工作线程模块中定义的操作,需要使用window.sharedStorage.worklet.addModule()将其添加到共享存储工作线程中。在我们的主应用程序上下文中,这在设置实验组值之前完成,以便在需要时随时可用:

js
async function injectContent() {
  // Add the module to the shared storage worklet
  await window.sharedStorage.worklet.addModule("ab-testing-worklet.js");

  // Assign user to a random group (0 or 1) and store it in shared storage
  window.sharedStorage.set("ab-testing-group", getExperimentGroup(), {
    ignoreIfPresent: true,
  });
}

选择 URL 并将其加载到围栏框架中

为了运行在工作线程中定义的操作,我们调用WindowSharedStorage.selectURL()。此方法充当我们工作线程操作的代理,安全地访问它并返回结果,而不会泄露任何数据。selectURL()是调用我们用户定义的工作线程操作的正确方法,因为它使用适用于 URL 选择操作的适当类结构定义,如上所述。

selectURL()需要一个包含要选择的 URL 的对象数组、一个可选的 options 对象,并且底层操作需要返回一个整数,它可以用来选择一个 URL。

js
// Run the URL selection operation
const fencedFrameConfig = await window.sharedStorage.selectURL(
  "ab-testing",
  [
    { url: `https://your-server.example/content/default-content.html` },
    { url: `https://your-server.example/content/experiment-content-a.html` },
  ],
  {
    resolveToConfig: true,
  },
);

由于 options 对象包含resolveToConfig: true,返回的Promise将解析为FencedFrameConfig对象。此对象可以设置为HTMLFencedFrameElement.config属性的值,从而导致所选 URL 的内容显示在相应的<fencedframe>元素中:

js
document.getElementById("content-slot").config = fencedFrameConfig;

完整的应用程序脚本如下所示:

js
// Randomly assigns a user to a group 0 or 1
function getExperimentGroup() {
  return Math.round(Math.random());
}

async function injectContent() {
  // Add the module to the shared storage worklet
  await window.sharedStorage.worklet.addModule("ab-testing-worklet.js");

  // Assign user to a random group (0 or 1) and store it in shared storage
  window.sharedStorage.set("ab-testing-group", getExperimentGroup(), {
    ignoreIfPresent: true,
  });

  // Run the URL selection operation
  const fencedFrameConfig = await window.sharedStorage.selectURL(
    "ab-testing",
    [
      { url: `https://your-server.example/content/default-content.html` },
      { url: `https://your-server.example/content/experiment-content-a.html` },
    ],
    {
      resolveToConfig: true,
    },
  );

  // Render the chosen URL into a fenced frame
  document.getElementById("content-slot").config = fencedFrameConfig;
}

injectContent();

共享存储与 Web 存储的区别

关键区别在于,共享存储旨在用于存储分区后的跨域数据。

  • 如果您是发布者,并且希望存储只有您自己可以访问的第一方数据,请使用Web 存储localStorage版本。
  • 如果您希望数据仅在浏览器会话期间持久化,请使用sessionStorage
  • 如果您在另一个网站上作为第三方运营,并且希望记录该网站的数据以便稍后在另一个网站上访问,请使用共享存储。

共享存储和 Web 存储之间的另一个重要区别是,从共享存储读取数据是受保护的(写入存储的行为类似)。使用localStoragesessionStorage,您可以自由读取。使用共享存储,只能在共享存储工作线程中进行读取,并且工作线程中用于读取的源与创建它的浏览上下文相同。

此外,作为一种跟踪保护措施,您无法在共享存储工作线程之外提取共享存储数据。您必须使用其中一个输出门来处理共享存储中的数据。

最后,localStorage中的数据会一直保留,直到手动清除。sessionStorage在浏览会话结束时清除,而共享存储数据在最后一次写入调用后 30 天清除。

接口

SharedStorage

表示特定源的共享存储。它定义了向共享存储写入数据的方法。

WindowSharedStorage

表示暴露给标准浏览上下文的特定源的共享存储。除其他外,它定义了使用可用输出门的方法,这些门充当在工作线程中定义的操作的代理。

WorkletSharedStorage

表示工作线程上下文内特定源的共享存储。除其他外,它定义了读取共享存储数据的方法。

SharedStorageWorklet

表示当前源的共享存储工作线程。它包含用于添加模块的addModule()方法。与常规Worklet不同,出于隐私原因,SharedStorageWorklet只能添加一个模块。

SharedStorageWorkletGlobalScope

表示SharedStorageWorklet模块的全局范围。它包含注册已定义操作和访问共享存储的功能。

输出门操作签名定义

SharedStorageOperation

表示所有不同输出门操作类型的基类。

SharedStorageRunOperation

表示运行输出门操作。

SharedStorageSelectURLOperation

表示 URL 选择输出门操作。

其他接口的扩展

Window.sharedStorage

返回当前源的WindowSharedStorage对象。

注册和本地测试

要在您的网站中使用共享存储 API,您必须在隐私沙盒注册流程中指定它。否则,共享存储 API 方法将无法成功运行。

您可以在不注册的情况下在本地测试您的共享存储 API 代码。要允许本地测试,请启用以下 Chrome 开发者标志:

chrome://flags/#privacy-sandbox-enrollment-overrides

示例

有关详细演示,请参阅共享存储 API 演示网站,其中还包含一些私有聚合 API 示例。

规范

规范
共享存储 API
# sharedstorage

标准立场

一家浏览器厂商反对此规范。已知标准立场如下:

浏览器兼容性

另见