页面性能:如何系统优化页面|浏览器篇
# 页面性能:如何系统优化页面
30 秒速记
- 页面性能应按生命周期拆解为加载、交互、关闭三个阶段,面试分析重点通常放在前两个阶段。
- 加载阶段覆盖请求发出到完整页面呈现,主要检查网络传输、关键资源以及
JavaScript对首次渲染的阻塞。 - 交互阶段覆盖页面可用后的持续操作,核心指标是每帧能否及时生成,主要矛盾集中在主线程上的
JavaScript与渲染工作。 - 关闭阶段负责页面退出前的清理;它通常不决定显示和操作速度,但仍需处理必要的资源释放。
- 优化时不能只列零散手段,应先定位问题属于哪个阶段,再沿该阶段的渲染路径寻找瓶颈。
页面性能要按生命周期拆成加载、交互和关闭三个阶段,其中优化重点通常是加载与交互。 加载阶段决定页面多久能完整呈现,主要受网络、关键资源和 JavaScript 阻塞影响;交互阶段决定操作是否流畅,本质上看主线程能否及时生成每一帧。关闭阶段更多是做资源清理,一般不直接影响显示和响应速度。实际排查时,我会先判断问题发生在哪个阶段,再沿对应的渲染链路找瓶颈。
在前面几篇文章中,我们分析了页面加载和 DOM 生成,讨论了 JavaScript 和 CSS 是如何影响到 DOM 生成的,还结合渲染流水线来讲解了分层和合成机制,同时在这些文章里面,我们还穿插说明了很多优化页面性能的最佳实践策略。通过这些知识点的学习,相信你已经知道渲染引擎是怎么绘制出帧的,不过之前我们介绍的内容比较零碎、比较散,那么今天我们就来将这些内容系统性地串起来。
那么怎么才能把这些知识点串起来呢?我的思路是从如何系统优化页面速度的角度来切入。
这里我们所谈论的页面优化,其实就是要让页面更快地显示和响应。由于一个页面在它不同的阶段,所侧重的关注点是不一样的,所以如果我们要讨论页面优化,就要分析一个页面生存周期的不同阶段。
通常一个页面有三个阶段:加载阶段、交互阶段和关闭阶段
- 加载阶段,是指从发出请求到渲染出完整页面的过程,影响到这个阶段的主要因素有网络和 JavaScript 脚本。
- 交互阶段,主要是从页面加载完成到用户交互的整合过程,影响到这个阶段的主要因素是 JavaScript 脚本。
- 关闭阶段,主要是用户发出关闭指令后页面所做的一些清理操作
这里我们需要重点关注加载阶段和交互阶段,因为影响到我们体验的因素主要都在这两个阶段,下面我们就来逐个详细分析下。
面试官追问
追问 1运营说首页白屏要等很久,但进入页面后筛选、滚动都很流畅;研发却准备先治理动画掉帧,你会把排查方向放在哪个阶段?
应先定位加载阶段,而不是优先治理交互帧率。故障发生在完整页面呈现之前,应检查网络以及 HTML、CSS、JavaScript 等关键资源;交互流畅只能说明页面加载后的渲染压力暂时不突出。
追问 2一个数据看板上线后首屏正常,但十几个图表联动时持续卡顿,项目经理要求继续压缩首页资源,你会怎样调整优化任务?
应把主要精力转到交互阶段,分析脚本执行和每帧生成成本。压缩资源主要改善加载链路,难以直接解决页面加载后主线程被 JavaScript 或渲染任务占用的问题;仍需保留加载指标,避免局部优化造成回退。
追问 3同一页面在公司网络下打开慢、操作顺,在低配设备上打开正常、操作卡,你会把两个现象合并成一个性能缺陷处理吗?
不宜直接合并,两者分别指向加载阶段和交互阶段。前者优先检查网络与关键资源,后者应检查脚本及渲染帧生成过程;只有采集到共同的长任务或资源依赖证据后,才适合归并根因。
追问 4线上用户只反馈“页面慢”,监控里接口耗时和点击响应都没有明显异常,你会怎样先缩小故障范围?
先按页面生命周期复现场景,确认慢发生在请求到完整呈现之间,还是加载完成后的交互期间。随后分别记录关键资源链路或交互帧生成过程;若用户描述无法区分阶段,不能仅凭接口数据宣布页面正常。
追问 5架构评审中有人主张加载、交互和关闭三个阶段平均投入性能预算,你会接受这种资源分配吗?
不会机械平均分配,应重点保障加载阶段的显示速度和交互阶段的响应速度。关闭阶段主要执行清理操作,仍需避免异常或遗漏,但它通常不是本文所讨论体验问题的首要优化对象;具体优先级还要服从实际故障证据。
# 加载阶段
30 秒速记
- 加载优化应围绕关键资源展开:首次
HTML、参与构建DOM的JavaScript、参与形成渲染树的CSS会阻塞首次渲染,图片、音频和视频通常不属于这一路径上的阻塞资源。 - 首屏耗时可从三个可量化维度检查:关键资源数量、传输体积,以及取得这些资源所需的网络往返。
- 减少数量的核心是把非首屏必需资源移出关键路径;小型脚本或样式也可内联,但会增加
HTML体积,不能无条件使用。 - 降低体积可通过压缩代码、删除无效内容及缩小关键代码范围实现;目标不是压缩所有资源,而是优先缩短阻塞渲染的部分。
- 并行发现并请求的
CSS与JavaScript会出现传输重叠,关键路径不能简单把每个请求的耗时相加。 - 降低网络成本既可减少请求和传输量,也可利用
CDN缩短单次RTT;原文以约14KB数据包估算RTT,这是特定简化模型,不应当作所有现代协议与网络环境的固定常数。
加载阶段要优先优化关键资源,也就是会阻塞首次渲染的 HTML、CSS 和 JavaScript。 我一般从资源数量、体积和网络往返成本入手,把非首屏必需内容移出关键路径,并压缩真正影响首屏的代码。并行请求的样式和脚本存在时间重叠,不能简单累加每个请求的耗时;使用 CDN 还能缩短单次 RTT。小资源可以考虑内联,但会增大 HTML,而约 14KB 一个数据包只是原文中的简化估算。
我们先来分析如何系统优化加载阶段中的页面,还是先看一个典型的渲染流水线,如下图所示

观察上面这个渲染流水线,你能分析出来有哪些因素影响了页面加载速度吗?下面我们就先来分析下这个问题。
通过前面文章的讲解,你应该已经知道了并非所有的资源都会阻塞页面的首次绘制,比如图片、音频、视频等文件就不会阻塞页面的首次渲染;而 JavaScript、首次请求的 HTML 资源文件、CSS 文件是会阻塞首次渲染的,因为在构建 DOM 的过程中需要 HTML 和 JavaScript 文件,在构造渲染树的过程中需要用到 CSS 文件。
我们把这些能阻塞网页首次渲染的资源称为关键资源。基于关键资源,我们可以继续细化出来三个影响页面首次渲染的核心因素。
第一个是关键资源个数。关键资源个数越多,首次页面的加载时间就会越长。比如上图中的关键资源个数就是 3 个,1 个 HTML 文件、1 个 JavaScript 和 1 个 CSS 文件。
第二个是关键资源大小。通常情况下,所有关键资源的内容越小,其整个资源的下载时间也就越短,那么阻塞渲染的时间也就越短。上图中关键资源的大小分别是 6KB、8KB 和 9KB,那么整个关键资源大小就是 23KB。
第三个是请求关键资源需要多少个 RTT(Round Trip Time)。那什么是 RTT 呢? 在《02 | TCP 协议:如何保证页面文件能被完整送达浏览器?》这篇文章中我们分析过,当使用 TCP 协议传输一个文件时,比如这个文件大小是 0.1M,由于 TCP 的特性,这个数据并不是一次传输到服务端的,而是需要拆分成一个个数据包来回多次进行传输的。RTT 就是这里的往返时延。它是网络中一个重要的性能指标,表示从发送端发送数据开始,到发送端收到来自接收端的确认,总共经历的时延。通常 1 个 HTTP 的数据包在 14KB 左右,所以 1 个 0.1M 的页面就需要拆分成 8 个包来传输了,也就是说需要 8 个 RTT
我们可以结合上图来看看它的关键资源请求需要多少个 RTT。首先是请求 HTML 资源,大小是 6KB,小于 14KB,所以 1 个 RTT 就可以解决了。至于 JavaScript 和 CSS 文件,这里需要注意一点,由于渲染引擎有一个预解析的线程,在接收到 HTML 数据之后,预解析线程会快速扫描 HTML 数据中的关键资源,一旦扫描到了,会立马发起请求,你可以认为 JavaScript 和 CSS 是同时发起请求的,所以它们的请求是重叠的,那么计算它们的 RTT 时,只需要计算体积最大的那个数据就可以了。这里最大的是 CSS 文件(9KB),所以我们就按照 9KB 来计算,同样由于 9KB 小于 14KB,所以 JavaScript 和 CSS 资源也就可以算成 1 个 RTT。也就是说,上图中关键资源请求共花费了 2 个 RTT。
了解了影响加载过程中的几个核心因素之后,接下来我们就可以系统性地考虑优化方案了。总的优化原则就是减少关键资源个数,降低关键资源大小,降低关键资源的 RTT 次数
- 如何减少关键资源的个数?一种方式是可以将 JavaScript 和 CSS 改成内联的形式,比如上图的 JavaScript 和 CSS,若都改成内联模式,那么关键资源的个数就由 3 个减少到了 1 个。另一种方式,如果 JavaScript 代码没有 DOM 或者 CSSOM 的操作,则可以改成 sync 或者 defer 属性;同样对于 CSS,如果不是在构建页面之前加载的,则可以添加媒体取消阻止显现的标志。当 JavaScript 标签加上了 sync 或者 defer、CSSlink 属性之前加上了取消阻止显现的标志后,它们就变成了非关键资源了。
- 如何减少关键资源的大小?可以压缩 CSS 和 JavaScript 资源,移除 HTML、CSS、JavaScript 文件中一些注释内容,也可以通过前面讲的取消 CSS 或者 JavaScript 中关键资源的方式。
- 如何减少关键资源 RTT 的次数?可以通过减少关键资源的个数和减少关键资源的大小搭配来实现。除此之外,还可以使用 CDN 来减少每次 RTT 时长。
在优化实际的页面加载速度时,你可以先画出优化之前关键资源的图表,然后按照上面优化关键资源的原则去优化,优化完成之后再画出优化之后的关键资源图表。
面试官追问
追问 1首屏包含一个 6KB 的 HTML、一个 8KB 的脚本和一个 9KB 的样式文件,评审者把三次下载的 RTT 直接串行相加,你会怎么纠正?
不能默认三条请求完全串行,浏览器接收 HTML 后会预解析并尽快发现脚本和样式,使后两者的请求区间重叠。按题源的简化模型,应计算 HTML 的路径,再取并行资源中体积较大者;真实结果仍应以网络瀑布图为准。
追问 2团队准备把首页全部 CSS 和 JavaScript 内联到 HTML,理由是把三个关键资源降成一个,你会批准吗?
只能对体积较小且首屏必需的内容有选择地内联,不能把减少请求数当成唯一目标。全部内联会增大首次 HTML,关键资源总大小和传输轮次仍可能上升;非首屏脚本或样式应优先移出阻塞路径。
追问 3一个活动页把首屏脚本从 8KB 扩到 30KB,请求数量没有变化,负责人因此认定首次渲染不受影响,你怎么判断?
请求数不变并不代表关键路径不变,关键资源体积增大会增加下载时间,并可能需要更多传输轮次。应重新画出关键资源图并检查脚本与样式的并发关系;仅统计文件个数会漏掉资源大小和 RTT 的影响。
追问 4线上首屏图片有几兆,监控显示完整内容出现很晚,开发直接认定图片阻塞了首次绘制,你会要求怎样验证?
应区分首次绘制与完整页面呈现,图片、音频和视频通常不阻塞首次渲染。先在网络瀑布图中确认 HTML、CSS、JavaScript 的关键路径,再观察大图何时完成;图片仍可能拖慢内容完整显示,但不能据此等同于阻塞首次绘制。
追问 5两个优化方案发生冲突:方案甲继续合并关键文件,方案乙保持文件结构并通过 CDN 缩短网络距离,你会依据什么选?
应比较关键资源个数、体积、依赖关系以及每次 RTT 的实际耗时,而不是固定选择合并或 CDN。合并可能减少请求却放大单个资源,CDN 主要缩短往返时延;最终要用优化前后的关键资源图验证收益。
