首页
/ Puppeteer Browser.targets() 完全解析:枚举浏览器全部 Target 的 API、源码实现与实战用法

Puppeteer Browser.targets() 完全解析:枚举浏览器全部 Target 的 API、源码实现与实战用法

2026-09-04 09:36:09作者:申梦珏Efrain

Browser.targets() 是 Puppeteer 中用于枚举当前浏览器实例下所有活动 Target 的核心同步 API。本文基于当前仓库中的 API 文档 docs/api/puppeteer.browser.targets.md,结合 packages/puppeteer-core 的 CDP 与 BiDi 两套实现源码,完整讲解该方法的签名、返回值语义、底层实现差异(Chrome DevTools Protocol 与 WebDriver BiDi 两种连接模式),以及它在检测扩展程序、监控新窗口、配合 waitForTarget 做自动化场景中的实战用法。读完本文,你可以准确判断哪些 Target 会被返回、如何将其转换为 Page/WebWorker 操作对象,并理解 browser.targets()browserContext.targets() 的包含关系。

一、方法定位与官方文档定义

Browser.targets() 定义于 Puppeteer 的核心 API 抽象类 Browser 上。官方文档(Browser.targets 文档)给出的定义非常简洁:

Gets all active targets. In case of multiple browser contexts, this returns all targets in all browser contexts.

即:获取所有活动的 Target;当浏览器存在多个 BrowserContext 时,返回所有 BrowserContext 中的全部 Target

其 TypeScript 签名为:

class Browser {
  abstract targets(): Target[];
}

返回值: Target[]Target 类型 的数组)。

这个定义对应仓库中的抽象声明,位于 Browser.targets 抽象方法

/**
 * Gets all active {@link Target | targets}.
 *
 * In case of multiple {@link BrowserContext | browser contexts}, this returns
 * all {@link Target | targets} in all
 * {@link BrowserContext | browser contexts}.
 */
abstract targets(): Target[];

三个关键语义可以从这里提炼出来:

  1. 它是同步方法——没有 Promise 返回类型,因为 Target 列表完全来自 Puppeteer 进程内维护的连接状态(本地缓存),无需向浏览器发起新的协议请求;
  2. 它是"活动"(active)Target 的集合——已经关闭或尚未初始化完成的 Target 不会出现在结果里(CSP 实现细节见下文);
  3. 它是全局视角——跨越该 Browser 实例下的所有 BrowserContext,这与单个上下文视角的 BrowserContext.targets() 形成互补。

二、Target 是什么:类型系统与可执行操作

理解 targets() 的返回值,必须先理解 Target 抽象类。源码注释给出了定义:

Target represents a CDP target. In CDP a target is something that can be debugged, such as a frame, a page or a worker.

即 Target 是一个可被调试的浏览器执行单元。每个 Target 都有一个类型,由 TargetType 枚举描述(TargetType 定义):

枚举值 字符串值 含义
TargetType.PAGE 'page' 普通页面(标签页)
TargetType.BACKGROUND_PAGE 'background_page' 后台页面(典型如 Chrome 扩展的后台页)
TargetType.SERVICE_WORKER 'service_worker' Service Worker
TargetType.SHARED_WORKER 'shared_worker' SharedWorker
TargetType.BROWSER 'browser' 浏览器实例本身
TargetType.WEBVIEW 'webview' <webview> 嵌入视图
TargetType.OTHER 'other' 其他未归类目标
TargetType.TAB 'tab' 内部类型(标记为 @internal

每个 Target 实例提供一组将"调试目标"转换为"可操作句柄"的方法(Target 类方法):

  • type(): TargetType —— 返回类型枚举,是 targets() 结果最常见的第一判断条件;
  • url(): string —— 目标当前 URL;
  • page(): Promise<Page | null> —— 若类型为 pagewebviewbackground_page,返回对应 Page 实例,否则返回 null
  • worker(): Promise<WebWorker | null> —— 若类型为 service_workershared_worker,返回 WebWorker 实例;
  • asPage(): Promise<Page> —— 强制把任意类型的 Target 当作页面处理,文档注释明确其用途:"It is useful if you want to handle a CDP target of type other as a page";
  • createCDPSession(): Promise<CDPSession> —— 为该 Target 创建 CDP 会话(详见 Target.createCDPSession 文档);
  • browser(): Browser / browserContext(): BrowserContext —— 反查所属浏览器实例与上下文;
  • opener(): Target | undefined —— 返回打开该 Target 的父 Target,顶层 Target 返回 undefined

这意味着 browser.targets() 的典型消费模式是:先按 type() 过滤,再按类型调用 page()/worker()/asPage() 拿到操作对象

三、源码级实现:CDP 与 BiDi 两条路径

targets() 是抽象方法,仓库中存在两处具体实现,分别对应 Puppeteer 的两种协议模式(当前版本同时支持 CDP 与 WebDriver BiDi 连接)。

3.1 CDP 实现:基于 TargetManager 的过滤

CDP 模式下的实现在 CdpBrowser.targets()

override targets(): CdpTarget[] {
  return Array.from(
    this.#targetManager.getAvailableTargets().values(),
  ).filter(target => {
    return (
      target._isTargetExposed() &&
      target._initializedDeferred.value() === InitializationStatus.SUCCESS
    );
  });
}

从源码结构看,实现分两层:

  1. 数据来源this.#targetManager.getAvailableTargets()。TargetManager 是 CDP 模式下持续监听 Target.targetCreated / Target.targetDestroyed / Target.targetInfoChanged 等协议事件、维护 Target 生命周期表的组件,因此 targets() 无需发起新的协议请求即可同步返回;
  2. 两道过滤
    • _isTargetExposed() 为假时过滤掉——即尚未"暴露"给用户层的 Target(例如某些内部目标);
    • 初始化状态必须为 InitializationStatus.SUCCESS——仍在初始化中(或初始化失败)的 Target 不会出现在结果里。

这解释了文档中 "active targets" 一词的精确含义:结果只包含已向用户层暴露且初始化成功的 Target

同文件紧邻的 target() 方法(CdpBrowser.target())展示了 targets() 的一个直接消费者——从结果中找 type() === 'browser' 的条目:

override target(): CdpTarget {
  const browserTarget = this.targets().find(target => {
    return target.type() === 'browser';
  });
  if (!browserTarget) {
    throw new Error('Browser target is not found');
  }
  return browserTarget;
}

3.2 BiDi 实现:自身 Target + 各上下文 Target 的扁平合并

BiDi(WebDriver BiDi)模式下的实现在 BidiBrowser.targets()

override targets(): Target[] {
  return [
    this.#target,
    ...this.browserContexts().flatMap(context => {
      return context.targets();
    }),
  ];
}

实现思路与文档描述完全一致:先放入浏览器自身的 Target(this.#target,即 BidiBrowserTarget 对应对象),再对 browserContexts() 返回的每个上下文调用其 targets() 并扁平合并。BiDi 模式下每个 BrowserContext.targets() 返回该上下文内由会话(session)映射出的 Target 列表,因此 Browser.targets() 天然就是"所有上下文的并集加上浏览器自身"。

3.3 BrowserContext 视角:CDP 模式的子集过滤

BrowserContext.targets 文档 描述的是单上下文版本。在 CDP 模式中,其实现非常直接——就是在全局结果上做上下文过滤(CdpBrowserContext.targets()):

override targets(): CdpTarget[] {
  return this.#browser.targets().filter(target => {
    return target.browserContext() === this;
  });
}

由此可见两者关系:browser.targets() 是全集,browserContext.targets() 是按 target.browserContext() 过滤后的子集。当你只想操作默认上下文(或某个 createBrowserContext() 创建的隔离上下文)中的页面时,应使用上下文版本;需要跨上下文盘点(例如审计所有上下文的 Service Worker)时才用 Browser.targets()

四、实战用法与仓库测试中的真实案例

4.1 枚举全部 Target 并按类型分组

最基础的用法是把返回值按 type() 分组,这是诊断"当前浏览器里到底跑着什么"的常用手段:

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch();
const targets = browser.targets();

for (const target of targets) {
  console.log(target.type(), target.url());
}
await browser.close();

由于 targets() 是同步方法且返回的是调用时刻的快照Array.from(...) 拷贝后的新数组),后续 Target 的新建/销毁不会反映在这个数组上;需要持续跟踪时请监听 targetcreated / targetdestroyed 事件(见 BrowserEvent 文档)。

4.2 把 Target 转为 Page / WebWorker

for (const target of browser.targets()) {
  switch (target.type()) {
    case 'page':
    case 'background_page': {
      const page = await target.page();
      if (page) console.log('page:', page.url());
      break;
    }
    case 'service_worker':
    case 'shared_worker': {
      const worker = await target.worker();
      if (worker) console.log('worker:', worker.url());
      break;
    }
    case 'other':
      // 文档建议:用 asPage() 将类型未知的 target 当作页面处理
      const forcedPage = await target.asPage();
      console.log('other:', await forcedPage.title());
      break;
  }
}

注意 page() 对非页面类型、worker() 对非 Worker 类型均返回 null,因此转换前先看类型可以避免无谓的调用。

4.3 仓库测试中的真实用例:定位扩展程序 Target

Puppeteer 自身的测试套件大量使用 browser.targets()。以扩展程序测试为例(extensions.test.ts),其典型模式是:启动加载了扩展的浏览器后,调用 browser.targets() 并从中 find 出扩展对应的 Target,再转成 Page 或 CDP 会话来验证扩展行为。该测试文件中 browser.targets() 出现十余处,覆盖扩展页、后台页、内容脚本注入等多种断言路径。这说明 targets() 是 Puppeteer 处理 Chrome 扩展自动化(参见 chrome-extensions 指南)的底层枚举入口。

另一个测试场景在 launcher.test.ts,在浏览器启动后枚举 Target 验证启动参数生效情况。

4.4 与 waitForTarget 配合:从"快照"到"等待"

targets() 只回答"现在有什么",而等待某个 Target 出现是另一个高频需求。仓库中 Browser.waitForTarget 的实现正好展示了二者的衔接方式:

async waitForTarget(
  predicate: (x: Target) => boolean | Promise<boolean>,
  options: WaitForTargetOptions = {},
): Promise<Target> {
  const {timeout: ms = 30000, signal} = options;
  return await firstValueFrom(
    merge(
      fromEmitterEvent(this, BrowserEvent.TargetCreated),
      fromEmitterEvent(this, BrowserEvent.TargetChanged),
      from(this.targets()), // 先检查当前已存在的 target
    ).pipe(
      filterAsync(predicate),
      ...

逻辑是:this.targets() 的当前快照与后续的 targetcreated / targetchanged 事件流合并,对满足谓词的第一个 Target 立即返回。其源码注释中自带的示例(同样收录在 waitForTarget 文档):

await page.evaluate(() => window.open('https://www.example.com/'));
const newWindowTarget = await browser.waitForTarget(
  target => target.url() === 'https://www.example.com/',
);

因此实践建议是:一次性盘点用 targets(),等待动态产生的目标(弹窗、window.open、扩展触发的后台页)用 waitForTarget,二者共用同一谓词风格(基于 target.url()target.type() 等属性判断)。

五、相关 API 速查

API 文档 视角 / 用途
Browser.targets() browser.targets 文档 全部上下文的活动 Target 全集(同步快照)
Browser.target() browser.target 文档 默认上下文中代表浏览器自身的 Target
BrowserContext.targets() browsercontext.targets 文档 单个上下文的 Target 子集
Browser.waitForTarget(predicate) browser.waitfortarget 文档 等待满足条件的 Target 出现(默认超时 30 秒)
Target.page() / worker() / asPage() target 文档 将 Target 转换为可操作句柄
Browser.pages() browser.pages 文档 仅返回已就绪的 Page 实例(异步)

六、小结

  • Browser.targets()同步方法,返回 Target[] 快照,覆盖该浏览器实例下所有 BrowserContext 中已暴露且初始化成功的全部活动 Target;
  • CDP 实现(cdp/Browser.ts)依赖 TargetManager 的本地状态并做 exposed + initialized 双重过滤;BiDi 实现(bidi/Browser.ts)则是浏览器自身 Target 与各上下文 Target 的扁平合并——两种模式下"多上下文返回全部 Target"的文档语义均成立;
  • 消费返回值的标准路径是 type() 过滤 + page() / worker() / asPage() 转换;
  • 快照不追踪变化:需要持续监控时监听 targetcreated 事件,等待新目标出现时使用 waitForTarget
  • 跨上下文审计、扩展程序自动化(如 extensions.test.ts 的做法)是该 API 在仓库中最典型的落地场景。
登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
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
590
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
889
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
982
503
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384