资讯动态

Puppeteer Browser.addScreen():Headless 模式下虚拟多屏仿真的接口契约与 CDP 实现解析

发布时间:2026/9/8 22:00:30 来源:尧图企业网站定制
Puppeteer Browser.addScreen()Headless 模式下虚拟多屏仿真的接口契约与 CDP 实现解析【免费下载链接】puppeteerJavaScript API for Chrome and Firefox项目地址: https://gitcode.com/GitHub_Trending/puppeteer1/puppeteerBrowser.addScreen()是 Puppeteer 中用于在浏览器内动态创建虚拟屏幕的 Browser 级方法它接收一份 AddScreenParams 屏幕描述通过 CDP 的Emulation.addScreen命令在 Chromium 内部注册一块新屏幕并返回 ScreenInfo 屏幕信息对象。本文基于 API 文档与 puppeteer-core 源码 完整拆解该方法的签名、参数语义、返回值结构以及 CDP/WebSocket 两种连接模式下的实现差异读完后可在 headless 测试中搭建稳定的“双屏”环境配合窗口定位、跨屏拖拽等多显示器场景。方法签名与适用范围按官方 API 文档 puppeteer.browser.addscreen.md该方法用于“添加一块新屏幕并返回所添加的 screen information object”。其 TypeScript 签名在抽象基类 Browser 中声明class Browser { abstract addScreen(params: AddScreenParams): PromiseScreenInfo; }项目说明入参paramsAddScreenParams描述新屏幕的几何位置、尺寸与显示属性返回值PromiseScreenInfo解析为该屏幕的完整信息对象含由 Chromium 生成的id运行限制文档 Remarks 明确Only supported in headless mode仅支持 headless 模式“仅 headless”这一限制在源码层面有直接印证headful 模式下Browser.screens()会返回操作系统的真实屏幕信息而真实屏幕不可被注入因此addScreen这类“伪造屏幕”的能力只保留给了 headless 仿真环境。仓库的集成测试 test/src/browser.test.ts 中Browser.screens用例在 headful 环境直接抛出Not testable in headful也从侧面验证了这一约束。addScreen并非孤立方法它与Browser.screens()查询所有屏幕、Browser.removeScreen(screenId)按 id 移除屏幕构成一组屏幕仿真 API。CHANGELOG 中也有对应记录add browser.screens, .addScreen and .removeScreen methods (#14445)。从 Browser.ts 的 TSDoc 可以看到removeScreen额外注明“Fails if the primary screen id is specified”——主屏幕不可被移除这也约束了addScreen的返回值中isPrimary恒为false新添加的必然是扩展屏。AddScreenParams 参数逐项解析AddScreenParams 接口定义于 packages/puppeteer-core/src/api/Browser.ts#L315-L326共有 9 个字段其中 4 个必填export interface AddScreenParams { left: number; // 必填新屏幕左上角在虚拟桌面坐标系中的 x 坐标 top: number; // 必填新屏幕左上角的 y 坐标 width: number; // 必填屏幕物理宽度CSS 像素 height: number; // 必填屏幕物理高度CSS 像素 workAreaInsets?: WorkAreaInsets; // 可选任务栏等系统 UI 对可用区域的裁剪 devicePixelRatio?: number; // 可选设备像素比DPR rotation?: number; // 可选旋转角度 colorDepth?: number; // 可选色彩位深如 24 / 32 label?: string; // 可选屏幕标签名用于 screen.label isInternal?: boolean; // 可选是否内建屏如笔记本内屏 }其中workAreaInsets的完整定义见 Browser.ts#L305-L310export interface WorkAreaInsets { top?: number; left?: number; bottom?: number; right?: number; }各可选字段的实际效果可由仓库测试用例直接验证。test/src/browser.test.ts#L131-L166 中“should add and remove a screen”用例是最完整的实战参考const screenInfo await browser.addScreen({ left: 800, top: 0, width: 1600, height: 1200, colorDepth: 32, workAreaInsets: {bottom: 80}, label: secondary, });对应断言揭示了每个字段的落地语义workAreaInsets.bottom: 80→ 返回对象中availHeight: 11201200 − 80availWidth保持 1600即 insets 只收缩avail*系列字段不改变width/height物理尺寸label: secondary→screenInfo.label secondary未设置label的主屏默认为空字符串colorDepth: 32→ 直接透传到返回对象的colorDepth未设置devicePixelRatio→ 默认返回 1未设置rotation→orientation为{angle: 0, type: landscapePrimary}自动推导字段→isExtended: true因为已存在主屏、isPrimary: false、isInternal: false、availLeft: 800、availTop: 0并由 Chromium 生成字符串id测试用expect.any(String)匹配。left/top的坐标系语义是“整块虚拟桌面”的全局坐标headless 默认主屏占据(0,0)-(800,600)默认 viewport 800x600见 browser.test.ts#L97-L128 的默认屏断言因此示例中left: 800表示新屏紧贴主屏右侧模拟常见的双显示器并排布局。返回值 ScreenInfo 对象结构addScreen返回的 ScreenInfo 在源码中的完整定义Browser.ts#L283-L300属性类型含义left/topnumber屏幕在虚拟桌面中的原点坐标width/heightnumber物理宽高availLeft/availTopnumber可用区域原点扣除 insets 后的左上角availWidth/availHeightnumber可用区域宽高width/height减去 insetsdevicePixelRationumber设备像素比colorDepthnumber色彩位深orientationScreenOrientation含angle: number与type: string如landscapePrimaryisExtendedboolean是否为扩展屏非主屏isInternalboolean是否内建屏isPrimaryboolean是否主屏addScreen结果恒为falselabelstring屏幕标签idstringChromium 分配的屏幕 id供removeScreen(id)使用id是屏幕仿真的关键句柄addScreen的返回值是唯一的“注册凭证”后续browser.removeScreen(screenInfo.id)依赖它注销屏幕。测试用例 browser.test.ts#L161-L164 完整演示了增删闭环添加后screens()长度从 1 变为 2removeScreen(id)后再回到 1。源码实现CDP 直通 Emulation 域BiDi 未实现CDP 路径Chrome over CDPaddScreen的实现非常薄位于 packages/puppeteer-core/src/cdp/Browser.ts#L630-L640override async addScreen(params: AddScreenParams): PromiseScreenInfo { const {screenInfo} await this.#connection.send( Emulation.addScreen, params, ); return screenInfo; }可以确认参数对象原样透传给 CDP 的Emulation.addScreen命令字段名与 CDP 协议一一对应workAreaInsets、devicePixelRatio、rotation等Puppeteer 层不做任何裁剪或默认值填充默认值由 Chromium 侧补齐如devicePixelRatio: 1响应体中的screenInfo字段直接作为Promise的解析值返回因此上表中的avail*、orientation、id均由 Chromium 计算同一文件中的screens()Emulation.getScreenInfos与removeScreenEmulation.removeScreen走相同的connection.send通道三者共享同一个 browser-scoped CDP 连接。BiDi 路径WebDriver BiDi在 packages/puppeteer-core/src/bidi/Browser.ts#L335-L337 中addScreen与screens、removeScreen一起直接抛出UnsupportedOperationoverride addScreen(_params: AddScreenParams): PromiseScreenInfo { throw new UnsupportedOperation(); }也就是说当前仓库中该能力只有 CDP 连接Chrome/Chromium支持Firefox 或 BiDi 模式调用会立即抛错。这是使用该方法时的第一个硬性前提。实战搭建双屏环境并验证窗口落位仓库中最有价值的组合用法出现在Browser.get|setWindowBounds的“window maximized state”用例browser.test.ts#L198-L214先用addScreen造出副屏再用返回对象的availLeft/availTop精确计算窗口坐标把新窗口打开在第二块屏幕上// 1. 添加副屏位于主屏右侧 const screenInfo await browser.addScreen({ left: 800, top: 0, width: 1600, height: 1200, }); // 2. 利用 availLeft/availTop 定位把窗口开在副屏上 const page await context.newPage({ type: window, windowBounds: { left: screenInfo.availLeft 50, top: screenInfo.availTop 50, }, });这个模式说明addScreen的典型价值多显示器窗口管理测试windowBounds需要相对副屏的坐标ScreenInfo的availLeft/availTop正好提供副屏可用区原点避免手写魔数坐标window.devicePixelRatio/window.screen相关 Web API 的确定性验证注入指定devicePixelRatio与colorDepth的屏幕后页面内读取到的屏幕属性稳定可控不受 CI 机器真实显示环境干扰与screens()/removeScreen组合做生命周期断言如 browser.test.ts#L131-L166 所示screens()返回数组长度变化可作为添加/移除成功与否的断言依据。一个可直接复制的完整示例综合上述源码与测试证据以下示例覆盖了添加、查询、断言、清理的完整流程仅适用于 headless Chrome over CDPimport puppeteer from puppeteer; const browser await puppeteer.launch({headless: true}); // 在默认 800x600 主屏右侧添加一块 1600x1200、底部预留 80px 任务栏的副屏 const screen await browser.addScreen({ left: 800, top: 0, width: 1600, height: 1200, workAreaInsets: {bottom: 80}, devicePixelRatio: 2, label: monitor-b, }); // 查询全部屏幕[主屏, 副屏] const all await browser.screens(); console.log(all.length); // 2 // 页面内可读取到仿真屏幕属性 const page await browser.newPage(); const info await page.evaluate(() ({ label: window.screen.label, dpr: window.devicePixelRatio, })); console.log(info); // 清理用 addScreen 返回的 id 移除副屏 await browser.removeScreen(screen.id); await browser.close();关键限制与注意事项headless-only文档 Remarks 与测试用例均确认 headful 模式不可用真实屏幕不可注入CDP-onlyBiDi 实现直接抛UnsupportedOperationbidi/Browser.ts#L335-L337Firefox 用户无法使用该 API主屏不可删除removeScreen传入主屏id会失败addScreen返回的屏幕恒为isPrimary: false默认值由 Chromium 补齐devicePixelRatio缺省为 1、label缺省为空串、未旋转时orientation为{angle: 0, type: landscapePrimary}这些默认行为来自测试断言属于当前仓库版本下的可观察事实坐标系是虚拟桌面全局坐标left/top相对整块虚拟桌面原点不是相对主屏副屏通常从left 主屏宽度开始放置以避免重叠。相关 API 索引API文档说明Browser.screens()puppeteer.browser.screens.md返回全部 ScreenInfo 对象Browser.removeScreen(id)puppeteer.browser.removescreen.md按id移除屏幕headless-onlyAddScreenParamspuppeteer.addscreenparams.md入参接口ScreenInfo/WorkAreaInsetspuppeteer.screeninfo.md返回对象与 insets 子结构Browser总览puppeteer.browser.mdBrowser 类全部方法索引对应源码入口抽象声明、CDP 实现、BiDi 占位实现、集成测试。【免费下载链接】puppeteerJavaScript API for Chrome and Firefox项目地址: https://gitcode.com/GitHub_Trending/puppeteer1/puppeteer创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价