关于 WebPerf Snippets
WebPerf Snippets 是通过 Playwright 在无头 Chromium 中运行精选性能片段的命令行工具。它面向需要诊断 Core Web Vitals 及其他页面性能问题的开发者,也可自动执行审计并在 CI 中设置性能预算。
主要功能
- 01在无头 Chromium 中运行精选片段
- 02Core Web Vitals 与加载工作流
- 03交互与结构审计
- 04JSON 和 Markdown 报告
- 05多次运行与性能预算
- 06CPU 与网络限速
适用场景
- 01在 CI 中诊断 LCP 回归
- 02按性能预算拦截拉取请求
- 03审计交互延迟与布局偏移
- 04将合成测量与 CrUX 实测数据对照
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
8收录的工具列表 · 更新于 1 天前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| category | enum | 否 | 仅列出此类别的片段CoreWebVitals | Loading | Interaction | Media | Resources |
详细介绍
webperf-snippets CLI
通过 Playwright 在无头模式下运行精选的 WebPerf Snippets。诊断 Lighthouse 未能呈现的 Core Web Vitals,并根据实际性能预算拦截 CI。
状态: v0.4。提供 Core Web Vitals、加载、交互和结构审计工作流,并可按名称运行集合中的每个代码片段。请参阅路线图。
为什么使用
Lighthouse 会给出评分。DevTools 代码片段则能给出诊断,例如 TTFB / 资源加载延迟 / 元素渲染延迟的细分部分、LoAF 脚本归因,以及阻塞渲染的资源。此 CLI 会在无头浏览器中运行相同的精选代码片段,让你能够:
- 在 CI 中诊断 LCP 回退,无需复制粘贴到 DevTools。
- 根据实际性能预算拦截拉取请求。
- 自动运行你已经在手动执行的代码片段。
安装
Playwright 是对等依赖项。请安装 Playwright 和此 CLI,并安装 chromium 浏览器:
npm install --save-dev webperf-snippets playwright
npx playwright install chromium
用法
npx webperf-snippets <url> [options]
示例
运行默认的 Core Web Vitals 工作流(LCP + CLS;若 LCP > 2.5s,则额外运行 LCP-Subparts):
npx webperf-snippets https://example.com
加载审计(11 个步骤:TTFB、FCP、阻塞渲染的资源、脚本、资源计时、第三方影响、字体、缓存;请参阅 CLI 页面中的列表):
npx webperf-snippets https://example.com --workflow loading
用于区分第一方和第三方资源的步骤,会将页面的根域名及其子域名(对于 example.com,如 cdn.example.com)视为第一方。如果网站从其他域名(例如 CDN)提供自己的资源,请使用 --own-domains 指定域名,可用逗号分隔,也可重复指定:
npx webperf-snippets https://example.com --workflow loading --own-domains cdn.example.net,static.example.org
每个域名都按其根域名计算,因此 cdn.example.net 会将 example.net 下的所有主机都视为第一方。CLI 会在每个代码片段的 OWN_DOMAINS 常量中写入域名列表,与在控制台中编辑代码片段顶部内容时使用的常量相同。域名为空或包含空格时,CLI 会以代码 2 退出。
用于 CI 的结构检查(22 项检查:阻塞渲染的资源、压缩、字体、图像、提示、DOM 大小等;请参阅 CLI 页面中的列表):
npx webperf-snippets https://example.com --workflow audit
用于 PR 评论的 Markdown 输出:
npx webperf-snippets https://example.com --markdown
JSON 输出(用于传递给 jq 或 CI):
npx webperf-snippets https://example.com --json
JSON 输出会在 conditions 中记录页面的测量方式,以便后续运行判断测量方式是否相同:视口、工作流(或使用 --snippet 时的代码片段路径)、等待时间以及 CLI 版本。
"conditions": { "viewport": "mobile", "workflow": "core-web-vitals", "waitMs": 3000, "cliVersion": "1.6.0" }
单个代码片段:
npx webperf-snippets https://example.com --snippet LCP-Subparts
使用交互脚本进行合成 INP 测量:
npx webperf-snippets https://example.com --snippet INP --interact-script interactions.json
npx webperf-snippets https://example.com --snippet Interaction/Interactions --interact-script interactions.json
交互代码片段(Interactions、Input-Latency-Breakdown、LongTask、Forced-Synchronous-Layout 等)会在第一步之前安装,并在最后一步之后读取,因此其结果会包含这些步骤造成的影响。
CLI 会等待浏览器报告最后一次交互的计时信息,然后才读取代码片段。如果脚本在点击后立即结束,页面会在点击所触发的后续操作(例如请求或动画)发生之前就被读取;如果需要测量这些操作,请以 wait 步骤结束脚本。
交互审计(长任务、长动画帧、滚动、布局偏移、强制布局、交互延迟)。CLI 会滚动页面,或运行你的 --interact-script;如果脚本中没有点击操作,则会跳过需要点击的步骤:
npx webperf-snippets https://example.com --workflow interaction
使用 Playwright 存储状态(Cookie 和 localStorage)访问需要登录的页面:
# Step 1: sign in once and save the session
npx playwright codegen --save-storage=auth.json https://example.com/login
# Step 2: measure an authenticated page
npx webperf-snippets https://example.com/dashboard --storage-state auth.json
该文件包含有效的会话 Cookie。请勿将其纳入版本控制(将其添加到 .gitignore),并在 CI 中运行时使用临时账号。如果路径不存在,CLI 会以代码 2 退出。
对于需要从每个请求中读取令牌的访问代理(例如 Cloudflare Access 的服务令牌),请使用 --header 将令牌作为请求标头传入,每个标头指定一次:
npx webperf-snippets https://review.example.com \
--header "CF-Access-Client-Id: $CF_ACCESS_CLIENT_ID" \
--header "CF-Access-Client-Secret: $CF_ACCESS_CLIENT_SECRET"
这些标头会随每个请求发送到 URL 所属的源,绝不会发送到其他源,因此第三方不会收到令牌,其跨源请求也无需进行 CORS 预检。浏览器会暂停发往该源的每个请求以添加这些标头,这会使计时增加几毫秒;如果代理接受会话 Cookie,使用 --storage-state 测量就不会产生这项开销。没有名称或冒号的标头会以代码 2 退出。像上面那样从环境变量读取这些值,避免它们出现在 shell 历史记录中。
将测量值与 CrUX 现场数据进行比较(即真实用户在第 75 百分位的体验):
CRUX_API_KEY=your-key npx webperf-snippets https://example.com
Field data (CrUX p75, mobile)
🟡 LCP 2.80s needs-improvement (synthetic: 2.10s)
🟢 CLS 0.0800 good (synthetic: 0.0500)
🟡 INP 220ms needs-improvement (synthetic: n/a)
获取 Chrome UX Report API 的密钥,并通过 --crux-key 或 CRUX_API_KEY 环境变量传入;建议使用环境变量,因为标志会出现在 shell 历史记录和 CI 日志中。该部分会显示在人类可读和 Markdown 输出中,并显示在 --json 输出的 crux 下。
- 数据群体由
--viewport决定:desktop使用桌面数据,mobile和tablet使用手机数据。标题会注明使用的是哪一种。 - 如果页面本身没有数据,CLI 会改为查询源,并将该部分标记为
origin-level,以免与页面级数据混淆。如果源也没有数据,输出会显示Field data: not available。 - CrUX 出错(配额、网络问题或 10 秒内没有响应)时,只会跳过该部分,不会改变退出代码,也绝不会打印密钥。
- 除非运行交互脚本,否则 INP 的合成值会显示为
n/a。
运行结束后,使用 --report-to 将结果发送到某个端点:
npx webperf-snippets https://example.com --workflow audit \
--report-to https://reports.example.com/api/report \
--api-key $REPORT_API_KEY
如果不使用 --report-to,CLI 不会进行任何外部调用。使用该参数时,所有代码片段运行完毕后,结果会以 JSON 格式通过 POST 发送:
{
"url": "https://example.com",
"workflow": "audit",
"timestamp": "2026-05-06T10:00:00.000Z",
"navMs": 1240,
"results": [],
"meta": { "viewport": "mobile", "waitMs": 3000, "cli_version": "1.6.0" }
}
运行 --snippet 时,workflow 为 null,并且 snippet 字段会包含名称。使用 --cpu 或 --network 时,meta 会在 throttling 中包含运行条件;使用 --runs 时,runs 会包含运行摘要。results 与 --json 输出中的列表相同,因此可能包含 URL 和测量页面的详情;请仅将其发送到你信任的端点。
- URL 必须使用
https。只有localhost可以使用http,因此密钥绝不会以明文发送。 --api-key会作为Authorization: Bearer <key>发送。建议使用PERF_REVIEWS_API_KEY环境变量,因为标志会出现在 shell 历史记录和 CI 日志中。密钥绝不会被打印。- 退出代码不会取决于报告。POST 失败(错误状态、端点无法访问或 5 秒内没有响应)时会打印警告,退出代码仍由预算和代码片段决定。如果端点响应
{ "ok": false, "regressions": [...] },回归项会作为警告打印,同样不会改变退出代码。
使用 --cpu 和 --network,在较慢设备和网络的条件下进行测量。如果不使用这些参数,CLI 会以当前机器的速度运行;笔记本电脑或 CI 运行器的速度通常比大多数手机快:
npx webperf-snippets https://example.com --cpu 4 --network slow-4g
--cpu <factor> 会按指定倍数降低主线程速度。--network 使用 DevTools 中的预设,数值相同:
| 预设 | 下载 | 上传 | 延迟 |
|---|---|---|---|
fast-4g | 8.1 Mbps | 1.35 Mbps | 165 ms |
slow-4g | 1.44 Mbps | 675 kbps | 562.5 ms |
3g | 400 kbps | 400 kbps | 2000 ms |
Chrome 会将延迟施加到每个响应的正文,而不是标头上。从 responseStart 读取的值(例如 TTFB 及其子项)不包含该延迟;每个资源的下载、FCP、LCP 和加载事件则包含。报告会在标题下方注明运行条件,JSON 输出则会在 throttling 中包含这些条件,例如 { "cpu": 4, "network": "slow-4g" }。CPU 因子小于 1 或使用未知预设时,会以代码 2 退出。
一次页面加载就是一个样本,实验室数据会因加载次数不同而变化。--runs <n> 会依次测量页面 1 到 9 次,并像 WebPageTest 一样报告中位数运行结果:每项结果都来自该次运行,因此彼此一致(LCP 的子部分之和等于其 LCP):
npx webperf-snippets https://example.com --runs 5 --budget-lcp 2500
中位数运行结果是 LCP 处于中位数的那次运行。不含 LCP 的工作流会使用其测量的 FCP、TTFB 和导航时间中的第一项,而 --median-by <metric> 可以选择其他指标(LCP、FCP、TTFB、CLS、INP 或 navMs)。运行次数为偶数时,会取中间两次运行中较低的那次,因此报告始终对应实际发生过的一次运行。报告会添加各项指标在所有运行中的中位数、最小值和最大值,JSON 输出则会在 runs 中包含这些数据:
"runs": {
"count": 5,
"failed": 0,
"medianRun": 3,
"medianBy": "LCP",
"summary": {
"LCP": { "median": 2140, "min": 1980, "max": 2610, "values": [2610, 2010, 2140, 1980, 2290], "unit": "ms" }
}
}
medianRun 从 1 开始计数,values 保留各次运行的顺序。预算会比较各项指标的中位数,而不是某一次运行。失败的运行(例如未完成的导航)会计入 failed,并从中位数计算中排除;若所有运行都失败,命令会以代码 1 退出。每次运行开始时都会将其报告写入 stderr,因此 stdout 只包含报告。无效的次数或指标会导致命令以代码 2 退出。
使用 --matrix <file>,即可在一条命令中测量多个页面、多个设备和多个变体。文件中列出这些项目,CLI 会在每台设备和每个变体上测量每个 URL,每种组合依次加载 runs 次:
{
"urls": {
"home": "https://example.com/",
"article": "https://example.com/news/some-article"
},
"devices": {
"mobile": { "viewport": "mobile", "cpu": 4, "network": "slow-4g" },
"high-end": { "viewport": "mobile", "cpu": 1, "network": { "downloadKbps": 50000, "uploadKbps": 10000, "latencyMs": 20 } },
"desktop": { "viewport": "desktop" }
},
"variants": {
"no-consent": {},
"consent": { "storageState": "states/consent.json" }
},
"runs": 3
}
npx webperf-snippets --matrix matrix.json --budget-lcp 2500
urls:名称和 URL。也可以使用 URL 数组,并以每个 URL 的路径和查询参数(/、/news/)为其命名;如果 URL 包含多个主机,还会附上主机名。devices:viewport预设、cpu倍数和network。网络可以是预设值(fast-4g、slow-4g、3g),也可以指定 DevTools 未提供预设的网络(如 5G),并以千比特每秒和毫秒为单位设置参数。视口预设数组(["mobile", "desktop"])可简写为不启用限速的设备。如果没有devices,矩阵会像 Lighthouse 一样,使用 CPU 4x 和 Slow 4G 测量一台中端手机。variants:每个变体的条件,包括storageState(相对于该文件的路径)、headers和ownDomains。如果没有variants,组合就是各设备上的页面:报告中不会有 Variant 列,组合的id为页面和设备(/news/ · mobile)。runs(1 到 9)和medianBy,其值会被--runs和--median-by替换。
命令行选项会应用于每种组合:--workflow、--snippet、--wait、--interact-script、--header、--own-domains、--storage-state、--cpu、--network 和各项预算。文件中的 cpu、network 和 storageState 会覆盖命令行对应的设置;文件中的 headers 和 ownDomains 会与命令行选项合并,同名标头以变体中的设置为准。矩阵文件用于纳入版本控制,因此请通过 --header 和环境变量传递访问代理令牌等机密信息;如果文件中的标头名称看起来像机密信息,会发出警告。CLI 不认识的键会导致错误,避免因拼写错误而在缺少相应条件的情况下进行测量。
该报告是一个表格,每个组合占一行,列出各项指标的评分中位数,随后列出决定退出代码的错误;--verbose 会额外显示每个组合中位数运行的报告。--markdown 会生成相同的表格,并将每个组合的报告折叠到 <details> 块中,以便在拉取请求评论中保持可读性。使用 --json 时,输出为 { "matrix", "runs", "combinations" },每个组合都是其中位数运行的报告,包含其 id、name、device、variant(当文件定义了变体时)、runs 和 conditions,以及其设备的视口。预算会与每个组合的中位数进行比较。当某个组合超出预算、出现错误或所有运行都失败时,命令以代码 1 退出;文件无效时,则在任何页面加载之前以代码 2 退出,并指出 JSON 中的问题位置。--viewport 不适用,因为每台设备都会设置自己的视口。
使用 CrUX 时,CLI 会针对每个 URL 和设备类型各请求一次,因此同一设备的各个变体会共享其现场数据。使用 --report-to 时,它会为每个组合发送一份报告,报告正文是单个页面的内容,其中包含 meta.device、meta.variant 和 meta.runs;命令行和文件中的标头是加载条件,报告不会包含它们的值。
使用 --baseline <file>(该文件是此次运行的 --json 输出)将一次运行与较早的运行进行比较。比较结果会指出哪些指标超出实验室噪声范围而变差或变好、哪些错误是新增或已修复,以及两次运行的测量方式有哪些不同:
# On main, once: the baseline
npx webperf-snippets https://example.com --runs 5 --json > baseline.json
# On the pull request: compare and report
npx webperf-snippets https://pr-123.example.com --runs 5 --baseline baseline.json --markdown
# Fail when the page gets worse
npx webperf-snippets https://pr-123.example.com --runs 5 --baseline baseline.json --fail-on-regression
如果两次运行都测量了 LCP、FCP、TTFB、CLS 和 INP,则会比较这些指标;每一侧都使用 --runs 指定的中位数。如果只有一侧测量了某项指标,则显示为 not measured。当某项指标恶化幅度超过其阈值(基线数值的一定比例与最小值两者中的较大者),且在基线包含两次或更多次运行时,其结果高于基线中最慢的一次运行,该指标就算退化:中位数仍处于基线范围内,说明基线本身已有这种波动。指标改善时也使用相同规则,只是方向相反;否则视为未变化。
| 指标 | 基线比例 | 最小值 |
|---|---|---|
| LCP、FCP、TTFB | 10% | 100 ms |
| INP | 10% | 50 ms |
| CLS | 无 | 0.05 |
--threshold 会更改阈值的一部分,同时保留另一部分:--threshold LCP=15% 设置比例,--threshold LCP=200ms 设置最小值,--threshold CLS=0.1 设置 CLS 唯一的阈值部分。可以重复使用它来设置更多指标。
参与比较的错误是那些决定退出代码的错误:工作流步骤中严重级别为 error 的问题,以及执行失败的代码片段。如果两个错误的代码相同,且指向同一资源,那么无论错误消息如何表述,都视为相同;页面自身的资源按其路径和查询进行比较,因此来自其他主机的拉取请求预览应用可以与生产环境匹配。若一侧的代码片段没有代码(例如基线来自较早版本),则按错误消息比较,并忽略其中的数值,因此“Render-blocking CSS (69.21 KB)…”和“Render-blocking CSS (71.80 KB)…”属于同一个错误。只有本次运行中出现的错误是新增错误,只有基线中出现的错误则视为已修复。警告和信息不参与比较,因为它们变化过于频繁。
报告会在常规报告之后添加比较结果,--markdown 还会为拉取请求评论生成一个表格。JSON 输出会在 baseline 中包含比较结果:
"baseline": {
"file": "baseline.json",
"url": "https://example.com/",
"runs": { "count": 5, "medianBy": "LCP" },
"metrics": {
"LCP": { "baseline": 2100, "current": 2480, "delta": 380, "deltaPercent": 18.1, "threshold": 210, "verdict": "regressed" }
},
"errors": { "new": [{ "id": "render-blocking", "message": "1 render-blocking resource(s) delay rendering until 900ms" }], "fixed": [] },
"warnings": []
}
当两次运行采用了不同的测量方式,例如使用了不同的工作流或代码片段、视口、等待时间、限流设置、运行次数或 CLI 版本,或者使用了不同的路径或查询参数时,CLI 仍会进行比较,并在 stderr、报告和 warnings 中发出警告:阅读拉取请求评论的人看不到 CI 的 stderr。另一个主机不会发出警告,因为它是针对生产环境的拉取请求的预览应用。只有一次运行的基线会警告你仅有阈值适用。
比较仅提供参考,除非使用 --fail-on-regression,否则退出代码与不进行比较时相同;使用该选项时,如果某项指标退步或出现新的错误,则以 1 退出,并在 stderr 中列出相关项。基线不存在或不是 CLI 的输出、矩阵的基线对应单个页面或单个页面的基线对应矩阵,或者 CLI 不识别 --threshold 时,CLI 会在页面加载前以代码 2 退出。
矩阵会将之前矩阵的 --json 输出作为基线,并按 id 将每种组合与相同 id 的组合进行比较:
npx webperf-snippets --matrix matrix.json --runs 3 --baseline last-week.json
表格会在每项指标后添加一列变化值:当变化有意义时显示变化量(▲ +380ms、▼ −90ms),未变化时显示 =,基线中没有的组合则显示 new。表格下方会列出警告(所有组合都有相同警告时只列出一次)、每种组合新增和修复的错误,以及仅存在于一侧的组合。--markdown 输出相同内容,每种组合的 <details> 块中包含其自身的比较结果。在 JSON 输出中,每种组合都会包含自己的 baseline;根级 baseline 会列出文件以及新增的 added 和缺失的 missing 组合。如果任何组合退步或出现新错误,--fail-on-regression 会以 1 退出。
CI 门控:
npx webperf-snippets https://example.com --budget-lcp 2500 --budget-cls 0.1
每项预算都会与测量值进行比较;使用 --runs 时,则与中位数比较。数值超过预算时即判定失败。--budget-lcp 和 --budget-cls 需要使用 core-web-vitals 工作流;--budget-fcp 需要使用 loading 工作流;--budget-ttfb 需要使用 loading 或 audit 工作流;--budget-inp 需要使用带点击或按键操作的 interaction 工作流;使用该指标的代码片段时,--snippet 也适用。运行未测量的指标所对应的预算会在页面加载前以代码 2 退出;未实际测量的指标(例如没有交互时的 INP)所对应的预算则以代码 1 退出:预算绝不会在未经检查的情况下通过。报告会列出每项预算的数值和判定结果:passed、exceeded(并注明超出多少)或 not measured(并说明原因)。这些内容会显示在面向用户的报告和 Markdown 报告的 Budgets 部分,以及 JSON 输出和 --report-to 正文的 budgets 中。矩阵会在表格下方列出失败的预算及其对应组合。每项失败的预算也会写入 stderr,包括使用 --json 时。
选项
| 选项 | 描述 |
|---|---|
--workflow <name> | 要运行的工作流。默认值:core-web-vitals。选项:core-web-vitals、loading、audit、interaction。 |
--snippet <name> | 按名称、别名或 Category/Name 路径运行单个代码片段。 |
--json | 输出 JSON,而不是格式化文本。 |
--markdown | 输出可由 GitHub 渲染的 Markdown(用于 PR 评论)。 |
--viewport <preset> | 视口预设:mobile(默认值)、tablet、desktop。 |
--wait <ms> | 页面加载后、评估代码片段前的等待时间。默认值:3000。 |
--cpu <factor> | 按此系数降低主线程速度,例如 4。默认不进行限速。 |
--network <preset> | 使用 DevTools 预设限制网络速度:fast-4g、slow-4g、3g。默认不进行限速。 |
--interact-script <path> | JSON 文件,包含页面加载后要执行的交互(scroll、click、hover、type、wait)。先安装 Interaction 代码片段,然后再读取。 |
--storage-state <path> | 用于需要身份验证的页面的 Playwright 存储状态(Cookie 和 localStorage)。 |
--header "Name: value" | 仅用于 URL 所属源的请求标头,例如访问代理的令牌。可重复使用以添加更多标头。 |
--own-domains <list> | 视为第一方的站点域名,例如站点自己的 CDN。可用逗号分隔,也可重复指定。 |
--runs <n> | 依次测量页面 n 次(1 到 9 次),并报告各次运行结果的中位数。 |
--median-by <metric> | 用于选择中位数运行结果的指标:LCP、FCP、TTFB、CLS、INP 或 navMs。默认选择已测得的 LCP、FCP、TTFB 和 navMs 中的首个指标。 |
--matrix <file> | 测量 JSON 文件中每个 URL 在其列出的每种设备和变体上的结果,并报告中位数表格。 |
--baseline <file> | 将页面或矩阵中的每种组合与先前运行的 --json 输出进行比较:哪些指标在超出误差范围的情况下变差或变好,以及哪些错误是新增或已修复的。 |
--threshold <metric>=<v> | --baseline 的指标阈值,例如 LCP=15%、LCP=200ms 或 CLS=0.1。可重复指定以添加更多指标。 |
--fail-on-regression | 如果指标相对于 --baseline 出现回退,或出现新错误,则以 1 退出。 |
--crux-key <key> | 在测量值旁添加 CrUX 实测数据(p75)。优先使用 CRUX_API_KEY。 |
--report-to <url> | 运行后将结果 POST 到此 https URL。未指定时不会进行外部调用。 |
--api-key <key> | 使用 --report-to 时作为 Bearer 令牌发送。优先使用 PERF_REVIEWS_API_KEY。 |
--budget-lcp <ms> | 如果 LCP 超过此值或未测得,则以 1 退出。 |
--budget-fcp <ms> | 如果 FCP 超过此值或未测得,则以 1 退出。 |
--budget-ttfb <ms> | 如果 TTFB 超过此值或未测得,则以 1 退出。 |
--budget-cls <score> | 如果 CLS 超过此值或未测得,则以 1 退出。 |
--budget-inp <ms> | 如果 INP 超过此值或未测得,则以 1 退出。 |
--verbose | 显示所有项目,包括通过检查的项目。 |
--headed | 显示浏览器窗口(用于调试)。 |
-h, --help | 显示帮助信息。 |
代码片段别名
| 别名 | 代码片段 |
|---|---|
LCP | CoreWebVitals/LCP |
CLS | CoreWebVitals/CLS |
INP | CoreWebVitals/INP |
LCP-Subparts | CoreWebVitals/LCP-Subparts |
fonts | Loading/Fonts-Preloaded-Loaded-and-used-above-the-fold |
render-blocking | Loading/Find-render-blocking-resources |
resource-hints | Loading/Resource-Hints-Validation |
preload-scripts | Loading/Validate-Preload-Async-Defer-Scripts |
priority-hints | Loading/Priority-Hints-Audit |
critical-css | Loading/Critical-CSS-Detection |
ttfb | Loading/TTFB-Sub-Parts |
script-parties | Loading/First-And-Third-Party-Script-Info |
script-loading | Loading/Script-Loading |
lazy-atf | Loading/Find-Above-The-Fold-Lazy-Loaded-Images |
lazy-conflict | Loading/Find-Images-With-Lazy-and-Fetchpriority |
eager-below-fold | Loading/Find-non-Lazy-Loaded-Images-outside-of-the-viewport |
后续步骤
一次运行的结果可在同一页面加载过程中添加步骤:LCP 超过 2.5 s 时添加 LCP-Subparts,超过 4 s 时添加 LCP-Trail;图片或视频 LCP 会添加相应的熵、图片或视频审计;CLS 超过 0.1 时添加 Layout-Shift-Loading-and-Interaction,超过 0.25 时还会添加延迟加载图片、字体、关键 CSS 和缺少尺寸信息的图片;TTFB 超过 600 ms 时添加 TTFB-Sub-Parts;FCP 超过 1.8 s 时添加渲染阻塞检查。完整表格请参阅 CLI 页面。每个代码片段只运行一次,后续步骤会给出提示:其中的 error 问题不会更改退出代码。
退出代码
| 代码 | 含义 |
|---|---|
0 | 所有检查均已通过。 |
1 | 超出预算、代码片段出错、工作流中的某个步骤报告了 error 问题,或使用 --fail-on-regression 时,相对于基线出现回归。 |
2 | 用法错误(缺少 URL、工作流未知、基线或阈值无效)。 |
MCP 服务
webperf-snippets mcp 会通过 stdio 启动一个 Model Context Protocol 服务,让 AI Agent 可以按名称调用代码片段,而无需读取其源码。将它添加到能够启动本地服务的客户端中,例如 Claude Code:
claude mcp add webperf -- npx -y webperf-snippets mcp
它需要 Playwright 的 Chromium(npx playwright install chromium);如果没有安装,工具会返回这条命令。MCP 服务文档介绍了其他客户端(Cursor、VS Code、Claude Desktop、Gemini CLI)的配置。该服务已列入 MCP Registry,名称为 io.github.nucliweb/webperf-snippets。
| 工具 | 功能 |
|---|---|
list_snippets | 列出代码片段及其类别、标题、简短别名,以及是否会跟踪交互。可选参数为 category。 |
run_snippet | 在无头 Chromium 中打开一个 URL,运行一个代码片段并返回其返回的对象。参数为 url、snippet(名称、别名或 Category/Name),以及可选的 viewport 和 waitMs。记录交互的代码片段会在通过 open_page 打开的页面上运行。 |
run_workflow | 运行 CLI 的工作流(core-web-vitals、audit、loading、interaction)及其决策树中的后续步骤。参数为 url、workflow,以及可选的 viewport 和 waitMs。 |
get_crux | 返回某个 URL 或其来源的 CrUX LCP、CLS 和 INP p75 值。参数为 url,以及可选的 formFactor(phone、desktop、tablet)。服务的环境中需要设置 CRUX_API_KEY。 |
open_page | 在后续调用中保持打开的页面中打开一个 URL,并安装跟踪代码片段(track,例如 INP)。返回一个 sessionId。 |
interact | 在打开的页面上执行步骤:click、type、hover、scroll 或 wait。 |
read_results | 返回打开页面的跟踪代码片段所记录的内容。 |
close_page | 关闭打开的页面。 |
该服务还提供资源,供客户端附加到上下文中:
| 资源 | 内容 |
|---|---|
webperf://schema | 每个代码片段结果都遵循的约定:基本结构、状态、结果读取方式,以及问题代码的规则。 |
webperf://schema/{snippet} | 一个代码片段返回的字段及示例,还有其问题代码。 |
webperf://workflows/{category} | 某个类别的工作流和决策树。 |
此外还提供提示词,即用于串联工具的模板(在 Claude Code 中为 /mcp__webperf__audit_lcp):audit_lcp、debug_inp 和 audit_page,每个提示词都带有一个 url 参数。
CrUX 密钥应放在客户端配置中,绝不能作为工具参数:
claude mcp add webperf -e CRUX_API_KEY=<key> -- npx -y webperf-snippets mcp
最多可同时打开三个页面;页面在闲置 10 分钟后关闭;客户端断开连接时,所有页面和 Chromium 都会关闭。
该服务可以响应协议两个版本的客户端:2026-07-28 修订版,以及较早的、使用 initialize 启动的版本。ADR 0002 解释了为何将其设为子命令并使用 stdio。
CI 示例
GitHub Actions:如果 LCP 超过 2.5s,则让 PR 失败:
- run: |
npm install --no-save webperf-snippets playwright
npx playwright install --with-deps chromium
npx webperf-snippets https://staging.example.com --budget-lcp 2500 --budget-cls 0.1
GitHub Action
该代码仓库提供了一个组合式 Action,可安装 CLI 和 Chromium、运行工作流并以 Markdown 格式输出结果。因此,任何代码仓库都无需额外设置,即可根据性能预算设置门禁:
jobs:
webperf:
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- uses: nucliweb/webperf-snippets/.github/actions/webperf-snippets@main
id: webperf
with:
url: https://staging.example.com
budget-lcp: 2500
fail-on-budget: "false"
- uses: actions/github-script@v7
env:
BODY: ${{ steps.webperf.outputs.markdown }}
with:
script: |
await github.rest.issues.createComment({
...context.repo,
issue_number: context.issue.number,
body: process.env.BODY,
});
- if: steps.webperf.outputs.exit-code != '0'
run: exit 1
| 输入 | 默认值 | 描述 |
|---|---|---|
url | 必填 | 要测量的 URL。 |
workflow | core-web-vitals | 要运行的工作流:core-web-vitals、loading、audit 或 interaction。 |
budget-lcp | auto | LCP 预算,单位为毫秒。auto 在 core-web-vitals 工作流中为 2500,在其他工作流中不设预算。留空可禁用检查。 |
budget-fcp | FCP 预算,单位为毫秒,适用于 loading。 | |
budget-ttfb | TTFB 预算,单位为毫秒,适用于 loading 或 audit。 | |
budget-cls | auto | CLS 预算。auto 在 core-web-vitals 工作流中为 0.1,在其他工作流中不设预算。留空可禁用检查。 |
budget-inp | INP 预算,单位为毫秒,适用于通过点击或按键执行的 interaction。 | |
fail-on-budget | true | 超出预算或代码片段出错时,让该步骤失败。设为 "false" 可读取 exit-code 并自行决定。 |
cli-version | latest | 要运行的 webperf-snippets npm 软件包版本。 |
| 输出 | 描述 |
|---|---|
markdown | Markdown 格式的结果。也会写入作业摘要。 |
exit-code | CLI 的退出代码:0 表示通过,1 表示超出预算或代码片段出错,2 表示用法错误。 |
Markdown 中包含从测量页面获取的文本,因此请像上面那样通过环境变量(env: 和 process.env)将其传递给其他步骤,切勿在脚本中插入 ${{ steps.webperf.outputs.markdown }}。将 fail-on-budget: "false" 设为关闭后,Action 步骤始终会成功,因此后续步骤可以先发布评论,再让作业失败。如果没有 permissions: pull-requests: write,评论步骤就无法在拉取请求中发布评论。
共享运行器上的 LCP、CLS 和 INP 测量结果会有 15% 到 30% 的波动;设置门禁时,建议优先使用确定性的 audit 工作流,或设置宽松的预算。
发布
CLI 软件包通过基于标签的工作流发布到 npm。发布是明确且有意为之的操作;只有推送 cli-v* 标签时才会发布。
发布步骤
- 更新
cli/package.json中的版本。 - 提交版本变更。
- 创建标签并推送:
git tag cli-v0.2.0 git push origin cli-v0.2.0 publish-cliCI 作业会运行完整测试套件,并发布到 npm。
为何选择基于标签,而非基于路径
另一种方案是在每次向 main 推送涉及 cli/ 的更改时自动发布,并通过版本检查跳过重复发布。但我们选择基于标签发布,因为这样可以让每次发布都经过慎重决定;main 上的 CI 通过并不意味着软件包已准备好发布,而标签则能明确传达这一意图。
访问控制
标签保护规则会限制可以推送 cli-v* 标签的人员。在代码仓库中,前往 Settings → Rules → New ruleset 配置规则,将目标设为 cli-v* 标签模式,并将推送权限限制为管理员或维护者。这样可以确保只有经授权的协作者才能触发发布。
必需的密钥
必须在仓库设置中配置 NPM_TOKEN 密钥,并授予其发布 webperf-snippets npm 包的权限。
已知限制
- 无头模式下的 CLS 较为保守:除非通过脚本执行滚动,否则不会检测到仅在滚动时发生的布局偏移。
- 仅限首次导航:每次调用
webperf-snippets只运行一个 URL。对于 SPA,必须直接传入路由切换后的 URL。 - 合成 INP ≠ 实际用户 INP:
--interact-script测量的是单个脚本事件的处理程序延迟。实际 INP 反映的是所有用户会话中最差的交互;字段数据请使用 RUM。
路线图
v0.2:加载工作流(TTFB、FCP、渲染阻塞、脚本、字体)、共享页面会话、用于 INP 的合成交互、用于 PR 评论的 Markdown 报告器。✓ 已发布v0.3:GitHub Action 封装、身份验证流程(登录并测量登录后的页面)、CrUX 字段数据补充、✓ 已发布--report-to。v0.4:按名称运行任意代码片段、22 步审计、11 步加载工作流、交互工作流,以及 Core Web Vitals 的决策树后续步骤。✓ 已发布
工作原理
- 通过 Playwright 启动无头 chromium。
- 导航到 URL,等待
load事件,然后等待--wait毫秒(默认 3000 毫秒),以便较晚出现的条目到达页面。 - 在页面上下文中评估每个代码片段的 IIFE,并捕获其返回对象。代码片段通过带缓冲的
PerformanceObserver读取 Chrome 仅向观察器公开的条目(LCP、布局偏移、长任务、事件时序),因此也能读取其运行前记录的条目。 - 使用交互脚本时,先安装跟踪代码片段,再运行交互,等待页面稳定,然后读取跟踪数据并运行其他代码片段。
- 根据工作流的决策树将后续代码片段加入队列。
- 呈现结果(人类可读或 JSON 格式),并以适当的代码退出。
许可证
MIT,详见 LICENSE。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。