# 已发布、源试用、实验标志与开发中

> 已发布、源试用、实验标志与开发中在 Chromium 发布流程中各自意味着什么、每种状态记录在哪里，以及为何应按能力门控代码。

能力状态描述的是一项特性在某一个引擎的发布流程中走到了哪一步、涉及哪些构建。开发中、位于实验标志之后、处于源试用和已发布这些标签分别对应不同的发布阶段和启用机制。访客浏览器是否暴露能力，仍需运行时检测。

## 工作原理

这些标签不是宣传用语。它们对应各引擎公开流程中有名字的阶段，而每个阶段都留下开发者可以查阅的记录。

### Chromium 发布流程中的阶段

Chromium 的 launching-features 文档给出了这条路径。对每一项面向 Web 暴露的特性，第 0 步都是创建一条 ChromeStatus 条目。到原型阶段，特性负责人向 blink-dev 发送 Intent to Prototype 并开始落地代码，文档明确要求这一阶段落地的改动都位于特性标志之后。当实现作为运行时启用的特性已经可用且较为完整，文档建议进入 Dev Trials 阶段并发出 Ready for Developer Testing 邮件，但明确说明这并非必需步骤。

源试用是之后的一个可选步骤，并不等同于标志阶段。文档描述的流程是进入 Origin Trial 阶段、生成发往 blink-dev 的 Intent to Experiment 邮件，并在至少获得一位 API owner 的 LGTM 后开始试用。发布是另一道门：文档要求 Intent to Ship 获得三位 API owner 的 LGTM。

试用有时间盒。初次源试用或实验最多只能运行 12 个 Chromium 里程碑，此后每次延期最多 6 个里程碑。延期还要求草案规范、TAG 评审、signals 征询、社区反馈收集与 web-platform-tests 都有实质进展。

### 源试用实际打开的是什么

Chrome 的源试用指南解释启用机制。注册一项试用后，该特性会对已注册源上的所有用户启用，无需他们切换任何标志或改用其他版本的构建，但可能需要升级浏览器。令牌有三种投递方式：放在 `<head>` 里的 `<meta http-equiv="origin-trial">` 标签、`Origin-Trial` HTTP 响应头，或由脚本注入。框架采用第一个有效令牌并忽略其余令牌；Chrome 会忽略无效或过期的令牌。

指南还介绍了源试用的变体。弃用试用（有时也称反向源试用）临时重新启用一项已被移除的特性；指南举的例子是自 Chrome 127 起移除的 mutation events。第三方源试用则让嵌入内容或服务的提供方在多个站点上使用该特性，而不必为每个源申请令牌。

## 支持范围

Firefox 把标志阶段记录为一个偏好项。MDN 的实验性特性页面逐项列出该特性包含在哪些构建里（Nightly、Beta、Developer Edition 或 Release）、是否默认启用，以及用于启用它的 `about:config` 偏好项名称。该页面也把生命周期讲清楚了：新特性通常先出现在 Nightly，并常常在那里默认启用以便尽早收集反馈；如果没有发现重大问题，就进入 Beta 与 Developer Edition；获批的特性在稳定的 Release 通道发布，而一旦某特性在正式版中默认启用，它就会从该页面移除。

WebKit 的特性状态页面写明旧页面已撤下。它请读者从 MDN 或 Can I Use 获取支持数据，并从 WebKit Standards Positions 查阅该项目对新兴规范的立场。

## 把状态映射到支持决策

可落地的换算很窄。实验标志让开发者在包含该能力的构建中启用它。源试用让已注册源为访客启用能力，无需每位访客自己切换标志或改用其他构建。Chromium 要求原型阶段落地的改动位于特性标志之后；下一阶段名为“Feature Complete behind a flag and implementation refinement”。

页面无法读取这些状态。状态看板可用于规划是否编写对应代码路径。最终运行时结果仍取决于访客浏览器暴露的能力，无论能力来自已开启的标志、有效的源试用令牌还是默认发布，代码都必须检测能力本身。

:::caution
状态标签与标准立场是两份不同的记录。在 Dev Trials 阶段，Chromium 发布流程称应考虑向其他浏览器厂商和开发者社区征询信号。准备发布前，如果尚未取得信号，则应至少提前一个月请求。WebKit 标准立场仓库中的 Web Install API 议题带有 `position: oppose` 标签（[Web Install API](https://github.com/WebKit/standards-positions/issues/463)，WebKit）。
:::

## 示例

运行时检测可以判断方法是否存在，也可以校验具体载荷。第一个示例在 Web Share 不存在时提供回退；第二个示例检测所选文件，但不发起分享请求。

### 按能力而不是按状态标签门控

检测针对特性暴露出来的方法。MDN 说明 `navigator.share()` 需要安全上下文和瞬时用户激活，因此示例从按钮处理函数调用它（[Navigator: share() method](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/share)，MDN）。

```html
<!doctype html>
<html lang="zh-CN">
  <meta charset="utf-8" />
  <title>能力检测</title>
  <button id="share" type="button">分享本页</button>
  <p id="status" role="status"></p>
  <p id="fallback" hidden>
    复制这个链接：<a id="page-link"></a>
  </p>
  <script>
    const button = document.querySelector("#share");
    const status = document.querySelector("#status");
    const fallback = document.querySelector("#fallback");
    const pageLink = document.querySelector("#page-link");

    pageLink.href = location.href;
    pageLink.textContent = location.href;

    button.addEventListener("click", async () => {
      if (!("share" in navigator)) {
        fallback.hidden = false;
        status.textContent = "分享不可用，请改为复制链接。";
        return;
      }

      try {
        await navigator.share({ title: document.title, url: location.href });
        status.textContent = "share() 已兑现。";
      } catch (error) {
        status.textContent = `share() 已拒绝（${error.name}）。`;
      }
    });
  </script>
</html>
```

该分支在用户操作时检测能力。回退会明显改变页面，并提供无需调用另一个可选 API 的可用链接。

### 提供操作前检查文件载荷

`navigator.canShare()` 返回指定数据能否交给 `navigator.share()` 分享。把所选 `File` 放进 `files` 数组即可检测这份载荷（[Navigator: canShare() method](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/canShare)，MDN）。

```html
<!doctype html>
<html lang="zh-CN">
  <meta charset="utf-8" />
  <title>文件分享能力</title>
  <label>
    选择文件
    <input id="file" type="file" />
  </label>
  <p id="file-status" role="status">尚未选择文件。</p>
  <script>
    const fileInput = document.querySelector("#file");
    const fileStatus = document.querySelector("#file-status");

    fileInput.addEventListener("change", () => {
      const [file] = fileInput.files;

      if (!file) {
        fileStatus.textContent = "尚未选择文件。";
      } else if (!("canShare" in navigator)) {
        fileStatus.textContent = "文件分享校验不可用。";
      } else {
        const result = navigator.canShare({ files: [file] });
        fileStatus.textContent = `canShare({ files }) 返回 ${result}。`;
      }
    });
  </script>
</html>
```

这个页面只显示同步校验结果，不会打开分享面板，也不会宣称分享已经完成。

## 另请参阅

- [Launching features in Blink](https://www.chromium.org/blink/launching-features/)（chromium.org）
- [Get started with origin trials](https://developer.chrome.com/docs/web-platform/origin-trials)（developer.chrome.com）
- [Experimental features in Firefox](https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Experimental_features)（developer.mozilla.org）
- [WebKit Feature Status](https://webkit.org/status/)（webkit.org）
- [Web Install API（navigator.install）](/zh/reference/installation/navigator-install/)
- [可安装性条件](/zh/reference/installation/installability-criteria/)
- [Web Share API](/zh/reference/capabilities/web-share/)