如何做到一秒渲染一个移动页面

声明:原文版权属于 Google,原文以 CC BY 3.0 协议发布,原文中的代码部分以 Apache 2.0 协议发布。中文翻译部分并非官方文档,仅供参考。

PageSpeed Insights 可以分析一个网页是否遵循了我们的攻略,从而在移动网络上用不到一秒的时间完成一次页面渲染。研究显示,任何超过一秒钟的延迟都将打断用户的思维顺流状态,带来较差的体验。我们的目标是不论在任何设备或网络条件下,都要维持用户在网页中的沉浸状态,提供更好的体验。

想要满足一秒的时间预算并不是那么容易。不过幸运的是,我们并不需要在这个时间预算内渲染出整个页面,而是 要在一秒以内传输并渲染出首屏内容,让用户尽快与页面互动。然后,当用户在和首屏内容进行互动时,页面的剩余部分可以在后台持续加载完整。

(译注:原文中的“ATF”一词源自传统出版业,指的是报纸头版的中折线以上区域,这块黄金区域往往用来刊登最有吸引力的新闻。延伸到互联网领域,ATF 指的是页面中不需要滚动就可以看到的首屏内容。)

适应高延迟的移动网络

在移动设备上达到“首屏秒开”的准则,需要面对其它网络所遇不到的独特挑战。用户可能正通过 2G、3G 或 4G 等各种各样的网络来访问你的网站。移动网络的延迟要明显高于有线连接,并且将消耗我们“首屏秒开”预算中的一大部分:

  • 3G 网络的往返时间将消耗 200-300 ms
  • 4G 网络的往返时间将消耗 50-100 ms

3G 是全球范围内占据统治地位的移动网络,而 4G 网络正在普及之中,你需要明白你的主流用户仍然在使用 3G 网络来访问你的页面。基于这个原因,我们不得不假设平均每次网络请求将消耗 200 ms。

明白了这一点之后,我们来倒推一下时间。如果我们来分析一下浏览器与服务器之间一次典型的通信过程,会发现 600 ms 的时间就已经被网络本身消耗掉了:一次 DNS 查询用于将主机名(比如 google.com)解析为 IP 地址,一次网络往返用于发起 TCP 握手,以及最后一次完整的网络往返用于发送 HTTP 请求。我们就只剩下 400 ms 了!

[图 1] 一秒渲染一个移动页面

  • DNS 查询 (200 ms)
  • TCP 连接 (200 ms)
  • HTTP 请求与响应 (200 ms)

这 600 ms 是不可避免的 3G 网络消耗,你对此无能为力。

  • 服务器响应时间 (200 ms)
  • 客户端渲染 (200 ms)

这 400 ms 是你可以优化的,只要你合理地更新你的服务器,并且适当地构建你的页面(这正是 PageSpeed Insights 这个工具可以帮到你的)。

实现半秒渲染的体验

在除去网络延迟之后,我们的预算只剩下区区 400 ms 了,但我们仍然还有大量的工作要做:服务器必须渲染出响应内容,客户端应用程序代码必须执行,而且浏览器必须完成内容的布局和渲染。了解了这些之后,下面这些准则将帮助我们控制住预算:

(1) 服务器必须在 200 ms 以内渲染出响应内容

服务器响应时间就是在除去网络传输时间之后,一台服务器首先返回 HTML 所花费的时间。因为我们剩下的时间实在太少了,这个时间应该控制在最低限度——理想情况下应该低于 200 ms,而且越少越好!

(2) 重定向的次数应该减至最少

额外的 HTTP 重定向会增加一次或两次额外的网络往返(如果需要再次查询 DNS 的话就是两次),这在 3G 网络上将导致几百毫秒的额外延迟。因此,我们强烈建议网站管理员们减少重定向的次数,而且最好完全消除重定向——这对 HTML 文档来说尤其重要(尽可能避免重定向到 “m.” 二级域名的行为)。

(3) 首次渲染所需的网络往返次数应该减至最少

由于 TCP 在评估连接状况方面采用了一套特殊机制(参见 TCP 慢启动),一次全新的 TCP 连接无法立即用满客户端和服务器之间的全部有效带宽。在这种情况下,服务器在首次网络往返中,通过一个新连接(约 14kB)只能发送最多 10 个 TCP 包,接下来它必须等待客户端应答这些数据,然后才能增加它的拥塞窗口并继续发送更多数据。

考虑到 TCP 的这种行为,优化你的内容就显得十分重要。传输必要数据、完成页面首次渲染所需的网络往返次数应该减至最少。理想情况下,首屏内容应该小于 14KB——这样浏览器才能在一次网络往返之后就可以绘制页面。同时,还有一个关键点需要留意,10 个数据包上限(IW10)源自 TCP 标准的最近一次更新:你应该确保你的服务器软件已经升级到最新版本,以便从这次更新中获益。否则,这个上限将可能降到 3~4 个数据包。

(4) 避免在首屏内容中出现外链的阻塞式 JavaScript 和 CSS

浏览器必须先解析页面,然后才能向用户渲染这个页面。如果它在解析期间遇到一个非异步的或具有阻塞作用的外部脚本的话,它就不得不停下来,然后去下载这个资源。每次遇到这种情况,都会增加一次网络往返,这将延后页面的首次渲染时间。

结论就是,首屏渲染所需的 JavaScript 和 CSS 应该内嵌到页面中,而用于提供附加功能的 JavaScript 和 CSS 应该在首屏内容已经传输完成之后再加载。

(5) 为浏览器的布局和渲染工作预留时间 (200 ms)

解析 HTML 和 CSS、执行 JavaScript 的过程也将消耗时间和客户端资源!取决于移动设备的速度和页面的复杂度,这个过程将占用几百毫秒。我们的建议是预留 200 ms 作为浏览器的时间消耗。

(6) 优化 JavaScript 执行和渲染耗时

执行复杂的脚本和低效的代码可能会耗费几十或上百毫秒——可以使用浏览器内建的开发者工具来收集概况、优化代码。如果你想深入这个话题,不妨看看我们的《Chrome 开发者工具交互教程》

请注意:以上并未列举出所有可能的优化方案——只列出了一些移动端达成半秒渲染时间的基本准则——其它所有的网页性能最佳实践也应该运用起来。到 PageSpeed Insights 来看看,获取进一步的建议和推荐方案。

如果需要深入探索上述移动端准则,请参阅:

常见问题

4G 网络对上述移动端准则有何影响?

较低的网络往返延迟是 4G 网络的一处关键改良。减少所有的网络损耗时间对网页性能将有巨大帮助,而目前在 3G 网络上这些损耗就占用了我们一秒预算中的大半时间。不管怎样,3G 仍然是全球最主流的移动网络,并且在今后几年都将如此——我们在优化网页时不得不把 3G 用户放在心上。

我正在使用一个 JavaScript 类库,比如 jQuery,有什么建议吗?

大多数 JavaScript 类库,比如 jQuery,通常用来增强页面、提供附加的交互、动画和其它效果。但是,大多数这些行为可以安全地在首屏渲染之后再加入进来。研究一下是否可以把这些 JavaScript 的加载和执行推迟到页面加载之后。

我在正使用一个 JavaScript 框架来渲染整个页面,有什么建议吗?

如果页面内容是由客户端 JavaScript 来渲染的,那么你应该研究一下是否可以把相关的 JavaScript 模块都内嵌进来,以免产生额外的网络往返开销。同样,利用服务器端渲染可以显著地改善首次页面加载的性能:在服务器端渲染 JS 模板,并内嵌到 HTML 中,然后一旦应用程序加载完成就立即在客户端渲染模板。

SPDY 和 HTTP 2.0 协议会有什么帮助?

SPDY 和 HTTP 2.0 协议的目标都是通过更有效地利用底层的 TCP 连接(多路复用、头压缩、优先化处理),来减少页面的加载延迟。而且服务器 push 通过消除额外的网络延迟,可以进一步促进性能的改善。我们建议你为服务器增加对 SPDY 协议的支持,并且当 HTTP 2.0 标准就绪之后就立即切换过去。

如何识别页面中的要害 CSS?

在 Chrome 开发者工具中,打开审计(Audits)面板,然后运行一次网页性能(Web Page Performance)报告。在生成的报告中,找一下“删除未使用的 CSS 规则(Remove unused CSS rules)”。也可以使用其它第三方工具或脚本,来识别每个页面分别应用了哪些 CSS 选择符。

这些最佳实践可以自动化吗?

当然可以。有很多商业的或开源的网页性能优化(WPO)产品都可以帮你达成上述部分或全部准则。对于开源解决方案,不妨看看 PageSpeed 优化工具

我怎样调整我的服务器来符合这些准则?

首先,确保你的服务器正在运行最新版的操作系统。为了从 TCP 初始拥塞窗口数量的增加(IW10)中获益,你需要 2.6.39+ 版本的 Linux 内核。对于其它操作系统,请查阅相关文档。

为了优化服务器的响应时间,请测评你的代码性能,或使用监控程序来发现性能瓶颈——比如脚本运行时、数据库调用、RPC 请求、渲染等等。最终目标就是在 200 ms 内渲染出 HTML 响应内容。

内容安全策略(Content Security Policy)怎么办?

如果你正在使用 CSP,那么你可能需要更新你的默认策略。(译注:CSP 是一项用于防范 XSS 的安全性规范,具体参见Content Security Policy – 维基百科。)

首先,尽可能避免在 HTML 元素中内联 CSS 属性(比如这样 <p style=...>),因为它们常常导致不必要的重复代码,而且默认会被 CSP 拦截(对 style-src 字段使用 unsafe-inline 指令可以禁用此类拦截)。如果你使用了内联的 JavaScript,那么你需要在 CSP 策略中使用 unsafe-inline 指令来令其执行。默认情况下,CSP 将拦截所有内联脚本标签。(译注:这里的“内联 JavaScript”包括多种形态的 HTML 内部的脚本代码,包括类似 <script>foo();</script> 这样的内嵌脚本标签、<a href="javascript:foo();"> 这样的伪协议 URL、以及 <a href="#" onclick="foo();">这样的事件处理属性。)

还有其它问题?请在我们的 page-speed-discuss 讨论组内随意提问或提供反馈。

1 1 收藏 1 评论

关于作者:CSS魔法

百姓网前端工程师,移动 Web UI 框架 CMUI 作者,自称 “披着前端工程师外衣的设计师”。 个人主页 · 我的文章 · 12 ·     

相关文章

可能感兴趣的话题



直接登录
最新评论
  • “……取决于移动设置的速度和页面的复杂度……” 抱歉这里出了一个笔误,“移动设置”应为“移动设备”。

跳到底部
返回顶部