背景
我团队 12 个前端应用(React 18 + Vite 5),去年推了一轮"性能预算进 CI":
- LCP < 2.5s(CI 里用 Lighthouse CI 跑 headless Chrome,4G 模拟)
- JS bundle 增量 < 50KB(对比 main 分支)
- CLS < 0.1
- 超标 → CI 红灯 → 不允许合入
跑了 4 个月,效果:P75 首屏从 2.8s 降到 2.1s,数字确实好看。但争议也越来越多,今天想拉出来讨论一下。
常见分歧(我观察到的 4 种立场)
立场 A:硬卡,超了就是超了。 代表:SRE / 平台组。逻辑是"没有门禁的性能预算和 KPI 一样,写下来没人管"。他们甚至建议把 LCP 阈值从 2.5s 收紧到 2.0s,理由是 Lighthouse 在 CI 里的分数和线上 RUM 有 10%–15% 的偏差(CI 环境 CPU 更快),所以 CI 阈值要更严。
立场 B:软告警 + 周会 review。 代表:部分前端 TL。逻辑是"CI 里跑 Lighthouse 的 LCP 和真实用户差太远——我线上 P75 LCP 是 2.1s,但 4G 模拟下 CI 测出来经常 3.2s,每次都要人肉判断是不是环境波动,block 了反而拖进度"。他们主张 CI 只卡 bundle 增量(这个 CI 和线上一致性高),LCP 靠线上 RUM 周会 review。
立场 C:按页面 / 场景分级。 代表:业务 PM + 前端。逻辑是"首页和后台管理页的 LCP 预算不该一样"。首页面向 C 端,2.5s 合理;后台管理页面向内部运营,3.5s 也能接受。一刀切会导致"改后台一个表格组件,LCP 超 0.2s,CI 红灯,但用户根本无感"。
立场 D:CI 测 LCP 本身就是伪命题,该用线上 RUM + 发布后自动回滚。 代表:可观测性方向的同学。逻辑是"RUM 数据才是 ground truth,CI 里 headless Chrome 的 LCP 受 CPU 调度、网络模拟精度、字体加载时序影响,同一份代码跑 5 次能差 400ms。与其在 CI 里卡一个不稳定的数字,不如发布后 15 分钟看 RUM P75,超阈值自动回滚"。
我的倾向(立场 B + C 的混合)
- CI 硬卡 bundle 增量(50KB),因为 CI 和线上对 JS 体积的感知是一致的,这个数字稳定、可复现。
- CI 对 LCP 只告警不 block,但告警要带上下文:和 main 分支 Lighthouse 分数 diff > 2 分才告警,< 2 分视为噪声。
- 线上 RUM 才是最终裁判:发布后 30 分钟窗口内 P75 LCP 比发布前恶化 > 20%,触发自动告警 + 人工确认回滚。
- 按页面分级:首页 / 落地页 LCP 预算 2.5s,后台 / 工具页 3.5s,写进配置而非代码硬编码。
理由:CI 的价值是"快速反馈确定性高的问题",bundle 体积是确定性的;LCP 在 CI 里受变量太多,把它当硬门禁会让团队"学会绕过"(比如把 LCP 元素改成 font-display: swap 骗 Lighthouse,线上反而更慢)。把 LCP 的裁决权交给线上 RUM,反而让团队关注真实用户。
想听大家在什么场景下怎么选
- 你们线上 RUM 的 P75 LCP 和 CI Lighthouse 的 LCP,偏差大概多少?4G 模拟下是不是更离谱?
- 有没有人真的做到"发布后 RUM 超阈值自动回滚"?回滚的判定窗口设多长?15 分钟够吗(有些用户要 20 分钟才打开页面)?
- bundle 增量预算你们设多少?50KB 是拍脑袋还是从历史数据回归出来的?
- 分级预算怎么落地:是 Lighthouse CI 的 config 里按路由写不同阈值,还是发布后 RUM 看板按页面拆?
- 最实际的问题:硬卡 CI 之后,你的团队"绕过"门禁的方式是什么?(我见过最经典的:把
<img>换成<link rel=preload>+ 懒加载,Lighthouse 分数上去了,但真实用户首屏白屏时间反而变长了。)
欢迎拿你们的数据和踩坑来聊,最好带具体数字。