KEEL · 龙骨 · A CURRICULUM FOR THE AI ERA

09 · 性能与 CWV 工程化:把指标变成门禁 — keel 龙骨

本章目标:把 LCP/INP/CLS 从"事后看报告"变成"上线前的预算与门禁"。 验收标准:发版流水线能在性能预算超标时自动拦截,且区分实验室数据与真实用户数据。

本章目标:把 LCP/INP/CLS 从"事后看报告"变成"上线前的预算与门禁"。
验收标准:发版流水线能在性能预算超标时自动拦截,且区分实验室数据与真实用户数据。

为什么性能要工程化:既有《SEO 收录与优化》第 07 章讲了 CWV 是什么、怎么达标。但那是一次性的调优。真正的问题是它会不会回退——一次不经意的依赖升级就能把 LCP 打回原形。解决办法只有一个:把预算写进流水线,让它自己守门。


一、三个核心指标与阈值

指标 含义 良好 需改进 差
LCP 最大内容绘制 ≤ 2.5s 2.5–4.0s > 4.0s
INP 交互到下次绘制 ≤ 200ms 200–500ms > 500ms
CLS 累计布局偏移 ≤ 0.1 0.1–0.25 > 0.25

判据口径:CWV 按第 75 百分位评估,不是平均值。也就是说,仍有 25% 的访问可以慢——但整体第 75 百分位必须达标。


二、真实用户数据 vs 实验室数据

两者测的不是一回事,必须分清:

维度 真实用户数据(CrUX / RUM) 实验室数据(Lighthouse / WebPageTest)
来源 真实访客的浏览器 受控环境的一次运行
覆盖 全量、按设备/网络分层 单次、条件固定
用途 排名信号依据 定位问题、做回归
特点 滞后(28 天滚动)、不可复现 即时、可复现
盲区 低流量页无数据 不能代表真实用户

核心取舍:

# 用 Lighthouse CI 在本地/CI 生成实验室数据
npx lighthouse https://www.xxxxxx.cn/learning/ \
  --only-categories=performance \
  --output=json --output-path=/tmp/lh.json --chrome-flags="--headless"
node -e "const r=require('/tmp/lh.json');
  const a=r.audits;
  console.log('LCP', a['largest-contentful-paint'].numericValue.toFixed(0),'ms');
  console.log('CLS', a['cumulative-layout-shift'].numericValue.toFixed(3));
  console.log('TBT', a['total-blocking-time'].numericValue.toFixed(0),'ms');"

三、把指标变成预算(Performance Budget)

预算 = 明确的上限数字,超标即失败。

预算项 上限(示例) 依据
LCP 2.5s(实验室同口径) 达标线
CLS 0.1 达标线
首屏 JS 170KB(gzip) 影响 INP/LCP
首屏 CSS 50KB 影响渲染阻塞
图片单张 200KB 影响 LCP
总请求数 50 影响加载
TTFB 200ms 服务端
// budget.json —— 交给 Lighthouse CI
[
  {
    "path": "/*",
    "timings": [
      { "metric": "largest-contentful-paint", "budget": 2500 },
      { "metric": "cumulative-layout-shift", "budget": 0.1 },
      { "metric": "total-blocking-time", "budget": 300 }
    ],
    "resourceSizes": [
      { "resourceType": "script", "budget": 170 },
      { "resourceType": "image", "budget": 300 },
      { "resourceType": "total", "budget": 800 }
    ]
  }
]

四、接进流水线(门禁)

# .github/workflows/perf.yml(示意)
- name: Lighthouse CI
  run: |
    npm i -g @lhci/cli
    lhci autorun --collect.url=https://www.xxxxxx.cn/learning/ \
                 --assert.budgetsFile=budget.json
# 无 CI 环境时的本地门禁脚本
#!/usr/bin/env bash
set -e
npx lighthouse https://www.xxxxxx.cn/learning/ \
  --only-categories=performance --output=json --output-path=/tmp/lh.json \
  --chrome-flags="--headless" --quiet
LCP=$(node -e "console.log(require('/tmp/lh.json').audits['largest-contentful-paint'].numericValue)")
CLS=$(node -e "console.log(require('/tmp/lh.json').audits['cumulative-layout-shift'].numericValue)")
awk -v l=$LCP -v c=$CLS 'BEGIN{
  if (l>2500){print "❌ LCP 超标: "l"ms"; exit 1}
  if (c>0.1){print "❌ CLS 超标: "c; exit 1}
  print "✅ 性能预算通过 LCP="l"ms CLS="c
}'

门禁的意义:性能和 SEO 其他环节一样,不得回退。接进 CI 后,第 12 章的治理框架才有强制力。


五、常见回退原因与预防

回退原因 表现 预防
依赖升级 JS 体积暴涨 体积预算 + bundle 分析
新增第三方脚本 TBT/INP 上升 脚本白名单 + 总预算
图片未优化 LCP 变差 构建期图片压缩与格式转换
字体加载 CLS/闪烁 预加载 + font-display
布局后插内容 CLS 上升 预留尺寸
服务端变慢 TTFB 上升 服务端监控
# 构建产物体积检查(示例,配合 webpack/vite 输出)
du -sh frontend/dist/assets/*.js | sort -h | tail -5

六、故障速查

现象 性能层原因 处理
LCP 超标 大图/字体/阻塞脚本 定位 LCP 元素并优化
INP 超标 长任务、重 JS 拆任务、减脚本
CLS 超标 无尺寸的图片/广告 预留空间
实验室达标真实不达标 样本偏差 看 CrUX/RUM 分层数据
上版后性能回退 依赖/脚本变化 检查门禁是否生效

七、本章验收

# 本章核心:一次性能门禁检查
npx lighthouse https://www.xxxxxx.cn/learning/ --only-categories=performance \
  --output=json --output-path=/tmp/lh.json --chrome-flags="--headless" --quiet

性能守住了,接下来要能证明"守住的东西确实有效"——即度量与归因。

进入 keel 阅读