# 测试 PWA

> 在 Chrome DevTools 中测试 PWA：读懂 Installability 小节，从 Service workers 面板驱动 Service Worker，并在真机上安装。

完成本指南后，你能在 Chrome DevTools 里回答一个页面为什么可装或不可装，按需让 Service Worker 走过
install、waiting 与 activation，不拔网线就模拟离线，并在一台真实的 Android 设备上确认安装流程。
这些都在 **Application** 面板里完成；Lighthouse 的 PWA 类目已弃用，不在流程之内。

你需要通过 HTTPS 或 `localhost` 提供的应用、带 DevTools 的 Chrome，最后一步还需要一台开启了 USB 调试的
Android 手机。下文的名称均为 DevTools 的英文界面标签。

## 1. 读 manifest 与它的 Installability 小节

打开 DevTools，选择 **Application**，再选 **Manifest**。**Identity** 与 **Presentation** 小节以可读形式
显示 manifest 字段，**Icons** 列出每个声明的图标（**Show only the minimum safe area for maskable icons**
复选框预览裁切后的安全区），随后是 **Shortcut #N** 与 **Screenshot #N** 小节。图标加载失败或缺少必需字段时，
会出现一个 **Installability** 小节给出原因。点击地址栏的 **Install** 按钮时保持 **Console** 抽屉打开：
它会记录 manifest 问题与安装生命周期。

能通过的 manifest 至少包含这些：

```json
{
  "name": "Example PWA",
  "short_name": "Example",
  "start_url": "/",
  "scope": "/",
  "display": "standalone",
  "icons": [
    { "src": "/icon-192.png", "sizes": "192x192", "type": "image/png" },
    { "src": "/icon-512.png", "sizes": "512x512", "type": "image/png" }
  ]
}
```

完整条件见[可安装性条件：PWA 如何才能被安装](/zh/reference/installation/installability-criteria/)。

## 2. 从 Service workers 面板驱动 worker

**Application** > **Service workers** 列出当前页面的注册，带 **Source**、**Status** 与 **Clients** 行。
各控件及其用途：

- **Offline** 让 DevTools 进入离线模式，与 Network 面板的 **Offline** 节流预设相同。勾选后重新加载，
  就能看到 worker 在没有网络时提供什么。
- **Update on reload** 在每次导航时重新抓取 worker 脚本，即使逐字节相同也当作新版本安装，于是 `install`
  再次运行，然后跳过等待阶段并导航。它免去了编辑 `sw.js` 时「关掉所有标签页」这一步；测试真实更新路径
  之前要关掉它。
- **Bypass for network** 把请求直接送到网络，用来判断 bug 在 worker 里还是在页面里。
- **Push** 与 **Sync** 不需要服务器就能触发带有所填载荷的 `push` 事件和一个 `sync` 事件。
- **stop** 停止正在运行的 worker；下一个事件会以全新的全局状态启动它，能暴露出假定内存变量会一直存在的代码。
- **Unregister** 移除注册；**Update** 执行一次性的更新检查。

**Update Cycle** 表格显示 worker 的各项活动及耗时，例如 install、wait 与 activate；**Expand** 按钮展开
精确时间戳。

:::observed
编辑 `sw.js` 后，在未勾选 **Update on reload** 的情况下重新加载一次，面板会显示两条记录：旧 worker 的
**Status** 为 `activated and is running`，新 worker 为 `waiting to activate`，旁边有一个 **skipWaiting**
链接（英文界面）。点击该链接无需关闭标签页即可激活新 worker，效果等同于在 worker 内调用 `self.skipWaiting()`。
:::

## 3. 断言之前先检测注册

假定 `navigator.serviceWorker` 存在的测试脚手架在没有该 API 的浏览器里会抛错，而对应 scope 下没有注册时
`getRegistration()` 解析为 `undefined`。两者都检查，脚手架就会给出原因而不是一串堆栈。

```js
export async function serviceWorkerState() {
  if (!('serviceWorker' in navigator)) {
    return { ok: false, reason: 'Service workers are not supported in this browser.' };
  }
  const registration = await navigator.serviceWorker.getRegistration();
  if (!registration) {
    return { ok: false, reason: 'No service worker is registered for this page yet.' };
  }
  const worker = registration.active || registration.waiting || registration.installing;
  return { ok: true, state: worker ? worker.state : 'unknown', scope: registration.scope };
}
```

在 Console 里运行：首次安装完成后，`(await serviceWorkerState()).state` 打印 `activated`。

## 4. 通过远程调试在真机上安装

桌面版 Chrome 的 **Install** 按钮无法重现 Android 的流程（WebAPK 生成、主屏幕图标、启动画面），
曾经覆盖这部分的 Lighthouse PWA 审计也已弃用，取而代之的是可安装性条件与这个面板。用 USB 连接手机，
在桌面端打开 `chrome://inspect/#devices`，选中页面，在手机的 Chrome 里打开三点菜单并选择 **Install app**
（部分设备上为 **Add to Home screen**）。连接到手机的桌面 DevTools 实例显示同样的 **Manifest** 与
**Service workers** 面板，所以第 1 步的 Installability 小节适用于你手里的这台设备。当图标出现在主屏幕、
应用以 manifest 的 `theme_color` 全屏启动时，安装路径就验证完毕。

## 另请参阅

- [Service Worker 是什么，以及最小可用的那一个](/zh/reference/service-worker/)
- [Service worker 调试](/zh/reference/service-worker/debugging/)
- [调试 PWA](/zh/guides/debugging/)
- [可安装性条件：PWA 如何才能被安装](/zh/reference/installation/installability-criteria/)
- [Debug Progressive Web Apps](https://developer.chrome.com/docs/devtools/progressive-web-apps/)（developer.chrome.com）
- [The service worker lifecycle](https://web.dev/articles/service-worker-lifecycle)（web.dev）
- [ServiceWorkerContainer: getRegistration() method](https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerContainer/getRegistration)（developer.mozilla.org）

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