# 安装后的应用快捷方式

> manifest 的 shortcuts 菜单为何只存在于已安装应用的图标上，哪些启动器显示它，URL 如何解析，以及应用如何得知由哪个快捷方式启动。

应用快捷方式是 manifest `shortcuts` 成员中的条目，用户右键或长按已安装 web 应用的图标时由操作系统列出；菜单属于图标，所以应用安装之前不存在任何快捷方式。Android 上的 Chrome 85、桌面端 Chrome 与 Edge 96、Samsung Internet 14、macOS 上的 Safari 17.4 会读取该成员；iOS 上的 Safari 与 Firefox 忽略它（BCD `html.manifest.shortcuts`）。

## 工作原理

安装时，浏览器把 `shortcuts` 复制进平台自己的快捷方式机制：Windows 跳转列表（右键任务栏或开始菜单图标）、macOS Dock 图标的上下文菜单、ChromeOS 任务栏菜单，或 Android 的启动器快捷方式（长按主屏幕或应用抽屉图标）。这些条目是静态副本，manifest 改动要等浏览器下一次 manifest 更新检查后才会反映到菜单里，而不是下一次页面加载。

每个快捷方式对象包含 `name`（必需）、`url`（必需），以及可选的 `short_name`、`description` 与 `icons`。`url` 相对 manifest 自身的 URL 解析，而不是相对页面或 `start_url`：位于 `/dashboard/manifest.json` 的 manifest 写 `"url": "../projects"` 会打开 `/projects`。解析后的 URL 必须落在 manifest `scope` 内；超出 scope 的条目会被浏览器直接丢弃而不报错。规范没有数量上限，但 Windows 跳转列表与 Android 启动器只显示前几项，所以最常用的操作应排在数组最前。

快捷方式一旦打开就没有任何特殊之处：应用在它的 standalone 窗口中收到一次指向快捷方式 URL 的普通导航。因此在不支持该成员的地方，快捷方式不花任何代价（同样的 URL 在应用内仍可作为链接使用）；在支持的地方，则多了一个启动器级别的入口。

:::observed
在 Windows 11（英文界面）上，右键 Chrome 安装的 PWA 的任务栏图标，跳转列表的 "Tasks" 分组按 `name` 列出 manifest 快捷方式，每项以其 `icons` 条目渲染为跳转列表尺寸，位于固定的 "Pin to taskbar" 与 "Close window" 之上；没有 `shortcuts` 的 manifest 只显示固定项。在 Android 上的 Chrome 中，长按 WebAPK 图标会列出同样的 `name` 值，把其中一项拖到主屏幕会生成一个独立的固定快捷方式，直接打开该快捷方式的 `url`。
:::

## 示例

manifest 片段声明两个快捷方式；脚本展示应用如何得知由哪一个启动，以及在不支持该成员时会怎样。

### 声明在应用 scope 内解析的快捷方式

manifest 位于 `/app/manifest.webmanifest`，所以相对 URL 解析到 `/app/` 之下。每项带 `?source=shortcut` 查询参数，便于应用归因启动来源。

```json
{
  "name": "Calendar",
  "start_url": "/app/",
  "scope": "/app/",
  "display": "standalone",
  "shortcuts": [
    {
      "name": "New event",
      "short_name": "New",
      "description": "Create an event starting now",
      "url": "new-event?source=shortcut",
      "icons": [{ "src": "icons/new-event-96.png", "sizes": "96x96", "type": "image/png" }]
    },
    {
      "name": "Today",
      "url": "today?source=shortcut",
      "icons": [{ "src": "icons/today-96.png", "sizes": "96x96", "type": "image/png" }]
    }
  ]
}
```

若改写成 `"url": "/new-event"`，它会解析到 `/app/` 之外，浏览器会悄悄丢掉这个快捷方式；图标保持 96×96 px 与 Chrome 文档中 Android 快捷方式图标的尺寸一致。

### 判断由哪个快捷方式启动，并为无快捷方式的浏览器回退

应用在启动时读取 `location.pathname` 与 `source` 查询参数。在不支持 `shortcuts` 的地方（iOS 上的 Safari、Firefox），同样的 URL 仍可作为应用内链接访问，所以应用改在自己的菜单里展示它们。

```js
const url = new URL(location.href);
const fromShortcut = url.searchParams.get('source') === 'shortcut';

if (fromShortcut && url.pathname.endsWith('/new-event')) {
  openEventEditor({ startsAt: Date.now() });
  analytics.track('launch_shortcut', { shortcut: 'new-event' });
}

// 回退：iOS Safari 与 Firefox 没有启动器菜单，把同样的操作放进应用内。
// `display-mode` 能告诉我们应用是否以安装态运行。
const installed = matchMedia('(display-mode: standalone)').matches || navigator.standalone === true;
if (!installed || /iPhone|iPad|Firefox/.test(navigator.userAgent)) {
  renderQuickActions([
    { label: 'New event', href: '/app/new-event?source=menu' },
    { label: 'Today', href: '/app/today?source=menu' },
  ]);
}
```

`source` 查询参数是唯一可靠的归因手段：启动器发起的导航没有 referrer，`document.referrer` 无法区分快捷方式与手动输入的 URL。

## 另请参阅

- [Web Application Manifest: shortcuts member](https://www.w3.org/TR/appmanifest/#shortcuts-member)（w3.org）
- [Expose common app actions as shortcuts](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/How_to/Expose_common_actions_as_shortcuts)（developer.mozilla.org）
- [`shortcuts` manifest 成员](/zh/reference/manifest/shortcuts/)
- [`shortcuts` 浏览器支持](/zh/compatibility/manifest-shortcuts/)
- [beforeinstallprompt 事件](/zh/reference/installation/install-prompt/)
- [`scope` manifest 成员](/zh/reference/manifest/scope/)