# 通知的 actions 与 badge 选项

> Notification 的 actions 按钮与 badge 图片选项如何工作、actions 所要求的持久化通知前提、根据 MDN 兼容性数据的当前浏览器支持情况，以及一个运行时特性检测示例。

**一句话：** `actions` 最多可以为一条通知添加 `Notification.maxActions` 个可点击按钮；`badge`
是一个独立的选项——根据 MDN，它是操作系统在 "there is not enough space to display the
notification itself"（没有足够空间展示完整通知）时展示的一张小图片，例如 Android 的通知栏。
这两者都是 Service Worker 驱动通知上的选项，并不是应用图标的 Badging API。

## 各自的作用

- **`actions`** —— 根据 MDN，这是一个只读数组，其元素为对象，包含 `action`（标识字符串）、
  `title`（按钮文案）、可选的 `icon` URL，以及可选的 `navigate` URL。根据 MDN，"browsers
  typically limit the maximum number of actions they will display"（浏览器通常会限制展示的
  按钮数量），该上限通过静态属性 `Notification.maxActions` 暴露。
- **`badge`** —— 根据 MDN，它是 "a string containing the URL of an image"（一个包含图片 URL
  的字符串），在空间不足时替代完整通知展示；MDN 针对 Android 的指引建议尺寸 "about 96 by 96
  px"（约 96×96 像素），并说明该图片 "will be automatically masked"（会被自动进行遮罩处理）。

```js
const permission = await Notification.requestPermission();
if (permission === 'granted') {
  const registration = await navigator.serviceWorker.ready;
  await registration.showNotification('New message', {
    body: 'Ada sent you a message.',
    badge: '/icons/badge-96.png',
    actions: [
      { action: 'reply', title: 'Reply' },
      { action: 'dismiss', title: 'Dismiss' },
    ],
  });
}
```

## 持久化通知前提

根据 MDN，`actions` 仅 "only for persistent notifications"（适用于持久化通知）——即通过
`ServiceWorkerRegistration.showNotification()` 展示的通知——而不适用于直接 `new
Notification()`。MDN 指出，向 `Notification()` 构造函数传入非 `null` 的 `actions` 选项会
"throws a `TypeError`"（抛出 `TypeError`）。

点击某个操作按钮后会发生什么，取决于该 action 是否设置了 `navigate` URL。根据 MDN 的
`actions` 文档，如果设置了 `navigate`，浏览器会直接导航到该 URL，并且**不会**派发
`notificationclick` 事件。只有当某个 action 没有设置 `navigate` URL 时，点击才会到达
Service Worker 的 `notificationclick` 事件，此时 `event.action` 中保存着被点击按钮的
`action` 字符串。

## 支持情况

根据上面引用的 browser-compat-data：

| 浏览器 | `actions` | `badge` |
|---|---|---|
| Chrome（桌面版） | 支持，自 53 版起 | 支持，自 53 版起 |
| Edge（桌面版） | 支持，自 18 版起 | 支持，自 18 版起 |
| Opera（桌面版） | 支持，自 39 版起 | 支持，自 39 版起 |
| Firefox（桌面版） | 支持，自 152 版起 | 不支持 |
| Safari | 不支持 | 不支持 |

根据 MDN，这两个选项都要求安全上下文。

## 如何在运行时检测

```js
function canShowActionsAndBadge() {
  if (!('serviceWorker' in navigator) || !('Notification' in window)) {
    return { actions: false, badge: false }; // 这里完全不具备持久化通知能力
  }
  return {
    actions: 'actions' in Notification.prototype,
    badge: 'badge' in Notification.prototype,
  };
}

async function notifyNewMessage(registration) {
  const { actions, badge } = canShowActionsAndBadge();
  const options = { body: 'Ada sent you a message.' };
  if (badge) {
    options.badge = '/icons/badge-96.png';
  }
  if (actions) {
    options.actions = [
      { action: 'reply', title: 'Reply' },
      { action: 'dismiss', title: 'Dismiss' },
    ];
  }
  // 当不支持 actions/badge 时，这里会改为发送仅含正文的通知——上面这些选项
  // 只是被省略，而不会导致报错。
  await registration.showNotification('New message', options);
}
```

## 常见问题

- [ ] 不要向 `new Notification()` 传入 `actions` 选项——根据 MDN，在非持久化（非 Service
      Worker）通知上这样做会抛出 `TypeError`。
- [ ] 使用 `Notification.maxActions` 而不是硬编码按钮数量；不同浏览器的上限不同。
- [ ] 不要把 `badge` 选项的小型通知栏图片与独立的应用图标 Badging API
      (`navigator.setAppBadge()`) 混淆——它们解决的是不同的问题。
- [ ] 对设置了 `navigate` URL 的 action，预期浏览器会直接导航，而不会触发
      `notificationclick` 事件；只有未设置 `navigate` 的 action 才会到达 Service Worker 的
      `notificationclick` 事件，此时才在其中读取 `event.action`。
- [ ] 依赖 `actions` 前先确认上方的支持情况表——尤其是 Firefox；两个选项在 Safari 上都不
      受支持。

## 下一步

- [Notifications API](/zh/reference/notifications/notifications-api/) —— 这两个选项所依赖的
  基础权限模型与"页面 vs Service Worker"区分。
- [应用图标徽章](/zh/reference/installation/badging/) —— 用于在已安装应用图标上设置数字的
  独立 Badging API。