白屏三秒不是原罪 但用户已经走了
首屏慢,第一反应常是换框架。真的,我听过太多次。Vue 换 React,Next 换 Nuxt,最后白屏从 3.2 秒变成 3.1 秒。问题不在框架。
先把首屏定义清楚。是 HTML 到达?是 FCP?还是 LCP 那一下?不同业务答案不一样。资讯站可能标题出来就算首屏,后台系统可能要等表格可点。用 FCP、LCP、TTFB、INP 这些指标,比盯着 load 事件有用。load 很晚,用户早跑了。
接着看瀑布图。DNS、TLS、排队、服务端响应、内容下载、解析、执行。哪一段最长?很多团队一上来就压图片,结果 TTFB 干到 1.8 秒。前端再瘦,也救不了。

服务端渲染和静态生成不是万能。它们把压力从浏览器挪到服务器。缓存没做好,SSR 反而更慢。边缘缓存、ISR、流式输出,都是取舍。流式 SSR 可以先吐壳,再补内容。但 hydration 一到,主线程还是会忙。页面看起来好了,点不动,照样挨骂。
资源优先级是隐蔽战场
浏览器不读心。它按自己的规则猜谁重要。关键 CSS 内联,首屏字体 preload,首屏图给高优先级。别把首屏大图设成 loading=lazy。这不是优化,是拖后腿。用 fetchpriority=high,尺寸给足,格式上 AVIF 或 WebP。图片 CDN 切图,别让手机下桌面大图。
JS 更麻烦。拆包要按路由,按交互,别按感觉。动态 import 能救命,但拆太碎也糟。HTTP/2 下多路复用,请求数没那么致命,可压缩率会掉,解析成本还在。一个小 chunk 里塞满 polyfill,首屏照样卡。现代浏览器给 module,老浏览器给 nomodule,这招现在意义小了。基线变了。
preload 别乱用。浏览器警告 unused preload 不是闲的。抢了带宽,关键资源反而晚。prefetch 留给下一跳,别在首屏塞一堆。preconnect 只给真正会用的域,第三方统计域也 preconnect,等于给广告开门。

字体也是债。自定义字体好看,但闪字、阻塞、大文件。子集化,只留用到的字形。font-display: swap 让文字先出来。但 swap 会跳。品牌方可能不接受。那就 preload 关键字体,或者干脆用系统字体栈。系统字体不酷,可它快。
指标别变成新的八股

Lighthouse 分数高,线上不一定好。实验室网络太干净。真机、弱网、低端 CPU,才是日常。上 RUM,采 LCP、INP、CLS,按页面、地区、设备分。别只看平均值。长尾用户才是痛的那批。
第三方脚本是黑洞。客服、埋点、A/B、热图,每个都加一点,主线程就满了。能延迟就延迟,能服务端就服务端。但延迟埋点可能丢转化数据。这事得和业务吵。吵完再决定。没有免费午餐。
缓存策略也要拎清。HTML 短缓存或协商缓存,静态资源带 hash 强缓存。CDN 回源率、命中率,值得盯。Service Worker 能预缓存,可更新逻辑一复杂,用户可能卡在旧版本。别为了首屏,把更新搞成玄学。
还有,性能预算放进 CI。主包超了,构建失败。听起来粗暴,但有用。代码评审时多问一句:这个依赖真的需要吗?它多大?有没有更小的替代?很多优化不是技术难题,是习惯问题。
首屏优化没有银弹。有的话,早被封装成 npm 包了。可那个包本身,也会让首屏更慢。