Web 性能入门
HTTP,第十章
在任何复杂系统中,性能优化的很大一部分工作在于梳理系统众多独立层级之间的交互,每一层都有其自身的约束与限制。至此,我们已经详细审视了诸多独立的网络组件——不同的物理传输方式与传输协议——现在我们可以将注意力转向更大范围的端到端 Web 性能优化图景:
- 延迟与带宽对 Web 性能的影响
- 传输协议(TCP/QUIC)对 HTTP 施加的约束
- HTTP 协议本身的特性与局限
- Web 应用的发展趋势与性能需求
- 浏览器的限制与优化手段
优化所有这些不同层级之间的交互,类似于求解一组相互依赖的方程,尽管如此,仍能得出多种可行解。并不存在一套固定不变的推荐方案或最佳实践,且各个组件本身也在不断演进:浏览器变得更快,用户的连接环境在变化,Web 应用在其范围、抱负和复杂性上持续增长。
因此,在我们深入列举和分析具体的性能最佳实践之前,重要的是退后一步,明确问题的本质:什么是现代 Web 应用,我们拥有哪些工具,如何测量 Web 性能,以及系统的哪些部分在助力或阻碍我们的进展。
超文本、网页与 Web 应用
过去几十年 Web 的演进至少带来了三类不同的体验:超文本文档、富媒体网页和交互式 Web 应用。诚然,后两者之间的界限对用户而言有时可能模糊,但从性能角度看,每一类都需要我们采取截然不同的对话方式、度量指标和性能定义。
超文本文档
超文本文档是万维网的起源,纯文本配合基础格式和超链接支持。按现代标准听来或许平淡无奇,但它证明了万维网的前提、愿景和巨大效用。
网页
HTML 工作组和早期浏览器厂商扩展了超文本的定义,以支持额外的超媒体资源,如图像和音频,并添加了许多用于更丰富布局的原语。网页时代就此到来,使我们能够利用各种媒体类型制作出视觉丰富的布局:视觉上精美,但大多不具备交互性,与印刷页面并无二致。
Web 应用
JavaScript 的加入,以及后来动态 HTML(DHTML)和 AJAX 的革命,再次颠覆了局面,将简单的网页转变为交互式 Web 应用,使其能够直接在浏览器内响应用户操作。这为首批成熟的浏览器应用铺平了道路,如 Outlook Web Access(IE5 中 XMLHTTP 支持的始作俑者),开启了一个脚本、样式表和标记之间复杂依赖关系图的新时代。
HTTP/0.9 会话由单个文档请求组成,这对于超文本的交付而言已完全足够:单个文档,一个 TCP 连接,随后连接关闭。因此,性能调优简化为针对短生命周期 TCP 连接上的单个 HTTP 请求进行优化。
网页的出现将交付公式从单个文档转变为文档及其依赖资源。因此,HTTP/1.0 引入了 HTTP 元数据(头部)的概念,HTTP/1.1 则通过一系列面向性能的基元对其进行了增强,如明确定义的缓存、持久连接(keepalive)等。于是,多个 TCP 连接现在可能同时发挥作用,关键性能指标也从文档加载时间转变为页面加载时间(Page Load Time,简称 PLT)。
PLT 最简单的定义是”浏览器中加载旋转图标停止旋转的时刻”。更技术化的定义是浏览器中 onload 事件的触发时间,即浏览器在文档及其所有依赖资源(JavaScript、图像等)加载完成后触发的事件。
最终,Web 应用将原本以媒体作为标记内容补充的简单网页,转变为复杂的依赖关系图:标记定义基本结构,样式表定义布局,脚本构建最终的交互式应用并响应用户输入,在此过程中可能同时修改样式和标记。
因此,作为 Web 性能领域事实标准的页面加载时间(PLT),也越来越不足以作为性能基准:我们不再构建页面,而是在构建动态的、交互式的 Web 应用。除了测量加载每个资源所需的时间(PLT)之外,我们现在更关注回答应用特定的问题:
- 应用加载过程中的里程碑有哪些?
- 用户首次交互的时间是多久?
- 用户应该参与哪些交互?
- 每个用户的参与度和转化率如何?
你的性能与优化策略的成功,与你定义和迭代应用特定基准与标准的能力直接相关。没有什么比应用特定的知识和测量更有效,尤其是当这些测量与业务的底线目标和指标挂钩时。
DOM、CSSOM 与 JavaScript
当我们提到现代 Web 应用中”脚本、样式表和标记之间的复杂依赖关系图”时,具体指什么?要回答这个问题,我们需要简要绕道浏览器架构,探究解析、布局和脚本管道如何协同工作以将像素绘制到屏幕上。
图 10-1. 浏览器处理管道:HTML、CSS 与 JavaScript
HTML 文档的解析构建了文档对象模型(DOM)。与此同时,还有一个常被遗忘的表亲——CSS 对象模型(CSSOM),它由指定的样式表规则和资源构建而成。两者随后结合创建”渲染树”,至此浏览器拥有足够信息执行布局并在屏幕上绘制内容。到目前为止,一切顺利。
然而,正是在这里,我们不得不引入我们既爱又恨的朋友:JavaScript。脚本执行可以发起同步的 document.write 并阻塞 DOM 解析与构建。同样,脚本可以查询任何对象的计算样式,这意味着 JavaScript 也可能阻塞于 CSS。因此,DOM 和 CSSOM 对象的构建经常交织在一起:DOM 构建无法继续,直到 JavaScript 执行完成;而 JavaScript 执行又无法继续,直到 CSSOM 可用。
你的应用性能,特别是首次加载和”渲染时间”,直接取决于标记、样式表和 JavaScript 之间这种依赖关系图的解析方式。顺便提一下,还记得那个流行的”样式放顶部,脚本放底部”的最佳实践吗?现在你知道原因了!渲染和脚本执行都会阻塞于样式表;尽快将 CSS 交付给用户至关重要。
现代 Web 应用的解剖结构
现代 Web 应用究竟长什么样?HTTP Archive 可以帮助我们回答这个问题。该项目通过定期爬取最流行的网站(来自 Alexa Top 1M 的 30 万+),记录并聚合每个目标网站的资源使用数量、内容类型、头部和其他元数据的分析数据。
根据 HTTP Archive 2024 年的数据,一个典型的现代 Web 应用构成如下:
-
桌面端:约 71 个请求,来自 15+ 个主机,总传输大小约 2.5 MB(中位数)
- HTML:约 2 个请求,~50 KB
- 图像:约 18 个请求,~1,054 KB
- JavaScript:约 24 个请求,~613 KB(已超越图像成为请求数最多的资源类型)
- CSS:约 8 个请求,~100 KB
- 字体:约 4 个请求
- 视频/其他:约 5+ 个请求
-
移动端:约 66 个请求,总传输大小约 2.3 MB(中位数)
当你阅读本文时,上述数字已经发生变化并持续增长;这种上升趋势一直稳定且可靠,没有停止的迹象。然而,抛开确切的请求数和千字节数不谈,这些单个组件的数量级值得我们深思:一个典型的 Web 应用现在已超过 2 MB,由来自 15+ 个不同主机的约 70 个子资源组成!
与桌面应用不同,Web 应用不需要单独的安装过程:输入 URL,按回车,即可运行!然而,桌面应用只需支付一次安装成本,而 Web 应用在每次访问时都在运行”安装过程”——资源下载、DOM 和 CSSOM 构建、JavaScript 执行。难怪 Web 性能是一个如此快速发展的领域和热门话题!数十个资源、数兆字节的数据、数十个不同的主机,所有这些都必须在数百毫秒内协同工作,以实现理想的即时 Web 体验。
速度、性能与人类感知
速度和性能是相对术语。每个应用都基于业务标准、上下文、用户期望和必须执行的任务复杂性,规定了自己的需求集。话虽如此,如果应用必须对用户做出反应和响应,那么我们必须针对特定的、以用户为中心的感知处理时间常数进行规划和设计。尽管生活节奏不断加快,或至少给人这种感觉,我们的反应时间保持不变(表 10-1),无论应用类型(在线或离线)或媒介(笔记本、台式机或移动设备)如何。
| 延迟 | 用户感知 |
|---|---|
| 0–100 ms | 即时 |
| 100–300 ms | 轻微可感知延迟 |
| 300–1000 ms | 机器正在工作 |
| 1,000+ ms | 可能发生注意力转移 |
| 10,000+ ms | 任务被放弃 |
表 10-1. 时间与用户感知
上表有助于解释 Web 性能社区的非官方经验法则:在 250 毫秒内渲染页面,或至少提供视觉反馈,以保持用户参与!
对于应用而言,要感觉即时,对用户输入的可感知响应必须在数百毫秒内提供。超过一秒后,用户的流程和与已启动任务的参与度就会被打破;十秒过去后,除非提供进度反馈,任务经常被放弃。
现在,加上 DNS 查找的网络延迟,接着是 TCP 握手,再加上典型网页请求的几个往返,我们 100–1000 毫秒的延迟预算中,大部分(如果不是全部)可能轻易就被网络开销消耗殆尽;参见图 8-2。难怪如此多的用户,尤其是在移动或无线网络环境下,要求更快的 Web 浏览性能!
Jakob Nielsen 的《可用性工程》和 Steven Seow 的《设计与工程时间》都是每个开发者和设计师都应该阅读的绝佳资源!时间是客观测量的,但感知是主观的,体验可以被工程化以改善感知性能。
将 Web 性能转化为真金白银
速度是一项功能,而且不仅仅是为了速度而追求速度。Google、Microsoft 和 Amazon 广受关注的研究都表明,Web 性能直接转化为真金白银——例如,Bing 搜索页面 2000 毫秒的延迟使用户人均收入下降了 4.3%!
同样,Aberdeen 对 160 多个组织的研究确定,页面加载时间每延迟一秒,转化率就损失 7%,页面浏览量减少 11%,客户满意度下降 16%!
更快的网站带来更多页面浏览量、更高的参与度和更高的转化率。然而,不要只听我们的一面之词,也不要盲目相信被广泛引用的行业基准:测量 Web 性能对你自己网站的影响,以及对你自己转化率指标的影响。如果你想知道如何做,请继续阅读,或跳转到”合成监控与真实用户性能测量”部分。
分析资源瀑布图
任何关于 Web 性能的讨论,如果不提及资源瀑布图,都是不完整的。事实上,资源瀑布图很可能是我们手中最具洞察力的网络性能和诊断工具。每个浏览器都提供查看资源瀑布图的 instrumentation,还有优秀的在线工具,如 WebPageTest,可以为各种浏览器在线渲染瀑布图。
WebPageTest.org 是一个开源项目和免费 Web 服务,提供从全球多个位置测试网页性能的系统:浏览器在虚拟机内运行,可以使用各种连接和浏览器导向设置进行配置和脚本化。测试完成后,结果通过 Web 界面提供,使 WebPageTest 成为你 Web 性能工具包中不可或缺的强大工具。
首先,重要的是认识到每个 HTTP 请求都由多个独立阶段组成(图 10-3):DNS 解析、TCP 连接握手、TLS 协商(如需要)、HTTP 请求发送,随后是内容下载。每个浏览器中这些单独阶段的视觉显示可能略有不同,但为简化起见,本章我们将使用 WebPageTest 版本。确保熟悉你常用浏览器中每种颜色的含义。
图 10-3. HTTP 请求的组成部分(WebPageTest)
对图 10-3 的仔细分析显示,Yahoo! 主页下载耗时 683 毫秒,其中超过 200 毫秒花在了等待网络上,占总请求延迟的 30%!然而,文档请求只是开始,因为我们知道,现代 Web 应用还需要各种资源(图 10-4)来生成最终输出。确切地说,要加载 Yahoo! 主页,浏览器需要 52 个资源,来自 30 个不同的主机,总计 486 KB。
资源瀑布图揭示了页面结构和浏览器处理管道的诸多重要洞察。首先,注意到在获取 www.yahoo.com 文档内容的同时,新的 HTTP 请求正在被分发:HTML 解析是增量进行的,允许浏览器尽早发现所需资源并并行分发必要请求。因此,资源获取的调度很大程度上由标记结构决定。浏览器可能重新排序某些请求,但文档中每个资源的增量发现才是产生独特资源”瀑布效应”的原因。
其次,注意到”开始渲染”(绿色竖线)发生在所有资源完全加载之前,允许用户在页面构建过程中开始与页面交互。事实上,“文档完成”事件(蓝色竖线)也触发得很早,远在剩余资源加载完成之前。换句话说,浏览器旋转图标已停止旋转,用户能够继续其任务,但 Yahoo! 主页正在后台渐进式地填充额外内容,如广告和社交小部件。
图 10-4. Yahoo.com 资源瀑布图(WebPageTest,2013 年 3 月)
上述示例中,首次渲染时间、文档完成与获取最后一个资源完成时间之间的差异,充分说明了讨论不同 Web 性能指标时所需的背景。这三个指标中哪一个才是正确的追踪对象?没有唯一答案;每个应用都不同!Yahoo! 工程师选择优化页面以利用增量加载,允许用户更早开始消费重要内容,为此他们必须运用应用特定的知识,判断哪些内容是关键,哪些可以稍后填充。
不同浏览器实现了不同的逻辑,决定何时以及以何种顺序分发单个资源请求。因此,应用性能会因浏览器而异。
提示:WebPageTest 允许你在运行测试时选择位置和浏览器品牌及版本!
网络瀑布图是一个强大的工具,可以帮助揭示任何页面或应用所选的优化方案(或缺乏优化)。先前分析和优化资源瀑布图的过程通常被称为前端性能分析与优化。然而,这个名称可能是个不幸的选择,因为它误导许多人相信所有性能瓶颈现在都在客户端。现实中,尽管 JavaScript、CSS 和渲染管道是关键且资源密集的步骤,但服务器响应时间和网络延迟(“后端性能”)对于优化资源瀑布图同样关键。毕竟,你无法解析或执行被网络阻塞的资源!
为了说明这一点,我们只需从资源瀑布图切换到 WebPageTest 提供的连接视图(图 10-5)。
与每个记录代表单个 HTTP 请求的资源瀑布图不同,连接视图显示每个 TCP 连接的完整生命周期——本例中全部 30 个——用于获取 Yahoo! 主页的资源。有什么突出的地方吗?注意到下载时间(蓝色指示)只是每个连接总延迟的一小部分:有 15 次 DNS 查找、30 次 TCP 握手,以及大量等待接收每个响应首字节的网络延迟(绿色指示)。
图 10-5. Yahoo.com 连接视图(WebPageTest,2013 年 3 月)
想知道为什么有些请求只显示绿色条(首字节时间)吗?许多响应非常小,因此下载时间在图表上无法显示。事实上,对于许多请求,响应时间通常由往返延迟和服务器处理时间主导。
最后,我们把最好的留到了最后。对许多人来说,真正的惊喜在连接视图的底部:检查图 10-5 中的带宽利用率图表。除了少数短暂的数据突发外,可用连接的利用率非常低——看起来我们并未受限于连接带宽!这是异常现象,还是更糟,浏览器 bug?不幸的是,两者都不是。事实证明,带宽并非大多数 Web 应用的限制性能因素。相反,瓶颈是客户端与服务器之间的网络往返延迟。
性能支柱:计算、渲染与网络
Web 程序的执行主要涉及三个任务:获取资源、页面布局与渲染,以及 JavaScript 执行。渲染和脚本执行步骤遵循单线程、交错执行模型;无法对生成的文档对象模型(DOM)进行并发修改。因此,优化渲染和脚本执行运行时的协同工作方式,正如我们在”DOM、CSSOM 与 JavaScript”部分所见,至关重要。
然而,如果浏览器被阻塞在网络上等待资源到达,优化 JavaScript 执行和渲染管道也无济于事。快速高效的网络资源交付是浏览器中运行的每个应用的性能基石。
但是,有人可能会问,互联网速度每天都在变快,这个问题不会自行解决吗?是的,我们的应用在不断变大,但如果全球平均速度已经达到 3.1 Mbps(网络边缘带宽)并且在增长,正如每个 ISP 和移动运营商的无处不在的广告所证明的那样,为什么还要费心呢,对吧?不幸的是,正如你可能直觉感受到的,正如 Yahoo! 示例所示,如果情况真是如此,你现在就不会在阅读这本书了。让我们仔细看看。
关于带宽和延迟趋势与相互作用的详细讨论,请回顾”延迟与带宽入门”中的”延迟与带宽入门”部分。
带宽并非关键(多数情况下)
且慢;带宽当然重要!毕竟,我们本地 ISP 和移动运营商的每个广告都在不断提醒我们它的诸多好处:更快的下载、上传和流媒体,速度高达 [插入最新数字] Mbps!
访问更高的带宽数据速率总是好的,特别是对于涉及批量数据传输的场景:视频和音频流媒体,或任何其他类型的大数据传输。然而,对于日常 Web 浏览,这涉及从数十个不同主机获取数百个相对较小的资源,往返延迟才是限制因素:
- 从 Yahoo! 主页流式传输高清视频受限于带宽。
- 加载和渲染 Yahoo! 主页受限于延迟。
根据你尝试流式传输的视频质量和编码,你可能需要几百 Kbps 到几 Mbps 的带宽容量——例如,高清 1080p 视频流需要 3+ Mbps。这个数据速率现在对许多用户来说触手可及,这从 Netflix 等流媒体视频服务的日益普及中得到证明。那么,为什么下载一个比高清电影小得多的 Web 应用,对能够流式传输高清电影的连接来说却如此具有挑战性?
延迟才是性能瓶颈
我们已经在前面的章节中涵盖了所有必要主题,可以对为什么延迟可能是日常 Web 浏览的限制因素做出良好的定性理论。然而,一图胜千言,让我们审视 Mike Belshe(SPDY 协议的创造者之一)进行的一项定量研究的结果(图 10-6),关于带宽与延迟的变化对 Web 上一些最受欢迎目的地的页面加载时间的影响。
图 10-6. 页面加载时间与带宽和延迟的关系
Mike Belshe 的这项研究成为 Google 开发 SPDY 协议的起点,后者后来成为 HTTP/2 协议的基础。
在第一项测试中,连接延迟保持固定,连接带宽从 1 Mbps 逐步增加到 10 Mbps。注意到起初,将连接从 1 Mbps 升级到 2 Mbps 几乎使页面加载时间减半——正是我们想要看到的结果。然而,此后带宽的每次增量改进都带来收益递减。当可用带宽超过 5 Mbps 时,我们看到个位数的百分比改进,而从 5 Mbps 升级到 10 Mbps 仅带来 5% 的页面加载时间改进!
Akamai 的宽带速度报告(网络边缘带宽)显示,美国普通消费者已经以 5 Mbps+ 的可用带宽访问 Web——这个数字许多其他国家正在迅速接近或已经超越。因此,我们得出结论,如果美国普通消费者有兴趣提高她的 Web 浏览速度,她从升级连接可用带宽中获益不多。她可能能够更快地上传或流式传输大型媒体文件,但包含这些文件的页面不会明显更快加载:带宽并不重要(多数情况下)。
然而,延迟实验讲述了一个完全不同的故事:每改善 20 毫秒延迟,页面加载时间就有线性改善!也许我们在选择 ISP 时应该优化延迟,而不仅仅是带宽?
“要加速整个互联网,我们应该寻找更多降低 RTT 的方法。如果我们可以将跨大西洋 RTT 从 150 毫秒降低到 100 毫秒,这对互联网速度的影响将大于将用户带宽从 3.9 Mbps 增加到 10 Mbps 甚至 1 Gbps。
另一种降低页面加载时间的方法是减少每次页面加载所需的往返次数。如今,网页需要一定量的客户端与服务器之间的来回通信。往返次数很大程度上是由于开始通信的握手(如 DNS、TCP、HTTP),以及通信协议引起的往返(如 TCP 慢启动)。如果我们能改进协议以用更少的往返传输这些数据,我们也应该能够改善页面加载时间。这是 SPDY 的目标之一。”
—— Mike Belshe,《带宽并不重要(多数情况下)》
上述结果对许多人来说是惊喜,但它们真的不应该令人惊讶,因为它们 underlying 协议性能特征的直接后果:TCP 握手、流量与拥塞控制,以及由于丢包导致的队头阻塞。大多数 HTTP 数据流由小型、突发的数据传输组成,而 TCP 针对长连接和批量数据传输进行了优化。网络往返时间是大多数情况下的 TCP 吞吐量和性能的限制因素;参见”优化 TCP”。因此,延迟也是 HTTP 及其上交付的大多数 Web 应用的性能瓶颈。
如果延迟是大多数有线连接的限制性能因素,那么正如你可能直觉感受到的,它对无线客户端而言是更重要的性能瓶颈:无线延迟显著更高,使网络优化成为移动 Web 的关键优先事项。
合成监控与真实用户性能测量
如果我们能测量它,我们就能改进它。问题是,我们在测量正确的标准吗?过程可靠吗?正如我们前面提到的,测量现代 Web 应用的性能是一个非平凡挑战:没有一个适用于每个应用的单一指标,这意味着我们必须在每种情况下仔细定义自定义指标。然后,一旦标准确立,我们必须收集性能数据,这应该通过合成监控和真实用户性能测量(RUM)的结合来完成。
广义而言,合成监控指的是任何具有受控测量环境的过程:运行性能套件的本地构建流程、针对预发布基础设施的负载测试,或一组定期执行一组脚本操作并记录结果的地理分布式监控服务器。这些测试中的每一个都可能测试基础设施的不同部分(如应用服务器吞吐量、数据库性能、DNS 计时等),并作为稳定的基线,帮助检测回归或缩小系统的特定组件范围。
配置得当,合成监控提供受控且可重现的性能测试环境,这使其非常适合在性能回归到达用户之前识别和修复它们。提示:确定你的关键性能指标,并在合成测试中设定每个指标的”预算”。如果超出预算,触发警报!
然而,合成监控不足以识别所有性能瓶颈。具体来说,问题在于收集的测量数据不能代表现实世界中决定应用最终用户体验的广泛多样性因素。导致这种差距的一些因素包括:
- 场景和页面选择:复制真实用户导航模式很困难。
- 浏览器缓存:性能可能因用户缓存状态而有很大差异。
- 中间件:性能可能因中间代理和缓存而异。
- 硬件多样性:CPU、GPU 和内存性能范围广泛。
- 浏览器多样性:浏览器版本范围广泛,新旧皆有。
- 连接性:真实连接的不断变化的带宽和延迟。
这些因素的组合意味着,除了合成测试外,我们还必须用真实用户测量(RUM)来增强我们的性能策略,以捕获用户实际体验到的应用性能。好消息是,W3C Web 性能工作组通过引入 Navigation Timing API(图 10-7),使这部分数据收集过程变得简单,该 API 现在已在许多现代桌面和移动浏览器中得到支持。
图 10-7. Navigation Timing 暴露的用户特定性能计时器
截至 2024 年,Navigation Timing 已被 Chrome、Firefox、Safari 和 Edge 等主流浏览器的最新版本支持。对于最新状态,参见 caniuse.com/nav-timing。
Navigation Timing 的真正好处在于它暴露了许多以前无法访问的数据,如 DNS 和 TCP 连接时间,具有高精度(微秒时间戳),通过浏览器中标准化的 performance.timing 对象。因此,数据收集过程非常简单:加载页面,从用户浏览器获取计时对象,并将其信标回传到你的分析服务器!通过捕获这些数据,我们可以观察真实用户在真实硬件上、跨各种网络看到的应用真实世界性能。
分析真实用户测量数据
分析性能数据时,始终查看数据的底层分布:扔掉平均值,关注直方图、中位数和分位数。在分析偏态和多峰分布时,平均值会导致无意义的指标。图 10-8 展示了单个网站上两种分布的真实示例:页面加载时间的偏态分布和服务器响应时间的多峰分布(两种模式是由于应用服务器缓存与未缓存页面生成时间所致)。
图 10-8. igvita.com 的页面加载时间(偏态)和响应时间(多峰)分布
确保你的分析工具能为你的性能数据提供正确的统计指标。上述数据来自 Google Analytics,它在标准网站速度报告中提供直方图视图。Google Analytics 在分析跟踪器安装时自动收集 Navigation Timing 数据。同样,还有许多其他分析供应商提供 Navigation Timing 数据收集和报告。
最后,除了 Navigation Timing,W3C 性能组还标准化了另外两个 API:User Timing 和 Resource Timing。Navigation Timing 仅为根文档提供性能计时器,而 Resource Timing 为页面上的每个资源提供类似的性能数据,允许我们收集页面的完整性能配置文件。同样,User Timing 提供一个简单的 JavaScript API,利用相同的高分辨率计时器标记和测量应用特定的性能指标:
function init() {
performance.mark("startTask1");
applicationCode1();
performance.mark("endTask1");
logPerformance();
}
function logPerformance() {
var perfEntries = performance.getEntriesByType("mark");
for (var i = 0; i < perfEntries.length; i++) {
console.log("Name: " + perfEntries[i].name +
" Entry Type: " + perfEntries[i].entryType +
" Start Time: " + perfEntries[i].startTime +
" Duration: " + perfEntries[i].duration + "\n");
}
console.log(performance.timing);
}
- 存储(标记)与名称关联的时间戳(startTask1)。
- 执行应用代码。
- 迭代并记录用户计时数据。
- 记录当前页面的 Navigation Timing 对象。
Navigation、Resource 和 User Timing API 的组合提供了为每个 Web 应用进行仪器化和执行真实用户性能测量的所有必要工具;再也没有借口不把它做对了。我们优化我们所测量的,RUM 和合成测试是互补的方法,帮助你识别应用性能和用户体验中的回归和真实世界瓶颈。
自定义和应用特定的指标是建立可靠性能策略的关键。没有通用的方法来测量或定义用户体验质量。相反,我们必须在每个应用中定义和仪器化特定的里程碑和事件,这个过程需要项目中所有利益相关者的协作:业务负责人、设计师和开发者。
浏览器优化
如果我们不提及现代浏览器远不止一个简单的网络套接字管理器,那将是我们的疏忽。性能是每个浏览器厂商的主要竞争特性,鉴于网络性能是如此关键的标准,浏览器每天都在变得更智能:预解析可能的 DNS 查找、预连接到可能的目的地、预获取和优先处理页面上的关键资源,等等。
执行的确切优化列表因浏览器厂商而异,但其核心可以归为两大类:
文档感知优化
网络栈与文档、CSS 和 JavaScript 解析管道集成,帮助识别和优先处理关键网络资源,尽早分发它们,并尽快使页面进入交互状态。这通常通过资源优先级分配、预解析(lookahead parsing)和类似技术完成。
推测性优化
浏览器可能随时间学习用户导航模式,并执行推测性优化,试图通过预解析 DNS 名称、预连接到可能的主机名等方式预测可能的用户操作。
好消息是,所有这些优化都是自动为我们执行的,通常能节省数百毫秒的网络延迟。话虽如此,理解这些优化在底层如何以及为何工作很重要,因为我们可以协助浏览器,帮助它更好地加速我们的应用。大多数浏览器采用四种技术:
资源预获取与优先级排序
文档、CSS 和 JavaScript 解析器可能向网络栈传递额外信息,指示每个资源的相对优先级:阻塞首次渲染所需的资源获得高优先级,而低优先级请求可能暂时在队列中搁置。
DNS 预解析
可能的主机名提前预解析,以避免未来 HTTP 请求上的 DNS 延迟。预解析可能通过学习的导航历史、用户操作(如悬停在链接上)或页面上的其他信号触发。
TCP 预连接
DNS 解析后,浏览器可能推测性地打开 TCP 连接,预期 HTTP 请求。如果猜测正确,它可以消除另一个完整的往返(TCP 握手)网络延迟。
页面预渲染
一些浏览器允许你提示可能的下一个目的地,并可以在隐藏标签页中预渲染整个页面,以便在用户发起导航时即时切换。
关于这些和其他网络优化在 Google Chrome 中实现的深入探讨,参见《Google Chrome 中的高性能网络》。
从外部看,现代浏览器网络栈呈现为一个简单的资源获取机制,但从内部看,它是如何优化 Web 性能的精心设计和引人入胜的案例研究。那么我们如何协助浏览器完成这一使命?首先,密切关注每个页面的结构和交付:
- CSS 和 JavaScript 等关键资源应尽可能早地在文档中可发现。
- CSS 应尽早交付,以解除渲染和 JavaScript 执行的阻塞。
- 非关键 JavaScript 应延迟加载,以避免阻塞 DOM 和 CSSOM 构建。
- HTML 文档由解析器增量解析;因此,为了获得最佳性能,文档应定期刷新。
此外,除了优化页面结构,我们还可以在文档本身中嵌入额外的提示,告知浏览器它可以为我们执行的额外优化:
<link rel="dns-prefetch" href="//hostname_to_resolve.com">
<link rel="preconnect" href="//example.com">
<link rel="prefetch" href="/images/big.jpeg">
<link rel="prerender" href="//example.org/next_page.html">
<link rel="modulepreload" href="/js/app.mjs">
- 预解析指定主机名。
- 预连接到指定主机(DNS + TCP + TLS)。
- 预获取此页面或未来导航的资源。
- 预渲染指定页面,预期用户的下一个目的地。
- 预加载 JavaScript 模块(ES modules)。
每一个都是推测性优化的提示。浏览器不保证会执行它,但可能使用提示来优化其加载策略。不幸的是,并非所有浏览器都支持所有提示(表 10-2),但如果不支持,提示将被视为无操作(no-op)且无害;尽可能使用上述每种技术。
| 浏览器 | dns-prefetch | preconnect | prefetch | prerender | modulepreload |
|---|---|---|---|---|---|
| Firefox | 3.5+ | 3.5+ | 3.5+ | 不支持 | 不支持 |
| Chrome | 1.0+ | 1.0+ | 1.0+ | 13+ | 63+ |
| Safari | 5.01+ | 11+ | 不支持 | 不支持 | 不支持 |
| Edge | 12+ | 12+ | 12+ | 79+ | 79+ |
表 10-2. 推测性浏览器优化提示
Internet Explorer 9 支持 DNS 预获取,但称之为 prefetch。在 Internet Explorer 10+ 中,dns-prefetch 和 prefetch 等效,在两种情况下都导致 DNS 预获取。
对大多数用户甚至 Web 开发者来说,DNS、TCP 和 SSL 延迟是完全透明的,在很少有人涉足的网络层协商。然而,这些步骤中的每一个对整体用户体验都至关重要,因为每个额外的网络往返都可能增加数十或数百毫秒的网络延迟。通过帮助浏览器预测这些往返,我们可以消除这些瓶颈,交付更快、更好的 Web 应用。
优化 Google 搜索的首字节时间(TTFB)
HTML 文档由浏览器增量解析,这意味着服务器可以且应该尽可能频繁地刷新可用的文档标记。这使客户端能够尽早发现和开始获取关键资源。
Google 搜索提供了这种技术益处的最佳示例之一:当搜索请求到达时,服务器甚至在分析查询之前就立即刷新搜索页面的静态头部。毕竟,为什么要等待呢,头部对每个搜索页面都是一样的!然后,当客户端解析头部标记时,搜索查询被分发到搜索索引,文档的其余部分(包括搜索结果)在结果准备好后交付给用户。此时,头部的动态部分(如登录用户名)通过 JavaScript 填充。