首页
/ Puppeteer BrowserContext.targets():获取并处理浏览器上下文中所有活跃 Target

Puppeteer BrowserContext.targets():获取并处理浏览器上下文中所有活跃 Target

2026-09-06 18:57:57作者:柏廷章Berta

BrowserContext.targets() 是 Puppeteer 中浏览上下文(BrowserContext)上的一个同步方法,用于一次性拿到当前上下文内所有活跃的 Target(页面、Web Worker、背景页等可调试对象)。本篇以 Puppeteer 仓库中的 API 文档为骨架,深入其 CDP 与 WebDriver BiDi 两套协议实现,讲清 targets() 的签名语义、返回值构成、与 pages()/waitForTarget() 的协作关系,帮助你在自动化脚本中正确枚举、过滤和等待上下文内的目标对象。

方法签名与返回值

根据 API 文档,该方法的定义如下:

class BrowserContext {
  abstract targets(): Target[];
}
  • 功能:获取当前浏览器上下文内所有活跃的 Target
  • 参数:无;
  • 返回值Target[],一个包含该上下文中全部活跃 Target 实例的数组(同步返回,不产生 Promise);
  • 声明位置抽象声明 位于 BrowserContext 基类中,具体行为由各协议后端(CDP / BiDi)实现类覆写。

BrowserContext 本身表示浏览器中相互隔离的用户环境:启动浏览器后至少存在一个默认上下文,其余可通过 browser.createBrowserContext() 创建(在 Chrome 中所有非默认上下文均为 incognito 模式)。每个上下文拥有独立的存储(cookies/localStorage 等),targets() 正是以这个隔离边界为范围进行枚举的。

什么是 Target

理解 targets() 的返回值,先要看 Target 抽象类:它代表一个 CDP target,即“任何可以被调试的对象”,例如页面、frame 或 worker。Target 类型由 TargetType 枚举定义(定义位置):

枚举值 字面量 含义
PAGE 'page' 普通页面
BACKGROUND_PAGE 'background_page' 扩展/后台背景页
SERVICE_WORKER 'service_worker' Service Worker
SHARED_WORKER 'shared_worker' Shared Worker
BROWSER 'browser' 浏览器自身
WEBVIEW 'webview' WebView
OTHER 'other' 其他类型
TAB 'tab' 内部使用的 tab 目标

每个 Target 实例提供了若干关键方法(见 Target 类实现源码):

  • type():返回 TargetType,是过滤 targets() 结果的首要依据;
  • url():返回目标当前 URL;
  • page():当类型为 'page''webview''background_page' 时返回 Page,否则返回 null
  • worker():当类型为 'service_worker''shared_worker' 时返回 WebWorker,否则返回 null
  • asPage():强制将任意类型的 target 当作 page 处理(适合处理 other 类型目标);
  • opener():返回打开当前 target 的上级 target,顶层目标返回 null
  • browserContext() / browser():返回所属上下文与浏览器,可用于反向归属判断。

典型用法是遍历并做类型分派:

const context = browser.defaultBrowserContext();
for (const target of context.targets()) {
  if (target.type() === 'page') {
    console.log(target.url());
  } else if (target.type() === 'service_worker') {
    const worker = await target.worker();
    // 处理 worker ...
  }
}

源码实现:CDP 后端的过滤链

在 CDP 实现中,CdpBrowserContext.targets() 的实现只有一行过滤逻辑:

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

即:先从所属 CdpBrowser 拿到浏览器级全量 target,再按“目标所属上下文 === 当前上下文”做引用相等过滤。这就解释了 Browser.targets()BrowserContext.targets() 的边界关系:前者覆盖所有上下文,后者是前者的子集。

而浏览器层的 CdpBrowser.targets() 还叠加了两道筛选条件:

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

从源码结构看,只有同时满足“已被暴露(_isTargetExposed)”和“初始化成功(InitializationStatus.SUCCESS)”的目标才会出现在结果中。因此 targets() 返回的是一个稳定、已就绪的快照:正在初始化中或尚未暴露的目标不会混入,调用方无需再做初始化状态判断。

源码实现:BiDi 后端的目标集合

在 WebDriver BiDi 后端中,targets() 的数据来源完全不同。BidiBrowserContext 内部维护了一张 #targets 映射表(结构定义):

readonly #targets = new Map<
  BidiPage,
  [
    BidiPageTarget,
    Map<BidiFrame | BidiWebWorker, BidiFrameTarget | BidiWorkerTarget>,
  ]
>();

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

即“页面 → [页面 Target, {frame/worker → frame/worker Target}]”的结构,targets() 将其拍平为一维数组返回。这张表是随着页面事件动态维护的:在 #createPage 中,每当新浏览上下文出现时创建 BidiPageTarget,帧挂载(FrameAttached)时注册 BidiFrameTarget,worker 创建(WorkerCreated)时注册 BidiWorkerTarget;对应地,TargetCreatedTargetChangedTargetDestroyed 事件会在创建、导航和销毁时通过 trustedEmitter 发出。BiDi 后端因此返回的 target 集合粒度更细——不仅包含页面目标,还包含每个 frame 与 worker 对应的 target 对象。

两种实现的差异提示了一点:targets() 的具体元素构成与协议后端相关,跨后端编写脚本时尽量依赖 type()url() 等抽象方法做判断,而不是假设元素数量或类型分布。

与 pages() 的关系:pages 是 targets 的投影

BrowserContext.pages() 的文档说明“非可见页面(如 background_page)不会出现在列表中,可用 Target.page 自行查找”。在 CDP 实现中可以看到 pages() 正是对 targets() 的投影(实现位置):

override async pages(includeAll = false): Promise<Page[]> {
  const pages = await Promise.all(
    this.targets()
      .filter(target => {
        return (
          target.type() === 'page' ||
          ((target.type() === 'other' || includeAll) &&
            this.#browser._getIsPageTargetCallback()?.(target))
        );
      })
      .map(target => target.page()),
  );
  return pages.filter(page => !!page);
}

其规则是:只保留 type() === 'page' 的目标,外加经 _getIsPageTargetCallback 判定为页面性质的 'other' 目标(includeAlltrue 时放宽);随后调用 target.page() 并把返回 null 的条目剔除。因此当你需要拿到 pages() 遗漏的目标(例如后台页、无法直接映射为 Page 的对象)时,回退到 targets() + asPage() 是文档推荐的思路。

配合 waitForTarget() 等待新目标出现

targets() 是同步快照,对于“目标稍后才出现”的场景(典型如 window.open 打开弹窗),应使用同类的 waitForTarget()

async waitForTarget(
  predicate: (x: Target) => boolean | Promise<boolean>,
  options: WaitForTargetOptions = {},
): Promise<Target> {
  const {timeout: ms = 30000} = options;
  return await firstValueFrom(
    merge(
      fromEmitterEvent(this, BrowserContextEvent.TargetCreated),
      fromEmitterEvent(this, BrowserContextEvent.TargetChanged),
      from(this.targets()),
    ).pipe(filterAsync(predicate), raceWith(timeout(ms))),
  );
}

从实现可以看到三个关键细节:

  1. 默认超时 30 秒timeout: ms = 30000),可通过 options.timeout 覆盖;
  2. 它把“TargetCreated 事件流 + TargetChanged 事件流 + 当前 targets() 快照”合并为一个数据流,也就是说已经存在且满足条件的目标会立即命中,无需等待新事件;
  3. 支持同步或异步谓词(filterAsync),因此可以在谓词内调用 target.page() 等异步方法做条件判断。

官方文档给出的示例是捕获 window.open 产生的新窗口 target:

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

仓库测试 test/src/target.test.ts 中有一个更完整的异步版本,用 Promise.all 同时发起等待与打开动作,谓词内通过 target.page().then(...) 比对 URL,并在 {timeout: 3000} 下断言新 page 与 otherPage 不是同一实例——这正是 waitForTarget 的推荐写法。而 枚举场景的用例 则验证了 browser.targets() 中应同时存在 about:blank 的 page 目标与 browser 类型目标,可作为 targets() 行为的对照基准。

使用建议与边界

  • 作用域context.targets() 只返回该上下文内的目标;跨上下文枚举请使用 browser.targets()Browser.targets 文档)。
  • 快照语义:方法同步返回数组快照,不随后续页面打开/关闭而变化;实时追踪请订阅 BrowserContextEvent 中的 TargetCreated / TargetChanged / TargetDestroyed 事件(BiDi 与 CDP 后端均会发出)。
  • 就绪保证:在 CDP 后端中,结果已过滤掉未暴露或未初始化成功的目标(CdpBrowser.targets);BiDi 后端则随 #targets 映射实时增删。
  • 与页面列表的取舍:拿 Page 对象用 await context.pages();需要 worker、后台页或自定义过滤逻辑时,用 targets() 遍历后按 type() 分派 page() / worker() / asPage()

相关文件索引

内容 路径
方法 API 文档 docs/api/puppeteer.browsercontext.targets.md
Target 类文档 docs/api/puppeteer.target.md
抽象声明与 waitForTarget packages/puppeteer-core/src/api/BrowserContext.ts
Target 抽象类 packages/puppeteer-core/src/api/Target.ts
CDP 上下文实现 packages/puppeteer-core/src/cdp/BrowserContext.ts
CDP 浏览器实现 packages/puppeteer-core/src/cdp/Browser.ts
BiDi 上下文实现 packages/puppeteer-core/src/bidi/BrowserContext.ts
行为测试 test/src/target.test.ts
登录后查看全文
热门项目推荐
相关项目推荐

项目优选

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