ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

深入 Angular Benchpress:构建可自动化、可跨浏览器的端到端性能基准测试

2026/9/9 12:41:26 拓冰建站 浏览量
深入 Angular Benchpress:构建可自动化、可跨浏览器的端到端性能基准测试 深入 Angular Benchpress构建可自动化、可跨浏览器的端到端性能基准测试【免费下载链接】angularDeliver web apps with confidence 项目地址: https://gitcode.com/GitHub_Trending/an/angular导读Angular 官方仓库中提供了一套名为Benchpress的端到端e2e性能测试框架它通过 WebDriver 驱动真实浏览器自动读取浏览器性能日志从而量化渲染时间、垃圾回收GC开销、脚本执行时间、帧率流畅度与网络请求等超出performance.now()能力范围的指标。本文以 packages/benchpress/README.md 为骨架结合本仓库内 src 目录下的真实源码讲解 Benchpress 的动机、工作方式、基准测试写法微基准 自定义用户指标 平滑度/请求指标以及其组件化架构读完你可以在自己的 Angular 或纯前端项目里复刻这套性能基准测试流程。上图是 Benchpress 的整体协作关系Node/Dart VM 中的测试通过 Runner → Sampler → Metric / Validator / Reporter 流水线借助 WebDriverAdapter / WebDriverExtension 驱动真实浏览器Chrome、iOS Safari、Firefox最终对被测应用施加操作并产出度量值。为什么需要 Benchpress传统微基准的局限社区中大量所谓微基准micro benchmark本质上是在浏览器里用秒表计时例如调用performance.now()。这种做法只能以时间为指标少数情况下结合 Chrome 特殊 flag 测量内存无法覆盖以下关键性能维度渲染时间浏览器花在 layout 或 paint 上的耗时。例如评估样式表改动对渲染性能的影响时仅测 JS 时间是看不到效果的。垃圾回收脚本执行被 GC 暂停了多久、回收了多少内存。GC 时间极不稳定量化它有助于稳定脚本执行时间同时 GC 回收量直接与 GC 耗时挂钩可用于评估与改进应用内存占用。区分脚本执行与等待例如在复杂用户交互中只测客户端耗时而忽略后端调用占用的等待时间。FPS量化滚动与动画的流畅度。这些数据在现代浏览器的 DevTools 中已经存在但没有一种标准化方式可以自动化地跨平台采集。Benchpress 正是要填补这个空缺让各类性能指标能够以自动化方式被访问见 README 的 Why 章节。工作原理WebDriver × 性能日志 × 插件归一化Benchpress 的核心机制是用 WebDriver 读取浏览器的performance log性能日志其中包含丰富的事件流例如脚本何时开始/结束执行、GC 何时开始/结束、浏览器何时把内容绘制到屏幕等等。由于不同浏览器的日志格式与能力不同Benchpress 通过浏览器插件per-browser extension把这些事件归一化为统一格式后再交给上层指标组件使用。在源码层这一抽象体现为 web_driver_extension.ts 中定义的WebDriverExtension及其按浏览器实现的三个子类见 index.ts 的公开导出ChromeDriverExtensionIOsDriverExtensionFirefoxDriverExtensionextension 负责用 WebDriverAdapter 实现 WebDriver 协议之外的附加方法如强制 GCforce gc、以归一化格式读取性能日志。功能清单Benchpress 提供的能力在 README 的 Features 章节中有明确归纳与其源码实现一一对应采样循环Sampler自动把基准测试重复执行多次见下文架构部分。自动预热检测自动等待/检测浏览器热起来即指标值不再持续下降。统一的结果上报器Reporterconsole reporterconsole_reporter.tsfile reporter对应 json_file_reporter.tsGoogle BigQuery reporterREADME 标注为 coming soon。支持纯微基准可直接复用console.time()/console.timeEnd()产生的标记进而复用已有的 reporter、Sampler 与自动预热能力。支持的浏览器按 README 的 Supported browsers 章节当前支持范围是全平台的ChromeiOS 上的Mobile SafariFirefox进行中work in progress。源码侧与之一致Runner 默认 Providers 按ChromeDriverExtension→FirefoxDriverExtension→IOsDriverExtension的顺序通过WebDriverExtension.provideFirstSupported([...])挑选第一个受当前浏览器支持的实现见 runner.ts。如何编写一个基准测试一个 Benchpress 基准由两部分组成被测应用application under test真正要被测试的 html/css/js 应用基准驱动benchmark driver一个通过 WebDriver 与被测应用交互的测试程序。最简单的一例脚本执行时间 渲染时间假设我们要测量向容器元素填充一段复杂 HTML 字符串所消耗的脚本执行时间与渲染时间。被测应用index.html大致如下button idreset onclickreset()Reset/button button idfill onclickfill()fill innerHTML/button div idcontainer/div script var container document.getElementById(container); var complexHtmlString ...; // 一段很长的 HTML 字符串 function reset() { container.innerHTML ; } function fill() { container.innerHTML complexHtmlString; } /script对应的基准驱动则负责驱动页面并让 Runner 采样// Runner 持有共享配置可被多个测试复用 var runner new Runner(...); driver.get(http://myserver/index.html); var resetBtn driver.findElement(By.id(reset)); var fillBtn driver.findElement(By.id(fill)); runner.sample({ id: fillElement, // prepare 是可选的在每次测量前把状态重置 prepare: () { resetBtn.click(); }, execute: () { fillBtn.click(); // 注意若 fillBtn 触发的是异步逻辑 // 这里需要等待其结束再做测量 } });Runner.sample()的入参类型在 runner.ts 中有精确定义id为必填字符串execute、prepare为可选回调microMetrics、userMetrics、providers均为可选。每次sample()调用Runner 都会基于默认 Providers 与新传入的 Providers 创建全新的 Angular Injector从而隔离各次采样的配置状态。在浏览器内自行测量console.time如果被测应用想自己计时可以在页面里直接埋点例如测量 10000 次document.createElementbutton idmeasure onclickmeasure()Measure document.createElement/button script function measure() { console.time(createElement*10000); for (var i 0; i 10000; i) { document.createElement(div); } console.timeEnd(createElement*10000); } /script这里的关键点在于特殊命名约定createElement*10000告诉 Benchpress——这段被测量的时间对应的是 10000 次createElement调用因此 Benchpress 会为其换算单次平均耗时。对应驱动driver.get(.../index.html); var measureBtn driver.findElement(By.id(measure)); runner.sample({ id: createElement test, microMetrics: { createElement: time to create an element (ms) }, execute: () { measureBtn.click(); } });值得一提的是即便不用 Benchpressconsole.time()/console.timeEnd()也会在 DevTools Timeline 中留下标记方便人肉可视化理解时序。上图是 README 中展示的真实 DevTools Timeline橙色的createElement*10000测量段与其前后的 click 事件、函数调用记录共同构成一段完整时间线Benchpress 即从这类事件日志中提取度量值。不用 console.time 的自定义用户指标还可以把任意数值挂到window对象上作为用户指标user metric。例如测量应用启动耗时bootstrap(App).then(() { window.timeToBootstrap Date.now() - performance.timing.navigationStart; });驱动侧写法如下注意userMetrics与覆盖验证器目标指标的providersdescribe(home page load, function () { it(should log load time for a 2G connection, (done) { runner .sample({ execute: () { browser.get(http://localhost:8080); }, userMetrics: { timeToBootstrap: The time in milliseconds to bootstrap, }, providers: [{provide: RegressionSlopeValidator.METRIC, useValue: timeToBootstrap}], }) .then(done); }); });采用这种策略时Benchpress 会阻塞等待被测应用window上出现指定属性名这里是timeToBootstrap且其值为 number再开始取样。该行为由 user_metric.ts 实现公开导出见 index.ts对应的单元测试在 test/metric/user_metric_spec.ts。上例中RegressionSlopeValidator.METRIC被显式覆盖为timeToBootstrap意味着预热完成的判据不再基于默认的scriptTime回归斜率而是基于该自定义指标见下文 Validator 讲解。平滑度指标Smoothness MetricsBenchpress 可以量化滚动与动画的平滑度收集以下指标frameTime.mean平均帧耗时ms60fps 的目标约为 16.6msframeTime.worst最差帧耗时msframeTime.best最佳帧耗时msframeTime.smooth达到 60fps 的帧占比%。采集前提有两步在基准应用内或通过 WebDriver 执行console.time(frameCapture)与console.timeEnd(frameCapture)把要评估的动作尽可能紧地包在这两个调用之间。上述指标只在两次调用之间被采集包得越紧结果越准确在测试中额外传入开启帧采集的 providerrunner.sample(providers: [{provide: bp.Options.CAPTURE_FRAMES, useValue: true}], ...)其中Options.CAPTURE_FRAMES对应的 InjectionToken 定义在 common_options.ts默认值为false见同文件 DEFAULT_PROVIDERS 第 40 行。帧相关指标的真实采集与回归测试可分别在 metric/perflog_metric.ts 与 test/metric/perflog_metric_spec.ts 中看到。请求指标Requests MetricsBenchpress 还能记录自window.performance.timing.navigationStart以来发送的请求数与接收到的编码字节数receivedData自上次 navigation start 起接收的字节数requestCount自上次 navigation start 起发送的请求数。对应需要额外传入的 providersrunner.sample(providers: [ {provide: bp.Options.RECEIVED_DATA, useValue: true}, {provide: bp.Options.REQUEST_COUNT, useValue: true} ], ...)这两个开关的 InjectionToken 同样定义在 common_options.ts默认值均为false。最佳实践README 的 Best practices 章节给出了一套经过实践检验的基准测试方法论整理如下使用归一化环境依赖执行环境性能的指标必须跑在归一化机器上。例如把真实移动设备的 CPU 频率固定到某个值——这通常需要 root 权限如 Google Nexus 设备的 userdebug 版 Android又例如一台不做后台任务、CPU 频率固定的校准机器。优先做相对比较相对比值随时间漂移的可能性更低也更利于解释结果。例如拿用某 UI 框架写的示例与手写示例对比并跟踪二者比值。针对提交区间做提交后断言post-commit跑基准很耗时逐提交运行通常太慢当某提交区间检测到回退时用二分法bisection定位问题提交。在新窗口里多次重复在全新窗口把同一基准跑多次取每次运行的平均值中的最小值。谨慎使用强制 GC强制 GC 会扭曲脚本执行时间与 gcTime 数字但在需要稳定 gcTime / gc 回收量数值时可能是必要的。该选项在源码中对应 common_options.ts 的Options.FORCE_GC默认false并由 WebDriverExtension 提供 force gc 能力。每个测试都开新窗口浏览器如 Chrome可能在多次页面刷新间保留 JIT 统计信息从而依据此前加载内容对页面做出不同优化开新窗口可避免这种串扰。架构详解从组件到代码README 的 Detailed overview 章节给出了完整组件图与概念定义下面结合本仓库源码逐项展开。两个核心概念valid sample有效样本能较好代表应被测量的世界的样本集合complete sample完整样本截至目前采集到的全部度量值集合。对应源码类型为 measure_values.ts 中的MeasureValues以及 sampler.ts 末尾定义的SampleStatecompleteSample: MeasureValues[]与validSample: MeasureValues[] | null两个字段。Runner持有默认配置构造函数接收_defaultProviders见 runner.ts每次sample()调用都创建一个新的 Injector其余所有组件都经由该 Injector 创建先通过 WebDriverAdapter 读取浏览器capabilities与window.navigator.userAgent再带着这些运行时信息创建第二个注入器去获取 Samplerrunner.ts。Sampler采样循环Sampler 拥有整个 sample 循环其职责按注释明确划分为四步sampler.ts调用prepare/execute回调经driver.waitFor执行从 Metric 取数beginMeasure/endMeasure请 Validator 判定是否存在有效样本把新数据上报给 Reporter若尚无有效样本则继续循环递归loop见 sampler.ts。值得注意的细节当某次度量结果带invalid标记时该轮数据会被丢弃并沿用上一状态sampler.ts每个MeasureValues都携带序号等于当前 complete sample 长度与时间戳this._now()。Sampler 会立即把每次的度量值上报给 reporter循环直到 Validator 能从完整样本中提取出有效样本为止最终把valid sample 与 complete sample 一并上报给 reporter。Metric指标采集器从浏览器获取度量值例如读取 performance logs、DOM 值、JavaScript 值。公开导出index.ts包含PerflogMetric解析性能日志得到 scriptTime / renderTime / gcTime / gcAmount 等UserMetric读取window上的自定义指标MultiMetric把多个 Metric 组合成一个默认组合为[PerflogMetric, UserMetric]runner.ts。Validator有效样本判定器从完整样本中抽取有效样本。README 给出两个示例策略等到累计 10 个样本就把它们作为有效样本注意这会包含预热时间等到scriptTime指标最近 10 个值的回归斜率 ≥ 0即该指标数值不再递减表示浏览器已热。后者即默认验证器RegressionSlopeValidator默认sampleSize 10、目标metric scriptTime见 regression_slope_validator.ts它取最近 10 个样本对[0..9]索引与该指标值做线性回归仅当斜率 ≥ 0 时返回该最新样本组否则返回null让 Sampler 继续同文件validate()L43-L67。此外还提供SizeValidator同样在 index.ts 导出Runner 的默认 Providers 同时注册了二者并默认useExistingRegressionSlopeValidatorrunner.ts。Reporter结果上报器把度量值、有效样本与完整样本上报到后端ConsoleReporter打印到控制台JsonFileReporter写入 JSON 文件依赖 common_options.ts 的Options.WRITE_FILE注入的文件写入函数MultiReporter把结果广播给多个 reporter默认组合为[ConsoleReporter]runner.ts。Reporter 的两个上报入口是reportMeasureValues(measureValues)与reportSample(completeSample, validSample)调用关系见 sampler.ts。WebDriverAdapterWebDriver 抽象层对底层 WebDriver 客户端的抽象每种客户端一个实现。公开导出的SeleniumWebDriverAdapter面向 selenium-webdriver Node.js 模块index.ts在 WebDriverAdapter 抽象上WebDriverExtension 才能实现 force gc、读取归一化性能日志等协议扩展方法每浏览器一个实现见 webdriver 目录下的 Chrome / Firefox / iOS 三个 extension。附包管理与发布与框架主体不同angular/benchpress不随框架一起发布其 Bazelnpm_package目标不带release-with-framework标签见 packages/benchpress/DEVELOPER.md。如需手动发布先在 package.json 中提升version再执行pnpm bazel run //packages/benchpress:npm_package.publish总结Benchpress 的价值在于把浏览器 DevTools 才有的性能数据变成了可自动化的、跨浏览器的标准化度量流水线写一个被测应用、配一段 WebDriver 驱动即可获得脚本时间、渲染时间、GC、帧率与请求量等指标配合其 Sampler 循环、回归斜率 Validator 与多 Reporter 上报机制你就能把性能回退检测接入提交后 CI 流程。若想深入了解某个指标的提取细节或验证器的判定算法可直接在 packages/benchpress/test 目录下查看perflog_metric_spec.ts、user_metric_spec.ts与chrome_driver_extension_spec.ts等测试结合本文源码路径逐一对照阅读效果最佳。【免费下载链接】angularDeliver web apps with confidence 项目地址: https://gitcode.com/GitHub_Trending/an/angular创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考