K 的一隅

浏览器 浏览器导航与页面加载

首屏为什么迟迟不出现:关键渲染路径与页面加载性能

从展览入口搭建说起,理解 DOM、CSSOM、Render Tree、阻塞资源、FCP、LCP 与首屏排障。

5 分钟阅读 更新于 2026-07-24
本文目录

展览入口要先搭骨架、挂灯光、摆最重要的展品。只要入口还被施工挡住,观众就看不到主要内容;但后台仓库里仍可以同时整理不影响开门的物品。页面首屏也是一条关键路径:有些资源阻塞第一次绘制,有些可以等用户真的需要时再加载。

先猜:HTML 到了为什么还是白屏

html
<link rel="stylesheet" href="all.css">
<script src="large.js"></script>
<main>欢迎来到页面</main>

HTML 很快下载完成,不代表用户立刻看见“欢迎来到页面”。CSS 可能还在下载,普通脚本可能阻塞解析,脚本本身又可能等待样式表;浏览器在无法安全计算最终样式和结构时,会推迟绘制。Network 里 Document 结束和屏幕出现内容是不同时间点。

从 DOM、CSSOM 到 Render Tree

首屏要出现,关键依赖不是 HTML 单独完成,而是 DOM 与 CSSOM 汇合后走完渲染链路。

DOM 描述页面节点,CSSOM(CSS Object Model,CSS 对象模型)描述规则;浏览器将可见节点和计算样式组合成 Render Tree(渲染树),再进行 layout(布局)、paint(绘制)和 compositing(合成)。

js
performance.mark('after-dom')
requestAnimationFrame(() => performance.mark('first-frame-candidate'))

这段标记可以帮助你把业务时刻和浏览器绘制机会放到时间线上,但它不等于真正的 FCP。首屏要看用户实际看到的内容是否完成绘制,而不是只看 DOM 是否存在。

什么是阻塞资源

render-blocking resources(阻塞渲染资源)通常包括影响首屏样式的 CSS;parser-blocking script(阻塞解析脚本)会让 HTML 解析器在执行点暂停。一个资源是否阻塞,不只取决于文件类型,还受媒体条件、脚本属性、位置、缓存和依赖关系影响。

html
<link rel="stylesheet" href="critical.css">
<link rel="stylesheet" href="print.css" media="print">
<script defer src="app.js"></script>

把非首屏样式拆开,给脚本使用 defer,可能减少阻塞;但如果拆分错误,首屏先出现无样式内容,再跳成完整样式,用户体验反而更差。性能优化的目标不是让某个请求越早出现,而是让用户尽快看到稳定且有意义的内容。

FCP 和 LCP 看的是不同问题

FCP(First Contentful Paint,首次内容绘制)表示页面第一次绘制出文字、图片或其他内容的大致时刻;LCP(Largest Contentful Paint,最大内容绘制)关注视口里最大的主要内容何时出现,常常是首图、标题或大块文本。

如果 FCP 很早但 LCP 很晚,用户可能先看到一个小标题或加载壳,主要内容仍在等待图片、字体、CSS 或数据。反过来,FCP 晚可能说明关键 CSS、HTML、脚本或服务器响应本身就阻塞了起点。

这两个指标是用户体验的测量入口,不是“低于某个数字就完美”的保证。设备、网络、页面内容和用户滚动位置都会影响实际值。

图片和字体如何影响首屏

首屏主图常常是 LCP 候选。给图片明确尺寸可以降低布局偏移,使用合适格式和尺寸可以减少下载;但盲目 preload 所有图片会抢占 CSS 和脚本的带宽。

html
<link rel="preload" as="image" href="/hero.webp" fetchpriority="high">
<img src="/hero.webp" width="1200" height="700" alt="产品展示">

字体也可能改变 LCP 和视觉稳定性。字体文件太大、字体 CSS 太晚、字体交换策略不合适,都会让文字先空白、后跳变,或让最终标题宽度变化。字体优化要结合实际文本和 Network 优先级,不是把所有 woff2 都预加载。

preload、prefetch 和“提前一切”

preload 表示当前页面很快需要的高优先级资源;prefetch 更像为未来导航准备的低优先级资源。两者都只是提示,浏览器仍会根据连接、缓存、设备和资源优先级决定是否以及何时使用。

html
<link rel="preload" as="style" href="critical.css">
<link rel="prefetch" href="next-route.js">

如果 as 写错,或者 preload 的资源最终没有被使用,会产生警告和浪费。把所有资源都标成重要,相当于给所有展品都安排同一辆货车,真正的入口材料反而无法优先到达。

Network 和 Performance 各自回答什么

Network 瀑布图适合看请求何时发现、排队、连接、等待响应、下载和命中缓存。Performance 时间线适合看 HTML 解析、脚本执行、样式计算、布局、绘制和用户可见事件。两者必须对照:请求结束后是否立刻开始解析?脚本下载完成后是否长时间执行?图片到了但是否等布局或解码?

js
performance.mark('data-ready')
requestAnimationFrame(() => performance.measure('ready-to-frame', 'data-ready'))

“首屏慢”至少可能有三类:服务器和 HTML 到得慢;资源或脚本阻塞第一次绘制;内容已到但主线程或布局让它迟迟没有画出。先分类,再看对应面板,避免只调一个最长请求。

和运行中重排重绘的区别

现有渲染文章重点讲页面已经运行后修改 DOM 的成本;本文讲初次导航时,浏览器如何从响应构建 DOM/CSSOM,并决定什么时候第一次绘制。两者共享 layout、paint、compositing 这些概念,却处在不同时间阶段。

首屏优化结束后,用户点击、滚动和动态数据仍会触发后续渲染。只优化首次出现而让交互阶段长任务不断,用户仍会觉得页面不快;只优化运行中动画而让首次 CSS 阻塞几秒,也不完整。

容易踩的坑

DOM 早出现不等于首屏快:缺 CSS 或被脚本挡住时,仍可能迟迟不能稳定绘制。preload 也不是越多越好,错误预加载会抢走真正关键资源的带宽。FCP 好看不代表 LCP 好看——页面可能先画出一个很小的加载壳。Lighthouse 高分同样不能保证真机流畅,真实设备、网络和交互路径仍可能完全不同。