K6 浏览器测试 Performance 和 Frontend

FreeGuideOnline 最新 2026-07-12

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_lcp
  • browser_web_vital_fid
  • browser_web_vital_cls

但有时你需要更精确的控制,比如在特定交互后手动采集,或收集其他指标(FCP、TTI)。以下演示使用 PerformanceObserverweb-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)']}` : '',
  };
}