首页
/ Puppeteer PWADisplayMode 类型详解:控制安装后 Web 应用以独立窗口还是浏览器标签页打开

Puppeteer PWADisplayMode 类型详解:控制安装后 Web 应用以独立窗口还是浏览器标签页打开

2026-09-07 23:46:10作者:幸俭卉

本文围绕 Puppeteer 中 PWADisplayMode 联合类型的定义、语义与真实用法展开,说明它如何通过 Browser.installPWA()displayMode 参数决定"已安装的 Web 应用(PWA)在用户点击时以独立窗口还是浏览器标签页打开"。阅读本文后,你将掌握该类型的字面量取值、底层 CDP 调用链(PWA.install + PWA.changeAppUserSettings)、必须在 pipe 连接下使用的限制,以及一套可复制的"安装 → 启动 → 校验显示模式 → 卸载"完整流程。

一、类型定义:两个字面量的联合

PWADisplayMode 是本仓库 API 文档 docs/api/puppeteer.pwadisplaymode.md 定义的一个 TypeScript 联合类型,用于表达"用户偏好将已安装的 Web 应用打开在独立窗口(standalone window)还是浏览器标签页(browser tab)中"。

官方类型签名为:

export type PWADisplayMode = 'standalone' | 'browser';

它与 Puppeteer 浏览器会话无关,也不描述"窗口物理形态",而是描述安装后的应用在操作系统/浏览器层面被打开时采用的展示模式,与 Web App Manifest 中 display 字段表达的概念一脉相承。

在源码层面,该类型的定义位于 packages/puppeteer-core/src/api/Browser.ts#L337-L343,标注为 @public,是面向使用者的公开 API:

/**
 * If the user prefers opening an installed web app in a standalone window or in
 * a browser tab.
 *
 * @public
 */
export type PWADisplayMode = 'standalone' | 'browser';

两个合法取值:

取值 含义
'standalone' 应用在独立的应用窗口中打开,拥有自己的窗口边框、任务栏入口等 OS 集成形态,页面上 (display-mode: standalone) 媒体查询为真
'browser' 应用作为普通浏览器标签页打开,等同于常规网页的浏览体验

二、使用场景:InstallPWAOptions.displayMode

PWADisplayMode 类型的实际消费点是 InstallPWAOptions 接口的可选属性 displayMode,该接口是 Browser.installPWA() 方法的入参。完整接口定义见 docs/api/puppeteer.installpwaoptions.md,其源码位于 packages/puppeteer-core/src/api/Browser.ts#L350-L374

export interface InstallPWAOptions {
  /** manifest 中的 id,通常是安装该 Web 应用的站点 URL */
  manifestId: string;
  /** 用于安装应用的 URL,或签名 Web Bundle 的 URL(必填) */
  installUrlOrBundleUrl: string;
  /** 应用应以独立窗口还是浏览器标签页打开 */
  displayMode?: PWADisplayMode;
}

对这三个字段的理解要点:

  • manifestId:Web App Manifest 文件中的 id 字段值,实践中通常就是安装该 Web 应用的站点 URL。它是后续 launchPWAgetPWAStateuninstallPWA 等操作的唯一凭证。
  • installUrlOrBundleUrl:之所以必填,是因为 PWA 安装走的是 browser 级 CDP 会话,该会话不关联任何页面,Chromium 无法像普通页面安装那样自行推导安装 URL,因此必须显式传入。
  • displayMode:可选。源码注释明确指出——单独的 PWA.install 调用会让应用停留在 Chromium 的默认显示模式(即 'browser');只有显式传入 displayMode,Puppeteer 才会追加一次 PWA.changeAppUserSettings 调用把偏好真正应用下去(详见下文第四部分)。

三、前置限制:仅支持 pipe 连接

installPWA(以及使用 displayMode 的完整链路)对连接方式有硬性要求,引用 docs/api/puppeteer.browser.installpwa.md 的 Remarks:

Only available when connected to the browser over a pipe connection. Set pipe: true in puppeteer.launch; the launch option defaults to false. The underlying PWA CDP domain is not exposed over a WebSocket connection.

翻译为实操结论:

  1. 底层使用的 CDP PWA不在 WebSocket 连接上暴露,因此必须走浏览器进程的管道(pipe)连接。
  2. 调用 installPWA 前必须让启动参数带上 pipe: true(见 puppeteer.launch,该启动项默认值为 false)。

这也是为什么仓库测试 test/src/cdp/pwa.test.ts#L11-L18 中用 setupSeparateTestBrowserHooks 单独拉起一个 {pipe: true} 的浏览器实例来跑 PWA 相关用例,并在注释中再次强调 "The PWA CDP domain is only available over a pipe connection."

此外还有一个值得注意的边界条件:在 packages/puppeteer-core/src/cdp/Browser.ts#L542-L547 的实现中,如果浏览器配置了网络限制(network restrictions),installPWAlaunchPWAuninstallPWA 会直接抛出 "PWA APIs are not supported when network restrictions are configured." 的错误,调用前需要确认环境没有启用此类限制。

四、底层原理:两次 CDP 调用的链式组合

displayMode 并非一次 CDP 调用就能生效。看 Chrome/Edge 实现(CDP 后端)packages/puppeteer-core/src/cdp/Browser.ts#L542-L559

override async installPWA(options: InstallPWAOptions): Promise<string> {
  if (this.#hasNetworkRestrictions) {
    throw new Error(
      'PWA APIs are not supported when network restrictions are configured.',
    );
  }
  await this.#connection.send('PWA.install', {
    manifestId: options.manifestId,
    installUrlOrBundleUrl: options.installUrlOrBundleUrl,
  });
  if (options.displayMode) {
    await this.#connection.send('PWA.changeAppUserSettings', {
      manifestId: options.manifestId,
      displayMode: options.displayMode,
    });
  }
  return options.manifestId;
}

从中可以提炼出三个源码级事实:

  1. 执行顺序:先发 PWA.install 完成应用安装,若 displayMode 存在,紧接着发 PWA.changeAppUserSettings 覆盖用户设置中的展示模式。
  2. 默认值行为:不传 displayMode 时,只执行一次 PWA.install,应用维持 Chromium 默认的 'browser' 模式。因此想得到独立窗口形态,务必显式传 displayMode: 'standalone'
  3. 返回值installPWA resolve 出的 string 就是对入参 manifestId 的原样回显(echo),可直接透传给 launchPWAgetPWAStateuninstallPWA,无需再查询 manifest。

五、完整实战:安装 standalone 应用并校验

下面这套流程与仓库测试用例 test/src/cdp/pwa.test.ts#L83-L104("installs a PWA with a standalone display mode")所验证的路径一致:安装时指定 displayMode: 'standalone',启动后用页面内 matchMedia('(display-mode: standalone)') 校验应用确实以独立窗口形态运行。

import puppeteer from 'puppeteer';

// 1. PWA 相关 API 只能在 pipe 连接下使用,必须显式开启
const browser = await puppeteer.launch({pipe: true});

// 2. manifestId 通常取站点 URL;installUrlOrBundleUrl 用于安装或指向签名 bundle
const manifestId = 'https://example.com/pwa/';
const installUrl = 'https://example.com/pwa/index.html';

// 3. 安装并把展示模式设为独立窗口
const returnedId = await browser.installPWA({
  manifestId,
  installUrlOrBundleUrl: installUrl,
  displayMode: 'standalone',
});
console.log(returnedId === manifestId); // true,返回值回显 manifestId

// 4. 启动已安装应用,得到承载应用窗口的 Page
const page = await browser.launchPWA({manifestId});

// 5. 在应用页面内确认 display-mode 生效
const isStandalone = await page.evaluate(() =>
  matchMedia('(display-mode: standalone)').matches,
);
console.log('isStandalone:', isStandalone); // true

await page.close();

// 6. 查看应用的 OS 集成状态(徽标数、注册的文件处理器)
const state = await browser.getPWAState({manifestId});
console.log('badgeCount:', state.badgeCount);
console.log('fileHandlers:', state.fileHandlers);

// 7. 清理
await browser.uninstallPWA({manifestId});
await browser.close();

关于步骤 4 中 launchPWA 的行为,docs/api/puppeteer.browser.launchpwa.md 补充了重要细节:CDP 的 PWA.launch resolve 的是被启动 tab 目标 的 id,而 Puppeteer 并不通过 Browser.targets() 暴露 tab 目标,因此 launchPWA 内部会等待该 tab 的子 page 目标(应用的实际 Web 内容)出现并 resolve 出对应的 Page。若 Chromium 选择聚焦一个已存在的应用窗口,则返回该窗口已有的 Page。实现代码见 packages/puppeteer-core/src/cdp/Browser.ts#L572-L608

launchPWA 还接受两个可选参数(见 docs/api/puppeteer.launchpwaoptions.md):

  • url:应用 scope 内要打开的 URL,缺省时使用应用的 start URL;
  • timeout:等待应用 page 目标出现的最长毫秒数,默认 30 秒,传 0 可禁用超时。

测试用例 test/src/cdp/pwa.test.ts#L70-L81 展示了显式传入 url 的启动路径。

六、与 browser 模式相关的其它验证路径

除 standalone 场景外,仓库测试 test/src/cdp/pwa.test.ts#L38-L51 还覆盖了不传 displayMode 的默认安装路径,并验证了两个状态约定:

  • 应用安装成功后,browser.getPWAState({manifestId}) 正常 resolve,此时 badgeCount0fileHandlers 是一个数组;
  • 应用卸载后,再对同一 manifestId 查询状态会 reject。

getPWAState 返回的 PWAState 接口只包含两个字段:badgeCount: number(应用图标上当前显示的徽标数)与 fileHandlers: Protocol.PWA.FileHandler[](应用向操作系统注册的文件处理器)。其入参 GetPWAStateOptions 仅有 manifestId 一个必填字段。

七、关键要点速记

  • PWADisplayMode = 'standalone' | 'browser',描述"用户偏好安装后的 Web 应用以独立窗口还是浏览器标签页打开",是 InstallPWAOptions.displayMode 字段的类型。
  • 必须 pipe 连接:PWA 系列 API 底层依赖的 CDP PWA 域不在 WebSocket 上暴露,launch 时需设 pipe: true
  • 不传即默认:省略 displayMode 时只执行 PWA.install,应用保持 Chromium 默认的 'browser' 模式;传了才会追加 PWA.changeAppUserSettings 真正生效。
  • installPWA 返回值回显传入的 manifestId,可直接用于 launchPWA / getPWAState / uninstallPWA
  • 页面上可用 matchMedia('(display-mode: standalone)').matches 反向校验展示模式是否按预期生效。
  • 网络限制环境下 PWA 系列 API 会被禁用并抛错,使用前需确认环境配置。

以上类型定义、实现与测试均来自当前仓库:类型与接口见 packages/puppeteer-core/src/api/Browser.ts,CDP 实现见 packages/puppeteer-core/src/cdp/Browser.ts,端到端验证见 test/src/cdp/pwa.test.ts,对应的 API 参考文档可继续查阅 PWADisplayModeInstallPWAOptionsBrowser.installPWA()Browser.launchPWA()Browser.getPWAState()PWAState

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

项目优选

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