K6 浏览器测试 Performance 和 Frontend
bash docker pull grafana/k6:master-with-browser
运行容器时注意挂载脚本目录并暴露 Chrome 调试端口(可选):
```bash
docker run --rm -i -v $(pwd):/scripts grafana/k6:master-with-browser run /scripts/test.js
2.2 验证安装
创建文件 hello.js:
import { browser } from 'k6/experimental/browser';
export const options = {
scenarios: {
ui: {
executor: 'constant-vus',
vus: 1,
duration: '30s',
options: { browser: { type: 'chromium' } },
},
},
};
export default async function () {
const context = browser.newContext();
const page = context.newPage();
await page.goto('https://test.k6.io');
page.close();
context.close();
}
执行:
k6 run hello.js
若控制台输出成功信息且无报错,表示环境就绪。
3. 核心概念:Performance 与 Frontend 测试
| 测试类型 | 关注点 | K6 能做什么 | 典型指标 |
|---|---|---|---|
| Performance(后端/网络性能) | 服务器响应时间、资源请求耗时、网络耗时 | 通过 Navigation Timing API、Resource Timing API 抓取精确的毫秒级时间戳 | TTFB, DOMContentLoaded, Load 时间, 首字节时间, DNS 时间, TCP 握手时间 |
| Frontend(前端用户体验) | 页面绘制、交互延迟、视觉稳定性 | 通过 JavaScript 执行标准 Web API(PerformanceObserver,web-vitals 库) | LCP, FID, CLS, FCP, INP 等 |
在 K6 浏览器脚本中,你可以同时收集这两类指标,只需在 page.goto() 完成后执行 JavaScript 提取即可。
4. 编写第一个浏览器测试脚本
4.1 场景配置
export const options = {
thresholds: {
'browser_web_vital_lcp': ['p(95)<2500'], // 95%的 LCP 低于 2.5s
'browser_web_vital_cls': ['p(95)<0.1'],
},
scenarios: {
browser_test: {
executor: 'constant-vus',
vus: 1,
duration: '1m',
options: {
browser: { type: 'chromium' },
},
},
},
};
- 使用
constant-vus执行器,每个 VU 会启动一个浏览器实例。 browser_web_vital_*是 K6 浏览器模块自动收集并暴露为自定义指标的 Web Vitals,你可以在thresholds中直接使用。
4.2 基础页面访问与指标输出
import { browser } from 'k6/experimental/browser';
export default async function () {
const context = browser.newContext();
const page = context.newPage();
try {
await page.goto('https://test.k6.io', { waitUntil: 'networkidle' });
console.log('✅ 页面加载完成');
} catch (e) {
console.error('❌ 页面加载失败:', e);
} finally {
page.close();
context.close();
}
}
waitUntil 选项支持:
load– 等待 load 事件触发;domcontentloaded– 等待 DOMContentLoaded;networkidle– 等待网络空闲(500ms 内无新请求),适合现代 SPA 页面。
5. 测量页面加载性能(Performance 指标)
这些指标主要来自 Navigation Timing API,反映网络和后端响应效率。
5.1 获取完整的 Navigation Timing 对象
const timing = await page.evaluate(() => {
const t = performance.getEntriesByType('navigation')[0].toJSON();
return t;
});
console.log(JSON.stringify(timing, null, 2));
你会得到如 domainLookupStart, connectEnd, responseStart, domComplete 等时间戳。通过差值计算各个阶段耗时。
5.2 提取关键耗时并创建自定义 K6 指标
import { Trend } from 'k6/metrics';
const ttfb = new Trend('ttfb');
const domContentLoaded = new Trend('dom_content_loaded');
const loadTime = new Trend('load_time');
export default async function () {
// ... 打开页面
const perfEntries = await page.evaluate(() => {
const [entry] = performance.getEntriesByType('navigation');
return {
ttfb: entry.responseStart - entry.requestStart,
dcl: entry.domContentLoadedEventEnd - entry.fetchStart,
load: entry.loadEventEnd - entry.fetchStart,
};
});
ttfb.add(perfEntries.ttfb);
domContentLoaded.add(perfEntries.dcl);
loadTime.add(perfEntries.load);
console.log(`TTFB: ${perfEntries.ttfb}ms | DCL: ${perfEntries.dcl}ms | Load: ${perfEntries.load}ms`);
}
这样在测试结束后的汇总输出中会包含这些 Trend 的 min、max、avg、p(90)、p(95) 等统计数据。
6. 前端性能指标(Frontend / Web Vitals)
K6 浏览器模块会自动捕获 LCP、FID、CLS,并分别记录为:
browser_web_vital_lcpbrowser_web_vital_fidbrowser_web_vital_cls
但有时你需要更精确的控制,比如在特定交互后手动采集,或收集其他指标(FCP、TTI)。以下演示使用 PerformanceObserver 和 web-vitals 库。
6.1 使用 PerformanceObserver 手动获取 Web Vitals
const vitalData = await page.evaluate(() => {
return new Promise((resolve) => {
const results = {};
// LCP
const lcpObserver = new PerformanceObserver((list) => {
const entries = list.getEntries();
const lastEntry = entries[entries.length - 1];
results.lcp = lastEntry.startTime;
});
lcpObserver.observe({ type: 'largest-contentful-paint', buffered: true });
// CLS
let clsValue = 0;
const clsObserver = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (!entry.hadRecentInput) clsValue += entry.value;
}
results.cls = clsValue;
});
clsObserver.observe({ type: 'layout-shift', buffered: true });
// FID(简化版,实际需要监听第一次输入)
const fidObserver = new PerformanceObserver((list) => {
const firstInput = list.getEntries()[0];
results.fid = firstInput.processingStart - firstInput.startTime;
});
fidObserver.observe({ type: 'first-input', buffered: true });
// 5秒后强制 resolve
setTimeout(() => resolve(results), 5000);
});
});
console.log(`Manual LCP: ${vitalData.lcp}ms, CLS: ${vitalData.cls}`);
注意:
buffered: true可以捕获观察器创建之前已经发生的条目,这对页面加载后立即执行的脚本非常重要。
6.2 注入 web-vitals 库(推荐)
先将 web-vitals 库的代码通过 page.addInitScript() 注入,使指标捕获更可靠。
// 在 page 创建后立即注入
page.addInitScript(`
import('https://unpkg.com/web-vitals@3/dist/web-vitals.attribution.iife.js')
.then(() => {
window.__webVitalsCallback = undefined;
});
`);
// 在 goTo 之后获取
const vitals = await page.evaluate(() => {
return new Promise((resolve) => {
const data = {};
webVitals.onLCP((metric) => { data.lcp = metric.value; });
webVitals.onCLS((metric) => { data.cls = metric.value; });
webVitals.onFID((metric) => { data.fid = metric.value; });
webVitals.onFCP((metric) => { data.fcp = metric.value; });
webVitals.onTTFB((metric) => { data.ttfb = metric.value; });
// 保证所有指标至少发送一次后 resolve
setTimeout(() => resolve(data), 6000);
});
});
console.log(`LCP=${vitals.lcp} | FID=${vitals.fid} | CLS=${vitals.cls}`);
这种方式能获得更准确的归因信息,并自动处理 SPA 路由变化等情况。
7. 高级主题:模拟用户交互与自定义指标
7.1 点击、输入等真实用户行为
// 等待某元素可见后点击
const button = page.locator('button#login');
await button.waitFor({ state: 'visible' });
await button.click();
// 填写输入框
await page.fill('input[name="username"]', 'testuser');
await page.fill('input[name="password"]', 's3cret');
await page.click('button[type="submit"]');
交互后可以重新测量 LCP 或 FID,评估关键交互的性能。
7.2 截图检查
// 截取当前页面并保存(仅调试用,会消耗性能)
await page.screenshot({ path: 'screenshots/homepage.png', fullPage: true });
7.3 检查资源加载(Resource Timing)
const slowResources = await page.evaluate(() => {
return performance.getEntriesByType('resource')
.filter(r => r.duration > 2000)
.map(r => ({ name: r.name, duration: r.duration }));
});
console.log('慢资源加载:', JSON.stringify(slowResources));
可快速定位拖慢页面的图片、脚本或 API 请求。
8. 结果分析与可视化
K6 默认会输出指标摘要到控制台:
✓ browser_web_vital_lcp........: avg=1856ms p(95)=2340ms
✓ browser_web_vital_cls........: avg=0.02 p(95)=0.07
使用 Grafana Cloud 或本地 InfluxDB + Grafana 可获得更丰富的时间序列可视化。 对于浏览器指标,K6 云服务会自动提供 Performance 和 Web Vitals 的专用仪表板。
你还可以通过 handleSummary() 导出 JSON 结果:
export function handleSummary(data) {
return {
'summary.json': JSON.stringify(data, null, 2),
stdout: data.metrics.browser_web_vital_lcp ? `LCP p(95): ${data.metrics.browser_web_vital_lcp.values['p(95)']}` : '',
};
}