跳到主要内容

白屏优化

面试速答(30 秒版 TL;DR)

白屏时间(White Screen Time)可以粗暴理解成:用户打开页面后,长时间看不到任何有意义内容的那段时间。

它通常不是单点问题,而是整条链路叠加:

  1. 服务端返回 HTML 太慢,TTFB 高。
  2. 首屏依赖资源太多,CSS / JS / 图片下载慢。
  3. JS 阻塞解析或阻塞首屏渲染。
  4. 首屏必须执行的逻辑过重,内容迟迟挂不上来。
  5. 明明数据还没到,却没有骨架屏或占位策略,用户只能盯着空白。

面试里推荐一句话概括:

白屏优化的本质,是让用户尽快看到“第一批有意义内容”,因此要同时处理服务端响应、关键资源交付、首屏渲染路径和感知层占位。


先分清楚:白屏不等于页面慢

页面慢是总体现象,白屏是“看不到内容”这个阶段特别长。

举两个常见场景:

  • 页面 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 拉起页面,白屏风险更高后台系统、交互密集页
SSRHTML 先到,首屏内容通常更早出现首屏体验敏感页面
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. 渐进渲染比“全部准备好再上屏”更实用

有些页面天然适合分阶段展示:

  1. 先显示导航、页头、容器骨架
  2. 再显示首屏核心卡片
  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:接口都等齐再渲染更稳。 这通常会把首屏显示时间无意义地推迟。


速记要点

  1. 白屏优化先看链路:TTFB、关键资源、JS 执行、数据等待、感知占位。
  2. 服务端让 HTML 更早回来,前端让关键资源更早到,页面让首批内容更早显示。
  3. 骨架屏优化的是“感知”,SSR / SSG / 代码分割优化的是“真实交付路径”。
  4. 排查时先分清是服务端慢、资源慢、执行慢,还是没有渐进展示策略。