# Speculation Rules API

> 请浏览器预取或预渲染可能下一页的 speculationrules JSON、它的 eagerness 等级、Chrome 的限制，以及它在生产环境中为何仅 Chromium 支持。

Speculation Rules API 让页面在 `<script type="speculationrules">` 内的 JSON 块或 `Speculation-Rules` 响应头中声明浏览器应在用户导航之前预取或完整预渲染哪些 URL，于是下一次点击直接由内存应答。对文档而言它取代了 `<link rel="prefetch">` 以及已废弃的 `<link rel="prerender">`（Chrome 63 起后者变成 NoState Prefetch），而且在生产环境中仅 Chromium 支持。

## 语法

```html
<script type="speculationrules">
{
  "prerender": [{ "where": { "href_matches": "/articles/*" }, "eagerness": "moderate" }],
  "prefetch": [{ "urls": ["/next.html"] }]
}
</script>
```

```http
Speculation-Rules: "/rules.json"
```

该块由浏览器解析而不执行；不支持该类型的引擎会忽略它。Chrome 109 随同源预渲染一起发布了该 API，Chrome 121 加入 `Speculation-Rules` 头并让 `source` 成员变为可选；Safari 26.2 在「SpeculationRules prefetch」偏好之后实现了 `prefetch` 规则，Firefox 没有实现（BCD `html.elements.script.type.speculationrules`）。

## 参数

| 成员 | 类型 | 含义 |
|---|---|---|
| `prefetch`、`prerender` | 规则数组 | 动作。prefetch 下载响应；prerender 在隐藏标签页中加载并渲染页面，脚本也会执行。 |
| `urls` | 字符串数组 | 列表规则：显式 URL，相对文档解析（带 `"relative_to": "document"` 时相对规则集解析）。 |
| `where` | object | 文档规则：对页面内链接的谓词，含 `href_matches`（URL 模式）、`selector_matches`（CSS 选择器）以及 `and`、`or`、`not` 组合子。 |
| `eagerness` | `"immediate"`、`"eager"`、`"moderate"`、`"conservative"` | 何时执行。`immediate` 在解析时；`moderate` 在悬停 200 ms 之后（触摸设备上为 `pointerdown`）；`conservative` 在 `pointerdown`；`eager` 在 Chrome 143 之前与 `immediate` 行为相同，之后改为基于视口的启发式。列表规则默认 `immediate`，文档规则默认 `conservative`。 |
| `requires` | array | `["anonymous-client-ip-when-cross-origin"]` 把跨源预取限定在私有预取代理上。 |
| `referrer_policy`、`tag`、`expects_no_vary_search`、`target_hint` | string | 推测请求的 referrer、在 DevTools 和 `Sec-Speculation-Tags` 头中显示的标签、`No-Vary-Search` 提示，以及预渲染的目标（`_self` 或 `_blank`）。 |

Chrome 限制并发的推测：`immediate` 规则 10 个预渲染和 50 个预取，`eager`、`moderate` 与 `conservative` 规则各 2 个，出现更新的候选时按先进先出替换（[Prerender pages in Chrome](https://developer.chrome.com/docs/web-platform/prerender-pages)，developer.chrome.com）。预取请求带 `Sec-Purpose: prefetch`；预渲染的文档看到 `document.prerendering === true`，并在激活时收到 `prerenderingchange` 事件。

## 异常

无。

## 各引擎的支持情况

Chromium 是唯一在生产环境中同时具备 prefetch 和 prerender 规则的引擎：Chrome 和 Edge 自 109 起，头形式自 121 起。Safari 26.2 只在启用「SpeculationRules prefetch」偏好时解析 `prefetch` 规则并忽略 `prerender`；Firefox 完全忽略该脚本类型（BCD `html.elements.script.type.speculationrules`）。不支持的引擎只是少了提前量，所以规则可以无条件发布。

## 示例

示例覆盖一个规则集、预渲染页面需要的页面侧代码，以及兜底。

### 悬停时预渲染文章链接

`moderate` 热度的文档规则会预渲染指针停留 200 ms 的那篇文章，覆盖了多数点击，又不必渲染页面上的每个链接。

```html
<script type="speculationrules">
{
  "prerender": [{
    "where": { "and": [
      { "href_matches": "/articles/*" },
      { "not": { "selector_matches": ".no-prerender" } }
    ]},
    "eagerness": "moderate"
  }]
}
</script>
```

退出登录、一键操作之类绝不能预渲染的链接加上 `no-prerender` 类；预渲染的页面会运行脚本，加载时的副作用在用户决定访问之前就会发生。

### 把分析上报推迟到激活

统计页面浏览的代码必须等预渲染页面变为可见，否则每个悬停过的链接都算一次访问。

```js
function onVisible(callback) {
  if (document.prerendering) {
    document.addEventListener('prerenderingchange', callback, { once: true });
  } else {
    callback(); // 未预渲染（或引擎不支持）：页面已经可见
  }
}

onVisible(() => sendPageView(location.href));
```

没有该 API 的引擎里 `document.prerendering` 为 `undefined`，`else` 分支把它当作普通的可见加载。

### 检测 API 并退回 prefetch

`HTMLScriptElement.supports('speculationrules')` 是能力测试。兜底插入一个 `<link rel="prefetch">`，在 Firefox 和 Chromium 中为下一个文档预热 HTTP 缓存而不预渲染。

```js
function speculate(url) {
  if (typeof HTMLScriptElement.supports !== 'undefined' && HTMLScriptElement.supports('speculationrules')) {
    const script = document.createElement('script');
    script.type = 'speculationrules';
    script.textContent = JSON.stringify({ prefetch: [{ urls: [url] }] });
    document.head.append(script);
    return;
  }
  const link = document.createElement('link'); // 兜底：普通 prefetch，不预渲染
  link.rel = 'prefetch';
  link.href = url;
  document.head.append(link);
}
```

脚本添加的规则与静态规则计入同一限制；移除脚本元素会取消其待处理的推测并释放容量。

:::observed
Chrome DevTools 的 Application > Background services > Speculative loads 有三个标签页：**Speculative loads**、**Rules** 与 **Speculations**，第一个报告当前页面自身是否由预渲染而来，第二个列出页面中找到的每个规则集及其解析状态，第三个列出每个候选 URL 的当前状态，推测失败时还给出原因（[Debug speculation rules](https://developer.chrome.com/docs/devtools/application/debugging-speculation-rules)，developer.chrome.com）。Network 面板中每个预取都带请求头 `Sec-Purpose: prefetch`，服务器也正是靠它区分推测请求与真实导航。
:::

## 另请参阅

- [Speculation Rules](https://wicg.github.io/nav-speculation/speculation-rules.html)（wicg.github.io）
- [Prerender pages in Chrome for instant page navigations](https://developer.chrome.com/docs/web-platform/prerender-pages)（developer.chrome.com）
- [Debug speculation rules](https://developer.chrome.com/docs/devtools/application/debugging-speculation-rules)（developer.chrome.com）
- [前进后退缓存（bfcache）](/zh/reference/performance/bfcache/)
- [资源提示（preload 与 preconnect）](/zh/reference/performance/resource-hints/)
- [View Transitions API](/zh/reference/performance/view-transitions/)