白屏优化
面试速答(30 秒版 TL;DR)
白屏时间(White Screen Time)可以粗暴理解成:用户打开页面后,长时间看不到任何有意义内容的那段时间。
它通常不是单点问题,而是整条链路叠加:
- 服务端返回 HTML 太慢,TTFB 高。
- 首屏依赖资源太多,CSS / JS / 图片下载慢。
- JS 阻塞解析或阻塞首屏渲染。
- 首屏必须执行的逻辑过重,内容迟迟挂不上来。
- 明明数据还没到,却没有骨架屏或占位策略,用户只能盯着空白。
面试里推荐一句话概括:
白屏优化的本质,是让用户尽快看到“第一批有意义内容”,因此要同时处理服务端响应、关键资源交付、首屏渲染路径和感知层占位。
先分清楚:白屏不等于页面慢
页面慢是总体现象,白屏是“看不到内容”这个阶段特别长。
举两个常见场景:
- 页面 3 秒完全加载完成,但 0.8 秒就出现首屏骨架,这叫“整体不一定快,但白屏不长”。
- 页面 2 秒完成,但前 1.8 秒什么都没有,这叫“总时长不算夸张,但白屏很重”。
所以白屏优化更关心的是:
- HTML 什么时候到
- 浏览器什么时候能开始绘制
- 首屏内容什么时候出现
- 在真实内容没准备好前,用户是否至少能看到稳定占位
白屏是怎么产生的?
可以把它拆成下面这条链路:
先看主链路:用户什么时候能看到第一批有意义内容。
这条链路上任何一个环节太慢,都会让空白期拉长。
常见根因可以分四类:
| 分类 | 典型问题 | 现象 |
|---|---|---|
| 服务端 | TTFB 高、SSR 慢、回源慢 | HTML 很晚才开始返回 |
| 资源交付 | JS/CSS/图片太大、域名握手慢 | 关键资源迟迟不到 |
| 前端执行 | 首屏 bundle 大、同步任务重 | DOM 已有但迟迟不渲染 |
| 感知设计 | 没骨架、没占位、无降级 | 用户主观感觉一直空白 |
一、先抓最值钱的指标:TTFB、FCP、LCP
白屏问题不能只靠肉眼判断,最好结合指标。
1. TTFB
HTML 首字节回来得晚,前面所有优化都要往后排队。
如果 TTFB 高,通常先看:
- 服务端处理是否过重
- 是否命中缓存
- 是否有跨地域访问
- 是否存在多层代理或回源慢
2. FCP
First Contentful Paint 表示页面第一次出现文本、图片、非白底画面的时间。
如果 FCP 很晚,往往说明:
- 首屏关键 CSS 太晚
- 首屏 JS 阻塞
- HTML 太薄,必须等 JS 执行后才生成内容
3. LCP
Largest Contentful Paint 关注主要内容完成展示的时间。白屏优化不是只让页面“先有点东西”,也要让关键内容尽快出来。
二、服务端层面:HTML 要更早返回
如果用户迟迟连 HTML 都拿不到,前端再怎么微调也有限。
1. 降低 TTFB
常见抓手:
- 页面或接口结果缓存
- CDN 边缘缓存静态 HTML 或预渲染结果
- 减少 SSR 阻塞查询
- 降低服务端模板拼装成本
2. 选择合适的渲染方式
不同站点适合的策略不一样:
| 方案 | 白屏表现 | 适合什么 |
|---|---|---|
| CSR | 依赖 JS 拉起页面,白屏风险更高 | 后台系统、交互密集页 |
| SSR | HTML 先到,首屏内容通常更早出现 | 首屏体验敏感页面 |
| SSG / 预渲染 | 首屏交付最稳定 | 内容站、营销页、文档站 |
面试里可以这么说:
白屏严重时,我会先判断当前页面是不是过度依赖 CSR。如果首屏内容高度固定、SEO 或首屏体验要求高,SSR / SSG 往往比继续堆客户端优化更直接。
三、资源交付层:让关键资源更早、更少、更稳
1. 首屏 JS 不能太重
CSR 页面里,首屏常见瓶颈不是“网络下不动”,而是:
- bundle 太大
- 下载后解析和执行也很重
典型手段:
- 路由级代码分割
- 首屏只保留关键逻辑
- 图表、编辑器、监控 SDK 非首屏延后
- 避免一个首页加载一堆只在次级交互才会用到的依赖
2. CSS 要尽量不要拖住首屏
浏览器要先拿到足够的 CSS 才能稳定渲染页面,所以应优先保证:
- 首屏关键样式尽快到
- 非关键样式延后
- 避免用层层
@import拉长关键链路
3. 关键图片不要缺席,非关键图片不要抢带宽
这类取舍对白屏体感影响很大:
- 首屏 Hero 图、主封面图不能到得太晚
- 首屏外图片尽量懒加载
- 图片尺寸要控制,避免图片资源压住 CSS / JS
四、前端执行层:别让 JS 把首屏卡死
即使 HTML、CSS、JS 都下载好了,白屏仍可能发生,因为浏览器主线程还要执行一大堆同步逻辑。
常见问题:
- 初始化阶段做大量计算
- 页面一挂载就串行请求多个接口
- 首屏组件树过深
- 大量同步渲染和状态计算
一个典型坏例子
async function bootstrap() {
await loadConfig()
await loadUser()
await loadPermissions()
await loadPageData()
renderApp()
}
这会导致:
- 所有准备工作串行执行
renderApp()被拖到最后- 用户在前面长时间只能看白屏
更合理的思路通常是:
- 能并行就并行
- 能先渲染骨架就别等全量数据
- 非关键依赖延后初始化
async function bootstrap() {
renderShell()
const [config, user] = await Promise.all([loadConfig(), loadUser()])
hydrateBaseState({ config, user })
loadPermissions()
loadNonCriticalModules()
}
这里的关键不是代码长什么样,而是:
- 先让页面“出来”
- 再逐步补齐完整能力
五、感知层优化:骨架屏、占位、渐进渲染
很多白屏问题在技术指标上不一定极端,但用户感知会很差,因为页面在等待期间完全空白。
1. 骨架屏的作用
骨架屏不是让页面真的更快,而是:
- 提前给用户稳定结构
- 减少“页面坏了”的错觉
- 把等待感从“空白”变成“正在填充”
2. 占位要稳定,不能一会儿高一会儿低
如果骨架屏尺寸和最终内容差异太大,会导致:
- 页面跳动
- CLS 上升
- 用户感知更差
3. 渐进渲染比“全部准备好再上屏”更实用
有些页面天然适合分阶段展示:
- 先显示导航、页头、容器骨架
- 再显示首屏核心卡片
- 最后加载次级区域和增强能力
六、白屏优化的实战排查顺序
面试里如果你能说出排查顺序,会比堆技巧更有说服力。
第一步:确认慢在服务端还是前端
先看 Network:
- HTML 请求是否很晚才返回
- 首屏关键资源是否被阻塞
- 是否存在明显瀑布流
第二步:确认慢在下载、执行还是数据等待
再看 Performance:
- 是否有长任务
- 是否有大段脚本执行卡住渲染
- FCP 前是不是一直在等 JS 或接口
第三步:确认感知层有没有兜底
即使指标不算太差,如果没有骨架和占位,用户也会明显感知到白屏。
典型题 & 标准答法
Q1:白屏优化你会从哪些方向入手?
我会按链路拆。先看服务端 TTFB 和当前渲染模式,再看首屏 JS/CSS/图片这些关键资源是否过重,然后看主线程执行是否阻塞渲染,最后补骨架屏和渐进渲染,优化用户感知。
Q2:白屏和首屏慢有什么区别?
白屏强调的是“前面长时间没有任何有意义内容出现”,首屏慢是更广义的体验问题。一个页面可以整体不算很慢,但白屏期很长;也可以很早出现骨架,但完整内容稍后才到。
Q3:为什么骨架屏能改善体验?
骨架屏不一定缩短真实加载时间,但它能更早给出页面结构反馈,让用户知道页面正在加载而不是卡死,同时如果尺寸稳定,也能减少布局跳动。
Q4:CSR 页面为什么更容易白屏?
因为它往往依赖 JS 下载、执行、拉数据之后才能把首屏内容真正挂到页面上。如果 bundle 大、执行重、接口慢,白屏就会明显拉长。
常见误区
-
误区 1:白屏优化就是加骨架屏。 骨架屏只是感知层兜底,不能替代 TTFB、资源和执行层优化。
-
误区 2:所有页面都应该改成 SSR。 SSR 不是免费午餐,也会增加服务端复杂度和回源压力,要看页面类型。
-
误区 3:只看下载时间,不看执行时间。 很多 SPA 的白屏,真正卡的是 JS 解析与执行。
-
误区 4:接口都等齐再渲染更稳。 这通常会把首屏显示时间无意义地推迟。
速记要点
- 白屏优化先看链路:TTFB、关键资源、JS 执行、数据等待、感知占位。
- 服务端让 HTML 更早回来,前端让关键资源更早到,页面让首批内容更早显示。
- 骨架屏优化的是“感知”,SSR / SSG / 代码分割优化的是“真实交付路径”。
- 排查时先分清是服务端慢、资源慢、执行慢,还是没有渐进展示策略。