# widgets 清单成员

> widgets 数组为 Windows 11 小组件面板注册 Adaptive Cards 小组件，由 PWA 的 Service Worker 通过 widgetinstall 等事件渲染和更新。

`widgets` 是一组小组件定义，已安装的 PWA 用它向操作系统的小组件界面提供内容。每个定义指明一个
Adaptive Cards 模板和一个 JSON 数据 URL；浏览器绘制卡片，应用的 Service Worker 响应小组件生命周期事件
来填充和刷新它，全程不需要打开任何页面。

Windows 11 上的 Microsoft Edge 108 及之后版本是唯一实现，目标是 Windows 11 小组件面板
（Microsoft Edge 文档，"Build PWA-driven widgets"）。该成员来自微软的说明文档，不是 W3C 或 WICG 草案；
Chrome 155、Firefox 157 与 Safari 27 在解析时将其丢弃。

## 成员

- **类型**：小组件定义对象数组。必填键：`name`、`description`、`tag`（Service Worker 用来指代该小组件的
  字符串）、`ms_ac_template`（Adaptive Cards 模板的 URL）与 `data`（模板绑定的 JSON 的 URL）。可选键：
  `short_name`、`template`（在 Edge 中仅供参考）、`type`（数据的 MIME 类型，默认 `application/json`）、
  `icons`、`screenshots`、`backgrounds`、`auth`（布尔值）与 `update`（刷新周期，单位为秒）。
- **默认值**：缺省。没有该成员，应用不会在小组件面板提供任何内容；缺少 `tag` 或 `ms_ac_template` 的条目
  不会被提供。
- **示例值**：`[{ "name": "待办任务", "tag": "tasks", "ms_ac_template": "/widgets/tasks.json", "data": "/widgets/tasks-data.json" }]`。

安装应用只是把它的小组件列进面板的 **Add widgets** 选择器。用户添加后小组件才激活，此时 Service Worker
收到 `widgetinstall` 事件；Worker 随后调用 `self.widgets.updateByTag(tag, { template, data })` 完成渲染。
其余事件有 `widgetuninstall`、`widgetresume`（面板重新可见）与 `widgetclick`（用户触发了 Adaptive Cards
动作，动作名在 `event.action` 上）。Edge 还提供 `self.widgets.getByTag()`、`getByInstanceId()` 与
`matchAll()` 用于枚举已安装的小组件。

:::observed
Chrome 155（Windows 11，英文界面）的 DevTools > Application > Manifest 中，含 `widgets` 数组的清单既没有
小组件相关的分节，也没有 **Errors and warnings** 条目：该成员被无提示地丢弃。同一台机器上的 Edge 154
在安装后把小组件以应用的 `name` 加入小组件面板的选择器，而 `'widgets' in self` 只在 Edge 的应用
Service Worker 控制台中求值为 `true`。
:::

## 示例

示例描述从 `https://tasks.example/` 安装的应用上的一个任务列表小组件。

### 声明小组件及其模板、数据与刷新周期

`tag` 是每次 Service Worker 调用的键，应在各版本间保持稳定。`update: 900` 请求 Edge 在小组件位于面板上时
每十五分钟重新拉取一次 `data`。

```json
{
  "widgets": [
    {
      "name": "待办任务",
      "short_name": "任务",
      "description": "一眼看到你的待办任务",
      "tag": "open-tasks",
      "template": "open-tasks",
      "ms_ac_template": "/widgets/open-tasks.ac.json",
      "data": "/widgets/open-tasks-data.json",
      "type": "application/json",
      "update": 900,
      "icons": [{ "src": "/widgets/icon-96.png", "sizes": "96x96" }],
      "screenshots": [
        { "src": "/widgets/open-tasks-wide.png", "sizes": "600x400", "label": "列出三个任务的待办任务小组件" }
      ]
    }
  ]
}
```

`screenshots` 条目会显示在选择器里，所以它应当呈现渲染后的卡片，而不是应用本身。

### 安装时渲染，恢复时刷新

Service Worker 拉取小组件定义中指明的模板与数据，把两者交给 `updateByTag()`。同一个函数也在
`widgetresume` 时运行，这是面板隐藏期间数据过期后的刷新时机。

```js
async function renderWidget(widget) {
  const definition = widget.definition;
  const template = await (await fetch(definition.msAcTemplate)).text();
  const data = await (await fetch(definition.data)).text();
  await self.widgets.updateByTag(definition.tag, { template, data });
}

self.addEventListener('widgetinstall', (event) => {
  event.waitUntil(renderWidget(event.widget));
});

self.addEventListener('widgetresume', (event) => {
  event.waitUntil(renderWidget(event.widget));
});
```

Edge 在 `event.widget.definition` 上暴露定义，清单键被转换为驼峰命名，所以模板 URL 读作 `msAcTemplate`。

### 检测 API 并处理卡片动作

`self.widgets` 只存在于实现了小组件的浏览器，所以跨浏览器共用的 Service Worker 要对每次调用加保护。
`widgetclick` 处理函数收到 Adaptive Cards 模板中声明的动作名，可以就地更新卡片。

```js
if ('widgets' in self) {
  self.addEventListener('widgetclick', (event) => {
    if (event.action !== 'complete-task') return;
    event.waitUntil(
      fetch('/api/tasks/complete', { method: 'POST', body: JSON.stringify(event.data) })
        .then(() => renderWidget(event.widget)),
    );
  });
} else {
  // 此浏览器没有小组件界面；应用只保留页内的任务列表。
}
```

在 Edge 之外，`else` 分支就是全部：清单条目无效，Worker 不注册任何监听器。

## 另请参阅

- [Microsoft Edge 上的 PWA（侧边栏、应用商店）](/zh/reference/platforms/edge/)
- [Windows 上的 PWA](/zh/reference/platforms/windows/)
- [icons 清单成员](/zh/reference/manifest/icons/)
- [screenshots 清单成员](/zh/reference/manifest/screenshots/)
- [Build PWA-driven widgets](https://learn.microsoft.com/en-us/microsoft-edge/progressive-web-apps/how-to/widgets)（learn.microsoft.com）
- [PWA-driven Widgets explainer](https://microsoftedge.github.io/MSEdgeExplainers/PWAWidgets/)（microsoftedge.github.io）