首页
/ Puppeteer Browser.uninstallPWA() 深度解析:通过 CDP PWA.uninstall 卸载已安装 PWA 的完整机制

Puppeteer Browser.uninstallPWA() 深度解析:通过 CDP PWA.uninstall 卸载已安装 PWA 的完整机制

2026-09-04 09:23:07作者:裴锟轩Denise

本篇技术指南围绕 Puppeteer 的 Browser.uninstallPWA() 方法展开,完整覆盖其 API 签名、UninstallPWAOptions 参数说明与"仅限 pipe 连接"这一关键使用限制,并结合仓库中 CDP/BiDi 双协议实现与真实测试用例,讲清该方法底层如何通过 CDP PWA.uninstall 命令完成卸载、如何验证卸载结果,以及在配置网络限制(banklist/allowlist)时为何会直接抛错。读完后你将掌握:在自动化脚本中正确安装、卸载 PWA 的完整生命周期,以及如何排查 uninstallPWA() 抛出的典型异常。

1. API 定义:签名、参数与返回值

Browser.uninstallPWA() 用于卸载一个此前已安装的渐进式 Web 应用(Progressive Web App, PWA)。其类型签名(见 api/Browser.ts 的抽象定义)如下:

class Browser {
  abstract uninstallPWA(options: UninstallPWAOptions): Promise<void>;
}

参数只有一个 options,类型为 UninstallPWAOptions:

属性 类型 说明 默认值
manifestId string Web 应用 manifest 文件中的 id 无(必填)

返回值为 Promise<void>,即调用成功不产生返回值,通过 Promise 的 resolve/reject 表示卸载成败。

UninstallPWAOptions 接口的完整定义位于 api/Browser.ts:

/**
 * Options for {@link Browser.uninstallPWA}.
 *
 * @public
 */
export interface UninstallPWAOptions {
  /**
   * The id from the web app's manifest file.
   */
  manifestId: string;
}

这里有一个关键点:manifestId 就是之前调用 Browser.installPWA() 时传入并原样返回的那个 id。按照 installPWA 文档的说明,installPWA 返回的 manifest id 会回显 InstallPWAOptions.manifestId,因此它可以被直接传给 Browser.launchPWA()Browser.getPWAState()Browser.uninstallPWA()——这构成了"PWA 安装 → 查询状态 → 启动 → 卸载"完整生命周期中各 API 的参数衔接关系。仓库测试中的实际用法可以印证这一点,测试把本地测试服务器上的目录式 URL 作为 manifest id:

const manifestId = `${server.PREFIX}/pwa/`;
await browser.installPWA({
  manifestId,
  installUrlOrBundleUrl: startUrl,
});
// ...
await browser.uninstallPWA({manifestId});

对应源码见 pwa.test.ts。仓库中还提供了真实的 PWA 测试站点资产,包括 index.htmlmanifest.webmanifest,可供理解一个最小可安装 PWA 的结构。

2. 前置条件:只能通过 pipe 连接调用

uninstallPWA() 最重要的使用限制是:只有当 Puppeteer 通过 pipe 连接(进程管道)与浏览器通信时才可用api/Browser.ts 的官方 remarksinstallPWA 文档 一致地指出:

  • 需要在 puppeteer.launch 中设置 pipe: true,该 launch 选项默认为 false;
  • 底层的 CDP PWA 域不会通过 WebSocket 连接暴露,因此用 puppeteer.connect() 走 WebSocket 连接已有浏览器时,无法使用这一组 PWA API。

一个满足前置条件的最小脚本骨架如下:

import puppeteer from 'puppeteer';

// PWA 域要求 pipe 连接,默认值是 false,必须显式开启
const browser = await puppeteer.launch({pipe: true});

try {
  const manifestId = await browser.installPWA({
    manifestId: 'https://example.com/app/',
    installUrlOrBundleUrl: 'https://example.com/app/index.html',
  });

  // 卸载:参数就是安装时的 manifestId
  await browser.uninstallPWA({manifestId});
} finally {
  await browser.close();
}

仓库的 PWA 测试套件正是以同样的方式搭建环境的——通过 setupSeparateTestBrowserHooks({pipe: true}) 启动独立测试浏览器,并在源码注释中明确写道"The PWA CDP domain is only available over a pipe connection",见 pwa.test.ts

此外,从 BiDi 实现可以看到该 API 的协议边界:BidiBrowser 对 PWA 系列方法全部抛出 UnsupportedOperation(见 bidi/Browser.ts),也就是说 uninstallPWA() 目前仅在 CDP 协议(protocol === 'cdp')下可用,WebDriver BiDi 连接下调用会直接失败。

3. 源码级实现:一次 CDP PWA.uninstall 命令的发送

CDP 协议下的实现在 cdp/Browser.ts,全文只有两个逻辑分支:

override async uninstallPWA(options: UninstallPWAOptions): Promise<void> {
  if (this.#hasNetworkRestrictions) {
    throw new Error(
      'PWA APIs are not supported when network restrictions are configured.',
    );
  }
  await this.#connection.send('PWA.uninstall', {
    manifestId: options.manifestId,
  });
}

可以从中读出三层信息:

  1. 协议层极薄:uninstallPWA() 本身不维护任何本地状态,它只是把 {manifestId} 序列化后通过浏览器主连接(connection)发送一条 CDP PWA.uninstall 命令,由 Chrome 端完成实际的 PWA 卸载。Promise<void> 在浏览器确认命令执行完成后才 resolve。
  2. 网络限制前置检查:与 installPWA()launchPWA()getPWAState() 完全相同,方法开头会检查 #hasNetworkRestrictions。当启动参数中配置了 blocklistallowlist(即对页面访问施加了网络限制)时,会直接抛出错误 'PWA APIs are not supported when network restrictions are configured.',根本不会发送 CDP 命令。这一行为有专门的回归测试覆盖,见 network_restrictions.test.ts:在配置 blocklist 与配置 allowlist 两种场景下,都断言 browser.uninstallPWA({manifestId}) 会 reject 并抛出上述错误消息。
  3. 错误排查提示:如果你遇到的异常是这条消息,问题不在 manifestId,而在于 launch 参数中同时启用了网络限制——要么移除 blocklist/allowlist,要么改用无限制的浏览器实例来执行 PWA 操作。

4. 如何验证卸载结果:用 getPWAState 观察状态反转

uninstallPWA() 返回 void,不提供任何卸载产物,因此验证卸载是否真正生效的惯用手段是查询 PWA 的 OS 层状态。仓库的集成测试 "installs and uninstalls a PWA" 给出了标准范式(见 pwa.test.ts):

// 卸载前:getPWAState 正常 resolve
const installedState = await browser.getPWAState({manifestId});
expect(installedState.badgeCount).toBe(0);
expect(Array.isArray(installedState.fileHandlers)).toBe(true);

await browser.uninstallPWA({manifestId});

// 卸载后:查询应用状态应当 reject
await expect(browser.getPWAState({manifestId})).rejects.toThrow();

这个"状态反转"测试说明了卸载的语义边界:卸载成功后,Chrome 侧不再持有该 manifestId 对应的已安装应用记录,PWA.getOsAppState(即 getPWAState 底层命令,见 cdp/Browser.ts)随之失败。反过来,如果调用 uninstallPWA 之后 getPWAState 仍然成功 resolve,通常意味着卸载命令未真正完成。

同一测试文件还展示了 uninstallPWA 在"启动 PWA 后清理"场景中的典型用法:launchPWA 启动应用窗口、断言其 display-mode: standalone,在 finally 块中关闭页面并附带 .catch(() => {}) 地卸载应用,保证无论断言成败都能清理测试环境,见 pwa.test.ts

5. 与 installPWA 的参数对照及适用边界

将本文档的 uninstallPWA 与其对偶 API installPWA 放在一起看,两者的约束条件完全一致、参数形成闭环:

维度 installPWA uninstallPWA
连接方式 仅 pipe 连接(launch({pipe: true})) 仅 pipe 连接(同左)
协议 CDP(发送 PWA.install) CDP(发送 PWA.uninstall)
BiDi 支持 UnsupportedOperation UnsupportedOperation
网络限制(blocklist/allowlist) 抛错,拒绝执行 抛错,拒绝执行
核心参数 manifestId + installUrlOrBundleUrl(可选 displayMode) manifestId
返回值 Promise<string>(回显 manifestId) Promise<void>
实现位置 cdp/Browser.ts cdp/Browser.ts

适用边界总结如下:

  • 可用环境:本地启动 Chrome 且 pipe: true 的自动化场景(如 E2E 测试中安装/卸载/启动 PWA 的完整链路);
  • 不可用环境:通过 WebSocket connect 到远程浏览器、BiDi 协议连接、或配置了 blocklist/allowlist 的浏览器实例;
  • manifestId 的来源:必须与 installPWA 时传入的值一致,通常取自测试/部署服务器上的 PWA 入口 URL。

6. 相关文档与源码索引

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
588
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
906
1.83 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
891
5.79 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.53 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.34 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
988
506
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384