web-vitals-cn

Category: Coding Risk: Low risk niuwoai/skills CC-BY-4.0

name: web-vitals-cn
description: 国内网络环境下的 Web 首屏与性能优化。当页面打开慢、LCP 差、白屏时间长、移动端卡顿、字体闪烁、或者要在弱网和国产浏览器上提速时使用。触发词:首屏慢、白屏、LCP、CLS、INP、Web Vitals、打包体积大、加载慢、CDN、字体加载、图片优化、弱网、性能优化。不负责后端接口性能(走 mysql-slow-query 或 go-service-release)。

国内网络下的前端性能优化

国内的性能优化和海外教程有几处关键差异:Google Fonts 和多数海外 CDN 不可用或极慢、运营商链路质量差异大、安卓中低端机占比高、微信内置浏览器(X5/XWeb)行为特殊。照抄海外最佳实践会踩坑。

一、先测量,别猜

三层数据缺一不可:

  • 实验室数据:Lighthouse / WebPageTest。用来定位问题。
  • 真实用户数据(RUM):用 web-vitals 库上报 LCP、INP、CLS,按机型、网络、地区切片。这才是判断依据,实验室数据经常和真实差一个量级。
  • 首屏瀑布图:Chrome DevTools 的 Network 面板,勾选「Disable cache」和网络限速(Slow 4G)。

看 RUM 的时候看 P75,不是平均值。平均值会被高端机拉好看。

核心指标的目标线:

指标 需要改进
LCP(最大内容绘制) ≤ 2.5s > 4s
INP(交互到下次绘制) ≤ 200ms > 500ms
CLS(累积布局偏移) ≤ 0.1 > 0.25

二、按收益排序的动作

第一档:网络层(收益最大,改动最小)

  1. 静态资源上国内 CDN,并开启 Brotli 压缩。这一步通常比后面所有代码优化加起来收益都大。
  2. HTML 不要缓存,静态资源用内容哈希 + 长缓存Cache-Control: max-age=31536000, immutable)。
  3. 首屏关键资源加 <link rel="preload">,尤其是首屏图片和关键字体。
  4. 减少首屏请求的域名数量。每多一个域名就多一次 DNS + TCP + TLS,弱网下代价很大。
  5. 开 HTTP/2 或 HTTP/3。国内部分老旧代理对 HTTP/3 支持不好,要灰度验证。

第二档:字体(国内特有的大坑)

  • 绝对不要用 Google Fonts。 国内访问 fonts.googleapis.com 经常超时,会造成长时间白屏或字体闪烁。改成自托管。
  • 中文字体必须子集化。 一个完整的中文字体动辄 5 到 10 MB。用 fonttools 按实际用到的字符做子集,或者用 unicode-range 分片按需加载。标题用自定义字体、正文用系统字体,是最省的方案。
  • font-display: swap 避免不可见文本。但 swap 会带来字体切换的抖动,正文量大时用 optional 更稳。
  • 系统字体栈在中文环境下已经够好看,能不自定义就不自定义:
font-family: -apple-system, BlinkMacSystemFont, "PingFang SC",
  "Hiragino Sans GB", "Microsoft YaHei", sans-serif;

第三档:图片

  • 格式:WebP 兼容性已足够,AVIF 在国产浏览器上要做降级。用 <picture> 给多格式。
  • 尺寸:按设备宽度给 srcset,不要给手机推 2000px 宽的图。
  • 首屏图片不要懒加载,反而会拖慢 LCP。首屏之外的才加 loading="lazy"
  • 必须写 widthheight(或 aspect-ratio),否则图片加载完会撑开布局,直接毁掉 CLS。
  • LCP 元素通常就是首屏那张大图,给它加 fetchpriority="high"

第四档:JavaScript

  • 首屏 JS 体积是 LCP 和 INP 的主要杀手。 目标是首屏 JS 压缩后 150KB 以内。
  • 路由级代码分割,非首屏组件动态导入。
  • 删掉没用的依赖。用 bundle 分析工具看体积构成,经常能发现一个 moment 或者整个 lodash 被全量引入。
  • 第三方脚本延后。统计、客服、埋点这类用 defer 或者在 load 之后再插入。第三方脚本是性能问题里最常见的隐形元凶,且不受你控制。
  • 长任务拆分。INP 差通常是某个事件处理函数里做了几百毫秒的同步计算,拆成分片或挪进 Worker。

第五档:渲染策略

  • 内容型页面优先 SSR 或静态预渲染。纯客户端渲染的首屏必然要等 JS 下载 + 解析 + 请求数据,在弱网下很难看。
  • SSR 之后注意 hydration 成本,大页面的 hydration 本身就是长任务。考虑局部 hydration。
  • 骨架屏改善的是体感,不改善指标,但值得做。

三、微信内置浏览器

国内绕不开的特殊环境:

  • 内核版本落后于同期 Chrome,新 API 要做特性检测。
  • 部分 CSS 和视频行为不一致,尤其是自动播放策略。
  • 分享卡片需要按微信规则配置,且要在真机上验证(开发者工具经常和真机不一致)。
  • 页面被微信预加载时,某些计时指标会失真,RUM 上报要注意识别。

规则:所有性能改动都要在真实微信里、用一台中低端安卓机验证一遍。iPhone 上快不代表用户那里快。

四、验收

改完之后必须给出前后对比,只说「优化了」不算数:

优化前 优化后
LCP (P75, 真实用户)
INP (P75)
CLS (P75)
首屏 JS 压缩体积
首屏请求数
弱网(Slow 4G)白屏时间

RUM 数据要等一到两天才有统计意义,别改完当天就宣布成功。

五、常见误区

  • 只在 Wi-Fi 和高端机上测。 用户的 P75 是中端安卓 + 4G。
  • 为了指标好看把首屏内容砍掉。 LCP 变好了,但用户看到的是空页面。
  • 无脑上懒加载,把首屏图也懒了,LCP 反而变差。
  • 迷信打包体积。 200KB 的 JS 如果不阻塞首屏,不如 50KB 但同步执行的糟糕。
  • 忽略第三方脚本。 自己的代码优化到极致,一个客服 SDK 拖后腿。
  • 上了 CDN 就不管缓存策略,HTML 被缓存导致发版不生效,比慢更糟。