# 通过应用商店分发 PWA

> 用 Trusted Web Activity 把 PWA 封装上架 Google Play，用 PWA Builder 打包提交微软商店，并对已安装的用户隐藏安装横幅。

import { Steps } from '@astrojs/starlight/components';

**目标：** 让你已有的站点出现在应用商店里，商店安装包由它生成，而不是重写一遍。在 Android
上，其中一条路径是 Trusted Web Activity——据 Chrome for Developers，它是一种基于 Custom Tabs
协议、从你自己的 Android 应用中打开你自己的 Web 应用内容（例如你的 PWA）的方式，其中内容由
用户的浏览器渲染，与用户在浏览器中看到的完全一样，只是以全屏方式运行。在 Windows 上，据
微软，把 PWA 发布到微软商店不需要修改代码。

## 浏览器与生态支持

据 Chrome for Developers，Trusted Web Activity 在 Android 版 Chrome 72 及以上可用。据
Chrome for Developers，Trusted Web Activity 会尽量遵循用户默认选择的浏览器：若该默认浏览器
支持 Trusted Web Activity，就使用它；否则会选择任意一个已安装且支持它的浏览器。据微软，
微软商店这条路径面向 Windows，通过 Microsoft Partner Center 完成，而不是通过浏览器。

## Google Play：通过 Trusted Web Activity

据 Chrome for Developers，Trusted Web Activity 中的内容是*受信任的*：应用与它打开的站点被
预期来自同一开发者，这一点通过 Digital Asset Links 验证。该能力仅限于你自己拥有的网站，而
你通过配置这些 asset links 来证明所有权。

据 Chrome for Developers，Bubblewrap 是一组面向 Node.js 的库与命令行工具，帮助开发者用
Trusted Web Activity 在 Android 应用中生成、构建并运行 PWA。据其 README，它要求 Node.js
14.15.0 或以上。

<Steps>

1. **安装 CLI 并基于 manifest 初始化。** 据 Chrome for Developers，Bubblewrap 会读取 Web
   Manifest，请开发者确认将在 Android 项目中使用的取值，然后据此生成项目。

   ```bash
   npm i -g @bubblewrap/cli
   bubblewrap init --manifest=https://my-twa.com/manifest.json
   bubblewrap build
   ```

2. **安装构建产物进行测试。** 据 Chrome for Developers，构建步骤输出
   `app-release-signed.apk`，该文件可安装到开发设备上测试，也可上传到 Play Store 发布。据
   Chrome for Developers，`bubblewrap install` 会把它安装到已连接的设备上，`adb install
   app-release-signed.apk` 同理。

3. **发布 `assetlinks.json`。** 据 Chrome for Developers，Digital Asset Links 本质上由两部分
   组成：你网站上一个指向应用的文件，以及应用中指向网站的元数据；把该文件上传到相对站点根
   目录的 `.well-known/assetlinks.json`，浏览器才能正确验证。

4. **先做应用图标，再部署。** 据 Chrome for Developers，快速上手之后的下一步是为你的应用
   创建一个图标；完成之后，你就可以考虑把应用部署到 Play Store。

</Steps>

据 Chrome for Developers，PWA Builder 提供图形界面，底层使用 Bubblewrap 库来驱动 Trusted
Web Activity 项目的生成。

## 微软商店：通过 PWA Builder

据微软，把 PWA 发布到微软商店不需要修改代码：你在 Microsoft Partner Center 创建应用预留
（app reservation），用 PWA Builder 打包你的 PWA，然后把包提交到商店。据微软，它列出的优势
包括：与其他 Windows 应用并列的可发现性、Windows 用户对商店下载的信任（这些下载遵循微软商店
政策）、一致的安装体验，以及 Partner Center 面板中关于应用健康状况与使用情况的分析数据。

## 在网页中检测商店版本，并做好回退

当同一个产品同时存在站点与商店条目时，网页版就不该继续推销用户已经完成的安装。据 MDN，
`navigator.getInstalledRelatedApps()` 返回一个 Promise，解析为一个数组，其中的对象代表用户
已安装的相关平台特定应用或 PWA；MDN 正是以此为例：当应用已安装时，移除「安装我们的 App」
横幅。

```js
async function shouldShowInstallBanner() {
  if (!('getInstalledRelatedApps' in navigator)) {
    // 此处不支持——直接展示横幅，而不是把一个可用的行动号召
    // 藏在浏览器根本无法回答的检测背后。
    return true;
  }
  const related = await navigator.getInstalledRelatedApps();
  return related.length === 0;
}
```

据 MDN，这只有在两件事都配置好时才生效：调用方 Web 应用必须出现在其 manifest 的
`related_applications` 成员中，且该平台特定应用或 PWA 必须反向声明这层关系——Android 应用
通过 Digital Asset Links 系统，Windows UWP 应用通过 URI Handlers。

## 实践清单

- **签名密钥不匹配会把你的应用降级为 Custom Tab。** 据 Chrome for Developers，Digital Asset Links 会
  考虑 APK 的签名密钥，而验证失败的一个常见原因就是使用了错误的签名——验证失败意味着你的
  站点会以顶部带浏览器 UI 的 Custom Tab 启动，而不是 Trusted Web Activity。
- **Google Play 持有的密钥可能与 Bubblewrap 不同。** 据 Chrome for Developers，Bubblewrap
  使用 `init` 阶段设置的密钥构建；但当你在 Google Play 发布时，根据你选择的签名处理方式，
  可能会为你另外创建一个密钥。请按 Play 实际发布所用的密钥核对 asset links。
- **在配置 asset links 之前，它还不是 TWA。** 据 Chrome for Developers，首次运行时你会发现
  网站是以 Custom Tab 而非 Trusted Web Activity 启动的，因为 Digital Asset Links 验证尚未
  配置。
- **宿主应用读不到你的 Web 状态。** 据 Chrome for Developers，宿主应用无法直接访问 Trusted
  Web Activity 中的网页内容，也无法访问 cookie、`localStorage` 之类的 Web 状态。原生与网页
  之间的数据传递应通过 URL 完成。
- **Web 体验就是产品本身。** 据 Chrome for Developers，内容由用户的浏览器渲染，与在浏览器中
  完全一样——站点上坏掉的东西，在商店版本里同样是坏的。
- **`getInstalledRelatedApps()` 并非处处可靠。** 据 MDN，它是实验性的、并非 Baseline，因为
  它在一些使用最广泛的浏览器中并不工作；而且必须在顶层安全上下文中调用——不能在 `<iframe>`
  内部。

## 下一步

- [Trusted Web Activity 参考](/zh/reference/installation/twa/) —— 以参考形式呈现的机制。
- [`related_applications`](/zh/reference/manifest/related-applications/) —— 上面的检测所
  依赖的 manifest 成员。
- [`getInstalledRelatedApps()`](/zh/reference/installation/get-installed-related-apps/) ——
  该 API 的独立条目，包含它的各项约束。
- [可安装性标准](/zh/reference/installation/installability-criteria/) —— 浏览器自身在提供
  安装之前要求什么。

← 返回[指南](/zh/guides/)总览。