2-最小化往返时间

原文:http://code.google.com/intl/zh-CN/speed/page-speed/docs/rtt.html

往返时间 (Round-trip time,简称RTT )是从客户端通过网络发送请求到服务端通过网络送来响应的时间,其中不包括数据传输时间。换句话说,往返时间包括信号在电缆线上一个来回的时间,但是不包括完全下载过程中传输字节所需要的时间(因此与带宽无关)。例如,浏览器第一次发起到web服务器的链接,一定会产生3个RTT:1 个RTT是域名解析;1个RTT建立TCP链接;1个RTT是HTTP请求和HTTP响应的第一个字节。通常网页都会产生几打的RTT。

从局域网不到1毫秒的RTT到最糟糕情况下超过1秒,RTT有巨大的差别,例如:不同大陆的用户用调制解调器链接到同一台主机的时间就不相同。对尺寸很小的下载文件,例如搜索结果页,对于高速链接(宽带)RTT是主要延迟因素。因此,提高网页评分的一个重要的策略是最小化需要往返的次数。由于往返主要决定于 HTTP请求和相应,所以最小化请求次数是十分重要的,尽可能的并行客户端请求。

  1. 最小化 DNS 查询(Minimize DNS lookups)
  2. 最小化重定向(Minimize redirects)
  3. 避免“坏”请求(Avoid bad requests)
  4. 绑定外部JavaScript(Combine external JavaScript)
  5. 绑定外部CSS(Combine external CSS)
  6. 用 CSS sprites 绑定图片(Combine images using CSS sprites)
  7. 优化样式和脚本的顺序(Optimize the order of styles and scripts)
  8. 避免使用 document.write 语句(Avoid document.write)
  9. 避免使用 CSS @import(Avoid CSS @import)
  10. 使用异步资源(Prefer asynchronous resources)
  11. 并行下载不同域名上的资源(Parallelize downloads across hostnames)


(最小化 DNS 查询)Minimize DNS lookups


概览

减少具体域名的数量,从而减少浏览器请求资源服务而不得不做的 DNS 解析数,也就是减少 RTT 延时。

详情

浏览器与 web 服务器建立稳定的网络连接之前,需要将 web 服务器的域名解析为 IP 地址。因为 DNS 解析可以被客户端浏览器和操作系统缓存,所以,在客户端缓存一个有效的、可用的记录,就不会有这类的延时。如果客户端需要在网络上执行 DNS 查询,产生的延时主要取决于能提供有效响应的域名服务器的远近。所有的 ISP 都有 DNS 服务器,DNS 服务器上缓存了来自权威域名服务器(authoritative name servers)的域名-IP映射;但是,如果缓存的 DNS 记录过期了、需要重新刷新,可能要在 DNS 服务器的层次结构中遍历好几个节点——有时可能要更广度的遍历才能找到那个权威服务器。如果那个 DNS 解析服务器没有记载(under load),就会进入 DNS 解析请求队列,这样就进一步增加了延迟。换句话说,理论上,DNS 解析要花 1 个 RTT 才能完成,但是实际上,延迟严重取决于 DNS 解析队列的延时。因此,缩减 DNS 查询比其他任何一种请求都重要得多。

由于权威服务器设置的 TTL(time-to-live)最终决定了 DNS 记录的有效期;许多网络管理员设置的 TTL 非常低(在 5 分钟到 24 小时之间)以便网络流量发生变化时可以快速更新。(但是,许多 DNS 缓存,包括浏览器,是“不服从 TTL”的,保持缓存记录的时间长于原始服务器设置,有时长达 30 分钟。)有几种途径可以减少 DNS 查询时间——例如增加你的 DNS 记录的 TTL 设置,最小化 CNAME 记录(会需要额外的查询),在多个地区复制你的名字服务器等——这些距离我们的 web 应用开发太远了,而且对于你站点的网络流量管理来说是未必可行。

取而代之的方法是,在你的应用中限制 DNS 查询延迟的最佳方法是最小化不同的 DNS 查询(DNS查询是要客户端去做的)的数目,特别是页面初始化加载时的查询。实现方法是最小化要下载资源的不同域名的个数。然而,因为使用多域名有助于提高并行下载量 , 这个在一定程度上依赖于每页面的需要的资源数。主机最优的数目通常在 1 到 5 之间(1 个主要的主机,加 4 个用来并行下载可缓存资源的主机)。根据经验规则,不要用 1 个以上的主机服务 6 个以下的资源;一个独立主机仅服务 2 个以下的资源绝对是浪费。使用 5 个以上的主机绝对没有这个必要(不包括你没法控制的主机服务资源,例如广告)。


推荐

尽可能使用 URL 路径取代域名
如果你在一个域上托管了多个 properties(原文:If you host multiple properties on the same domain),把这些 properties 托管给 URL 路径比托管给彼此独立的主机名要好。例如,你开发站点名托管为www.example.com/developer 比托管为 developer.example.com 要好,除非有更好的技术理由使用使用后者,例如:实现基于 DNS 流量的平衡加载策略,否则使用域名而不是 URL 路径方式编码产品名并没有什么益处。事实上,构建 properties 时改善独立主机名上的延迟状况能实际的增强用户体验: 用户可以在一个浏览器 session 链接两个 properties 而不会产生额外的 DNS 查询滞待。另外,可重用的域名允许浏览器更高频的重用 TCP 链接,从而进一步缩减了往返时间数。如果一个 property 上的流量较大,第一次访问重用该域名的其他 proterties 能增加该 DNS 的缓存命中率,因为很可能有效的映射已经存在存在于某个本地缓存服务器中了。

相同主机名上的 early-loaded JS 文件作为主文档?? (原文:Serve early-loaded JavaScript files from the same hostname as the main document.)
特别重要的是:最小化“关键路径(critical path)”上的查询。我们为代码和资源请求定义关键路径以便呈现网页的初始视图。特别的,外部 JS 文件(在文档头部加载的,或者在文档体的开始部分加载的)应当和调用它的主文档在同一台主机上。在 JS 文件装载、解析和执行的过程中,大多数浏览器会阻塞其他下载和页面的呈现。额外的 DNS 查询时间会进一步加重页面加载过程中的延迟。相同主机名下的这些文件不可能像包含他们的文档那样 延迟加载 。有个例外,多网页共享的 JS 文件不在多个域上:这种情况下,服务于唯一 URL 上的文件会增加缓冲命中率,并可能会超过 DNS 查询开销。



最小化重定向(Minimize redirects)


概览

最小化从一个 URL 转到另一个 URL 的重定向的数目,以免产生多余的往返时间(RTT)让用户等待。

详情

有时,你的应用有必要使用从一个 URL 到另一个 URL 的重定向。下面是 web 应用使用重定向的理由:

  • 说明被移动资源的新地址。
  • 跟踪点击操作、记下并记录发出引用的页面(referring pages)。
  • 存储多域名, 允许“用户友好(user-friendly)”或“vanity”的域名和网址,捕捉拼错的或者输入错误的网址。
  • 链接站点或者应用的不同部分、不同国家代码的顶级域(country-code top-level domains)、不同的协议(HTTP 转 HTTPS)、不同的安全策略(例如非认证页面和认证页面)等。
  • 给 URL 目录名添加斜线(trailing slash)以便浏览器访问其内容。

不管是什么理由,重定向都触发了新的 HTTP 请求-响应周期从而增加了往返时间的延时。最小化应用中出现重定向的数目是很重要的,尤其是那些启动首页时需要的资源。解决这个问题的最佳途径就是限制使用重定向——除非是技术上绝对必要的,或者没有其他替代解决方法的情况。

推荐

排除不必要的重定向
这儿有些策略可以简单排除不必要的重定向:
  • 不要在页面上引用那些明知道会重定向到其他 URL 的 URL。当资源地址发生改变时,你的应用应当有一个合适的方式更新该 URL 的引用。
  • 不要用多个重定向请求一个给定的资源。例如,如果 C 是目标页,同时有 2 个不同的起点 A 和 B,A 和 B 都应该直接重定向到 C;永远不要让 A 重定向到 B。
  • 最小化额外域名的数目,额外的域名会引发重定向却不提供实际内容。有这样一个来自多域名重定向的诱惑:为了解决名字空间,或者捕获错误的用户输入(拼错或输 错 URL)。然而,如果你让用户认为他们可以从多个 URL 抵达你的站点,你会在一个昂贵的循环中结束的工作——不断的购买新的域名仅仅为了阻止域名被抢注、不断的接管每个域名的变种(原文:However, if you train users into thinking they can reach your site from multiple URLs, you can wind up in a costly cycle of buying up new domains just to stop cybersquatters from taking over every variant of your name.)。

针对用户输入的 URL 使用服务端重写(server rewrites)
许多 web 服务器支持内部“重写(rewrites)"。你可以配置从一个 URL 到另一个 URL 的映射。当客户端的请求是非法 URL 的时候,服务端自动重映射到正确的 URL 并提供资源从而避免发出重定向。对那些你没法控制的 URL,要用这种方法来捕获。页面上简单更新 URL 引用时,不要使用重定向。你应当用单独的 URL 指向资源。如果可能的话,避免为可缓存的资源使用重定向。还有一个例子,重写机制(rewrite mechanism)有一个候选项,可以自动在用户输入 URL 的目录名之后添加请求斜线。

在后台跟踪 web 流量
为了跟踪各种属性之内或者之间的流量,有些站点采用立即重定向到一个页面记录中央独立服务的所有属性。然而这样的重定向总会增加两个页面传输之间的延时。为了避免这种情况,可以找到方法在后台记录页面表示层日志。

一 个流行的、利用时尚的异步法记录页面表示层日志的方法是:在目标页面的尾部加入一段 JavaScript 代码段(或者作为 onload 事件处理句柄(onload event handler))。最常用的实现方法是构建一个请求作为“beacon”发给服务器,再将那些有趣的数据编码并作为参数放到 URL 中给 beacon 资源(原文:The most common way of doing this is to construct a request to the server for a "beacon", and encode all the data of interest as parameters in the URL for the beacon resource.)。为了保持 HTTP 响应比较小,1x1像素的透明图片用作候选的 beacon 请求(a beacon request)是比较合适的。稍微优化过的 beacon 会使用 HTTP 204 响应(“无内容(no content)”),该响应比 1x1 的gif还要略小一点。

这里有一个小例子,假设 www.example.com/logger 提供记录日志的服务,它请求记录一个叫做 beacon.gif 的图片,并将当前页面的 URL,以及传递发出引用的页面的 URL(如果有这样一个页面) 作为参数传递:

<script type="text/javascript">



var thisPage = location.href;



var referringPage = (document.referrer) ? document.referrer : "none";



var beacon = new Image();



beacon.src = "http://www.example.com/logger/beacon.gif?page=" + encodeURI(thisPage)



+ "&ref=" + encodeURI(referringPage);



</script>



这种类型的 beacon 组号包含在 HRML 页面的尾部,以避免与其他需要呈现页面内容的 HTTP 请求产生竞争。用这种方式,当用户访问页面的时候,这个请求就会生成,因此不需要额外的等待时间。


优先使用 HTTP 而不是 JavaScript 或 meta 重定向
有几种方式会发出重定向:
  • 服务器端:配置你的 web 服务器,使其能发出 300 HTTP 响应代码(最常见的是 301(永久移动) 或 302(找到/临时移动)),该响应代码将随 Location 头一同发送给新的 URL。
  • 客户端:在超文本文档的头部,需要设置包含 http-equiv="refresh" 属性的 meta 标签,或者设置 JavaScript 的 window.location   对象(带或者不带 replace() 方法)。

如果你需要要使用重定向机制,使用服务端方法比使用客户端方法更好。浏览器处理 HTTP 重定向方式的效率比处理 meta 和JavaScript 重定向方式要高。例如,在浏览器中,JS 重定向方式要增加解析的延迟;而 301 或 302 重定向在浏览器解析 HTML 文档之前就执行了。

另外,按照 HTTP/1.1 规范,301 和 302 响应可以被浏览器缓存。意味着,即使资源本身是不可缓存的,浏览器也要看一下当前 URL 是否在当地缓存中。301 响应默认是可缓存的,除非特别设定。为了让 302 响应可以被缓存,你需要配置你的 web 服务器添加 Expires Cache-Control max-age 头(具体参见利用浏览器缓存 )。这里要警示的是:许多浏览器实际上没有按规范办事,并没有缓存 301 或 302 响应;哪些浏览器符合规范,请参见Browserscope 的列表。


例子

Google Analytics 用图片 beacon 方法跟踪 Analytics 账户持有人所有页面上的从外部链接进来的、内部的、链接到外部的轨迹(inbound, internal, and outbound traffic)。这个账户拥有者的页面上嵌入引用了一个外部的 Javascript 文件,它定义了一个叫做 trackPageview() 的函数。 在这个文档体的末端,页面包含了一段 Javascript 代码,当访问者请求该页面时,这段 JS 代码调用 trackPageview() 函数。trackPageview() 构建了一个叫 __utm.gif 的1*1像素的图像,并加在含有多个参数的 URL上(原文:The trackPageview() function constructs a request for a 1x1-pixel image called __utm.gif, with multiple parameters in the URL.)。这些参数详细制定了几个变量,例如:页面 URL、要指向的页面、浏览器设置、用户语言环境等。当 Analytics 服务器获得请求时,记录下这些信息,并为账户持有者提供服务(当他们登进这个报表站点之后)。

附加资源


避免坏请求(Avoid bad requests)

概览

删除“死链(broken links)”,或者返回 404 或 410 错误的请求,避免浪费请求。

详情

因为你的网站一直在改变,无可避免的会有资源被转移或者被删除。如果你没有正确更新前端代码,服务器会反馈 404 “未找到(Not found)”或者 410 “已失效(Gone)”。不必要的请求不但让用户感觉不好,也会让你的网站看起来不专业。这样的请求就被浪费了。同时,如果是针对资源的请求,例如 JS 或 CSS 文件,有可能会阻塞浏览器一连串的处理工序,会把你的站点弄瘫。短期内,你要用链接检查工具扫描站点,比如 Google的扒错工具 Webmaster Tools 找到他们,然后修复它们。长远的看,当资源地址发生改变时,你的应用应当有一个合适的方式更新该 URL 的引用。

推荐

避免使用重定向来处理死链(broken link)
不管怎样,只要资源发生移动,你就要立即更新指向它们的链接,如果删除资源,你也要随机删除链接。避免使用 HTTP 重定向用户请求的资源,或者提供一个替换用的“建议”页面。正如前面所讨论的,重定向会减慢网站的速度,要尽可能避免。


绑定外部 JavaScript(Combine external JavaScript)

概览

尽可能绑定最少的外部 script 文件,以便减少 RTT 和 下载其他资源的延时。

详情

优秀的前端开发人员会使用模块、可重用的组件来编写 web 应用。把代码分割成模块化的软件组件是一种很好的工程实践方法,然而一次性把模块都导入到 HTML 页面中会极大增加页面加载时间。首先,对于缓存是空的客户端,浏览器必须为每个资源发出 HTTP 请求,从而导致相关的往返时间问题。其次,大多数浏览器在下载并解析 JavaScript 文件时会阻止页面其他部分的加载。(要查看浏览器是否支持并行下载JS,请参见 Browserscope 。)

下面的例子剖析了这样一个HTML文件,这个HTML文件包含了来自同一域的13个不同js文件的请求;下面的截屏来自 Firebug's Net 面板,这里是 Firefox 3.0+访问的 DSL高速连接:

Web 性能最佳实践 2-最小化往返时间 - Sunny_叶纷飞 - 网赚

所有的文件被顺序下载,总共花费了4.46秒才完成。下面是同样的HTML文档,只是把 13 个文件集合成 2 个文件:

Web 性能最佳实践 2-最小化往返时间 - Sunny_叶纷飞 - 网赚

同样是 729 kb,现在只花了1.87秒下载完毕。如果你的站点含有许多 JavaScript 文件,把它们集合到少数几个输出文件,这样可以极大缩减延时。

然而,还需要加入其他因素才能最终确定要优化成几个文件。首先,推迟加载不需要在页面开始执行的 JS 代码也是很重要的。其次,有些代码必须要有几个不同的版本,此时你需要把它分成几个文件。最后,你需要的来自某些域的 JS 是你没法控制的,例如:跟踪脚本或者广告脚本。我们建议最多3个 JS 文件,最好是2个 JS 文件。

在开发周期中,通常也会产生多个不同的 JavaScript 文件,因此,集成这些 JS 文件也是一个开发步骤。看看下面推荐的分割 JS 文件的方法。更新所有页面引用的 JS 文件也应当是一个开发步骤。

推荐

优化划分文件
下面是开发中集成 JavaScript 文件的经验法则:
  • 划分2个 JavaScript 文件:一个是页面初始化时加载的 JS 文件,要求代码是最小的;另一个是页面加载完之后才需要的 JS 文件。
  • 在文档的 <head> 引用最少的 JS 文件,并最小化这些文件的尺寸。
  • 很少被使用的 JS 组件放在各自单独的文件中,仅在用户需要的时候才使用。
  • 对于不能被缓存的、字节数很小的 JS 代码,把他们内联到 HTML 页面里面。

在文档头部正确定位脚本
不管一段脚本是外部的还是内联的,如果对于其他元素来说他出现的顺序是正确的,又能最大可能的实现并行下载,那么这种做法就是有益的。


绑定外部 CSS(Combine external CSS)

概览

尽可能绑定最少的外部的样式表文件,以便减少 RTT 和 下载其他资源的延时。

详情

像外部 JavaScript 一样,多个外部 CSS 文件也会产生多余的 RTT 开销。如果你的站点有许多 CSS 文件,把他们集合到尽可能少的几个文件中以便减少延时。我们建议最多 3 个 CSS 文件,最好是 2 个。

开发周期中同样也会产生许多不同的 CSS 文件,集成这些 CSS 文件也是开发的一个步骤。下面是推荐的分割 CSS 文件的方法。更新所有页面引用的 CSS 文件也应当是一个开发步骤。

推荐

优化分割文件
下面是开发中集成 CSS 文件的经验法则:
  • 为每个页面规划 2 个 CSS 文件:一个 CSS 文件包含初始化页面必须的最小代码;另一个 CSS 文件包含直到页面加载完成之后才需要的代码。
  • 很少被使用的 CSS 组件放在各自单独的文件中,仅在用户需要的时候才使用。
  • 不需要被缓存的 CSS 可以用内联方式。
  • 不要使用 CSS @import 导入 CSS 文件。

在文档头部正确定位样式文件
如果 对于其他脚本来说 CSS 文件出现的顺序是正确的 ,又能最大可能的实现并行下载,那么这种做法就是有益的。



用 CSS 子画面合并图片(Combine images using CSS sprites)

什么是CSS子画面(CSS sprites)?

有的翻译成“CSS拼合技术”、“CSS精灵”等。

具体解释和实现,请参见: http://baike.baidu.com/view/2173476.htm

概览

尽可能用最少的 CSS 文件绑定图片,以便缩减下载其他资源是的往返次数,缩减请求的开销,同时缩减下载页面的总字节数。

详情

与 JavaScript 和 CSS 极为相似,下载多幅图片会增加往返开销。站点如果有许多图片,需要把他们集成到少数几个输出文件中以减少延时。

推荐

使用子画面(sprite),让图片一同加载
合并在同一页面上加载的,和总是一同加载的那些图片,将他们做成子画面。例如,每张页面都会加载的一套图标都应当做成子画面。对于那些每次浏览都会改变的动态图片,例如个人资料图片或其他经常改变的图片,不适合做成子画面。

优先将 GIF 和 PNG 图片做成子画面
GIF 和 PNG 图片采用无损压缩技术,因为把他们做成子画面不会降低图片质量。

优先将小图片做成子画面
每个请求通常产生固定的请求开销( request overhead )。浏览器下载众多小图片花费的时间可能占请求开销的主要部分。合并这些小图片,则请求开销从为每个小图片的建立一个请求开销缩减为只为一个子画面图片建立请求开销。

缓存子画面图片
如果小画面图片拥有较长缓存期限,这就意味着这个图片一但被浏览器缓存( cached )通常不必再获取了。

使用子画面服务
诸如( SpriteMe )之类的子画面服务方便建立 CSS 子画面。

最小化子画面里的空白空间
为了呈现一张图片,浏览器必须为图片解压并解码。要解码的图片影像的大小通常和图片像素成正比。因此,小画面图片中的空白可能不太会影响图片的大小,但是这些未显示的像素会增加页面的内存耗用量,导致浏览器反应迟钝。

小画面图片要用较小的调色板
超过 256 色的小画面图片会产生 the resulting sprite ,用 PNG 真彩代替调色板会增加 the resulting sprite 的尺寸(原文:Spriting images with more than 256 colors can cause the resulting sprite to use the PNG truecolor type instead of the palette type, which can increase the size of the resulting sprite.)为了产生最佳的小画面,待合并的图片要使用同样的 256 色调色板。如果你的图片需要些灵动的颜色,考虑把 the resulting sprite 颜色的调色板缩减为256色。
备注:“the resulting sprite”不清楚是什么意思???



优化样式和脚本的顺序(Optimize the order of styles and scripts)

概览

正确的安排好外部样式单和外部、内联脚本的顺序能较好的并行下载他们,并同时加快了页面呈现速度。

详情

因为 JavaScript 会改变网页内容和布局,所以对于 script 标签之后的网页内容,浏览器通常等到那段 script 代码下载完成之后才会呈现、解析和执行。更重要的是往返时间,如果页面引用的资源在 script 代码之后,许多浏览器会阻塞这些资源的下载,直到 script 代码下载完并执行完成。另一方面,如果其他文件已经开始下载,这时遇到引用的 JS 文件,JS文件可以和其他资源并行下载。

例:说说下面文档中的 3 个样式文件和 2 个 脚本文件的下载顺序

<head>

<link rel="stylesheet" type="text/css" href="http://singlefamilies.blog.163.com/blog/stylesheet1.css" />

<script type="text/javascript" src="http://singlefamilies.blog.163.com/blog/scriptfile1.js" />

<script type="text/javascript" src="http://singlefamilies.blog.163.com/blog/scriptfile2.js" />

<link rel="stylesheet" type="text/css" href="http://singlefamilies.blog.163.com/blog/stylesheet2.css" />

<link rel="stylesheet" type="text/css" href="http://singlefamilies.blog.163.com/blog/stylesheet3.css" />

</head>




假设每个引用的资源恰好都需要 100 毫秒的下载时间,同时,浏览器能维持多达 6 个来自同一个独立主机(更多信息,参见并行下载不同域名上的资源(Parallelize downloads
across hostnames)


)的并发链接,并且,当前缓存是空的。那么,该下载的分析图如下:

Web 性能最佳实践 2-最小化往返时间 - Sunny_叶纷飞 - 网赚

后面的两个样式单文件必须等到那个 JS 文件下载完成后才能开始下载。总下载时间等于两个 JS 文件的下载时间加上最大的 CSS 文件的下载时间(本案中:100 ms + 100 ms + 100 ms = 300 ms)。

如下细微的调整下资源的顺序:

<head>

<link rel="stylesheet" type="text/css" href="http://singlefamilies.blog.163.com/blog/stylesheet1.css" />

<link rel="stylesheet" type="text/css" href="http://singlefamilies.blog.163.com/blog/stylesheet2.css" />

<link rel="stylesheet" type="text/css" href="http://singlefamilies.blog.163.com/blog/stylesheet3.css" />

<script type="text/javascript" src="http://singlefamilies.blog.163.com/blog/scriptfile1.js" />

<script type="text/javascript" src="http://singlefamilies.blog.163.com/blog/scriptfile2.js" />

</head>




结果我们得到如下的下载分析图:

Web 性能最佳实践 2-最小化往返时间 - Sunny_叶纷飞 - 网赚

节省了 100 ms 的下载时间。较大的样式单会耗用较长的下载时间,这样节省的时间可能更多。

因此,为了较好的性能,样式单应当总是在文档头部 指定。这是非常重要的,只要有可能,任何外部的 JS 文件都必须跟在头部的样式单之后,以便缩减下载延迟。

另外,比较微妙的是跟在样式单之后出现的内联脚本,如下所示:

<head>

<link rel="stylesheet" type="text/css" href="http://singlefamilies.blog.163.com/blog/stylesheet1.css" />

<script type="text/javascript">

document.write("Hello world!");

</script>

<link rel="stylesheet" type="text/css" href="http://singlefamilies.blog.163.com/blog/stylesheet2.css" />

<link rel="stylesheet" type="text/css" href="http://singlefamilies.blog.163.com/blog/stylesheet3.css" />

<link rel="alternate" type="application/rss+xml" href="http://singlefamilies.blog.163.com/blog/front.xml" title="Say hello" />

<link rel="shortcut icon" type="image/x-icon" href="http://singlefamilies.blog.163.com/blog/favicon.ico">

</head>




本案中,相反的问题出现了:第一个样式单实际上阻塞了内联脚本的执行,因此其他资源的下载也被阻塞了。

解决方案依旧是把这段内联脚本尽可能搬到所有其他资源的后面,如下:

<head>

<link rel="stylesheet" type="text/css" href="http://singlefamilies.blog.163.com/blog/stylesheet1.css" />

<link rel="stylesheet" type="text/css" href="http://singlefamilies.blog.163.com/blog/stylesheet2.css" />

<link rel="stylesheet" type="text/css" href="http://singlefamilies.blog.163.com/blog/stylesheet3.css" />

<link rel="alternate" type="application/rss+xml" title="Say hello" href="http://singlefamilies.blog.163.com/blog/front.xml" />

<link rel="shortcut icon" type="image/x-icon" href="http://singlefamilies.blog.163.com/blog/favicon.ico">

<script type="text/javascript">

document.write("Hello world!");

</script>

</head>





推荐

尽可能将外部脚本放在所有外部样式表的后面
浏览器按照样式表和脚本在文档中出现的顺序依次执行它们。如果 JS 代码不依赖于 CSS 文件,你可以将这个 CSS文件 放到 该 JS 文件的前面。如果 JS 代码依赖于包含在外部文件中的样式——例如:JS代码输出页面时用到该样式,而此时这个页面你还没有写完——实际这是不可能的。

尽可能把内联脚本放在其他资源之后
把 内联脚本放在所有其他资源之后,以避免阻塞那些资源的下载,使得页面可以逐步呈现。但是,如果那些”其他资源“是内联脚本依赖的外部的 JS 文件,很可能页面不会逐步呈现。这时,最好的把这个内联脚本放在 CSS 文件之前。(原文:Putting inline scripts after all other resources prevents blocking of other downloads, and it also enables progressive rendering. However, if those "other resources" are external JS files on which the inline scripts depend, this might not be possible. In this case, it's best to move the inline scripts before the CSS files.)



避免 document.write(Avoid document.write)

概览

使用 document.write() 拿取外部资源,尤其在超文本文档的前部分,会显著增加页面呈现的时间。

详情

现代浏览器通常用推测分析器(speculative parser)比较有效的探测 HTML 标记(markup)内所引用的外部资源。这些推测分析器有助于缩减页面加载实践。由于推测分析器小巧而高速,它们不执行 JavaScript,结果,如果用 JavaScript 的 document.write() 拿取外部资源,将导致推测分析器无法拿取那些资源。这样就会延迟那些资源的下载、解析和呈现。

在外部 JavaScript 文件中用 document.write() 的代价更高,因为浏览器会序列化下载外部资源。在浏览器执行 document.write() 拿取其他外部资源之前,必须先下载、解析,然后执行这个外部 JavaScript 资源。例如:如果外部 JavaScript 资源 first.js 含有如下代码:

document.write('<script src="http://singlefamilies.blog.163.com/blog/second.js"><//script>');







所有的浏览器都会序列化下载的 first.js 和 second.js。使用任何一个下文推荐的技术都可以减少这些资源的阻塞和序列化,从而缩减了显示页面的延时。

推荐

在 HTML 标记中直接声明资源。
在 HTML 标记中申明允许推测分析器探测这样的资源。例如可以这样在 HTML 的<script>标签调用 document.write
<html>

<body>

<script>

document.write('<script src="http://singlefamilies.blog.163.com/blog/example.js"><//script>');

</script>

</body>

</html>






document.write() 脚本标签直接写在 HTML中:

<html>

<body>

<script src="http://singlefamilies.blog.163.com/blog/example.js"></script>


</body>

</html>






使用异步资源(Prefer asynchronous resources)
有些情况下,直接在 HTML 中申明资源是不可能的。比如,资源的 URL 是客户端动态确定的,必须用 JavaScript 构建 URL。此时,只好 用异步加载技术

使用“友好的 iframe”
还有些情况,例如优化那些无法用上述推荐技术的遗留代码,无可避免的使用 document.write 。这时,友好的 iframe 可用来避免主页面的阻塞。
友好的 iframe 就是与其父页面同源的 iframe。友好的 iframe 中引用的资源是和主页面引用的资源并行加载的。因此,在友好的 iframe 中调用 document.write 不会阻塞父页面的加载。尽管不阻塞父页面,在友好的 iframe 中使用 document.write 依然会减慢自己 iframe 中加载的内容,所以,“友好的 iframe”只能是不得已而为之的最后选择。



避免 CSS @import(Avoid CSS @import)

概览

在页面加载过程中,如果外部样式单中使用了 CSS @import 也会延长页面加载时间。

详情

CSS @import 允许样式表导入其他样式单。当一个外部样式单使用了 CSS @import,浏览器不能并行的下载这个样式单,因而增加了整体页面的往返时间。例如,如果 first.css 包含下面的内容:

@import url("second.css")







在浏览器发现 first.css 需要 下载second.css 之前,浏览器必须先下载、解析、执行 first.css。

推荐

使用 <link> 标签取代 CSS @import
<link> 标签取代所有样式单中的@import。这样,浏览器就可以并行下载样式单了,缩短了页面加载时间:
<link rel="stylesheet" href="http://singlefamilies.blog.163.com/blog/first.css">

<link rel="stylesheet" href="http://singlefamilies.blog.163.com/blog/second.css">


 

 

使用异步资源(Prefer asynchronous resources)

概览

异步拿取资源,以防止这些资源阻塞页面加载。

详情

当浏览器解析传统的 script 标签时,它必须等这段脚本依次下载、解析、执行之后才去呈现出现在这段脚本代码之后的 HTML。然而,使用异步技术,浏览器可以一边解析脚本一边异步的呈现该脚本之后的 HTML,而不必等这段脚本。当脚本被异步加载的时候,会尽可能快的工作,但是脚本会被延迟到浏览器的 UI 线程完成其他事情之后(例如呈现页面)才被执行(原文:When a script is loaded asynchronously, it is fetched as soon as possible, but its execution is deferred until the browser's UI thread is not busy doing something else, such as rendering the web page.)。

推荐

不需要构建页面视图的 JavaScript 资源,例如那些用来跟踪或分析的脚本,应当异步加载。有些用于显示用户可见(user-visible)内容的脚本也可以异步加载,尤其是那些不太重要的内容(例如被折叠起来的内容)。

使用脚本的 DOM 元素

在当前浏览器中,用脚本的 DOM 元素尽可能实现异步加载(原文:Using a script DOM element maximizes asynchronous loading across current browsers):

<script>

var node = document.createElement('script');

node.type = 'text/javascript';

node.async = true;

node.src='http://singlefamilies.blog.163.com/blog/example.js';

// Now insert the node into the DOM, perhaps using insertBefore()

</script>




使用脚本 DOM 元素的一个异步的属性允许在 IE、Firefox, Chrome 和 Safari 中异步加载。注意:此时的 HTML <script>



标签的异步属性仅在 Firefox 3.6 和 Chrome 8 中有效,其他浏览器目前还不支持这种异步加载机制。
加载谷歌的异步分析器
最新版的 Google Analytics snippet 使用了异步 JavaScript。使用旧版 snippet 的页面要 更新到异步版


并行下载不同域名上的资源(Parallelize downloads across hostnames)

概览

来自不同域名的服务端资源多用并行方式下载。

详情

HTTP 1.1 规范(8.1.4 节)规定浏览器应当允许每个域名至多两个并发连接(实际上,较新的浏览器允许的要多,具体列表请参见 Browserscope )。 如果 HTML 文档引用了许多资源(例如:CSS、JavaScript、图片,等),超了一台主机允许的最大限度,浏览器发出这些资源的请求,这个请求队列便开始等 待。等其中某些请求执行完毕,浏览器又会发出一系列资源请求以填满这个请求队列。这个过程不断重复,直到下载完成所有需要的资源。换句话,如果一个页面从 单个服务器引用了超过 X 个外部资源(X 是该主机允许的最大连接数),这时浏览器只能顺序下载这些它们,一次下 X 个,每 X 个资源耗用 1 个RTT。总的往返时间是 N/X,这里的 N 是要从一台主机拿取资源的总数。例如,如果一个浏览器允许每域名 4 个并发链接,且一个页面引用了同一个域名的 100 个资源,则每获得 4 个资源要消耗 1 RTT,要完成所有的下载就要 25 个RTT。

把资源分配给多个域名,如此一来就可以绕过这个限制。用这个“伎俩”迫使浏览器更多的并行下载,最终减少页面的加载时间。但是,使用多并发连接会增 加客户端 CPU 的运算量,因为会增加新建 TCP 连接的时间,导致往返时间的增加,同样的也会增加客户端 DNS 查询的延迟 with empty caches(DNS lookup latency for clients with empty caches). 因此,撇除连接数的影响,这个技术确实能提高性能。主机最佳的数目通常在 2 到 5 之间,文件尺寸、带宽等都是决定主机数目的因素。如果你的页面提供大量的静态资源,例如图像,这些资源都在一个域名上,用 DNS 别名方式把他们分配给多个域名。如果你的页面引用了来自单个主机的10个以上的资源,建议采用该技术。(如果页面引用的资源不到这个数,就没必要用了。)

设置附加域名,先要在你的DNS数据库中配置 CNAME 记录的子域,并指向一个单独的 A 记录,然后在配置你的 web 服务器提供来自多主机的资源服务。要想得到更好的性能,如果所有或者部分资源没有使用 cookie 数据(通常不使用的),可以考虑将所有或者部分主机的子域做成无 cookie 的域。为了确保资源均匀分配给不同的域名,页面在引用资源的时候要在 URL 中使用 CNAME 域名。

如果你用 CDN(Content Delivery Network,即内容分发网络)服务静态文件,你的 CDN 可以支持多域名的资源服务。联系你的CDN(Contact your CDN to find out)。

推荐

用多域名来平衡并行资源
大多数被请求的静态资源,包括图像,CSS,和其他二进制对象,都能够被并行化。给这几个域名 均衡的分配请求也十分重要。如果做不到这点,依据经验,试着确认主机的服务量是否超过了所有主机服务量的均值的50%。例如,如果你有 40 个资源,4台主机,每台主机理想状态下应服务 10 个资源;最坏的情况下,每台主机不应该服务 15 个资源。如果你有 100 个资源,4台主机,每台主机应该服务 25 个资源,不应当有主机的服务量超过38。

另一方面,许多浏览器不以并行的方式下载 JavaScript 文件* ,因此从多域名方式对它们来说没有什么益处。所以,用多域名方式平衡资源时,请从分配公式(allocation equation)中移除所有的 JS 文件。

* 关于支持与不支持并行下载 JavaScript 文件的浏览器列表,请参见 Browserscope


防止外部的 JS 阻塞并行下载
下载外部 JavaScript 时,许多浏览器会阻塞 所有 域名上其他类型文件的下载,不管有多少域名都是这样。为了防止下载 JS 时阻塞其他资源下载(以及提高 JS 自身的下载速度):

总是从同一个域名拿取资源
为提高浏览器缓存命中率,客户端应当总是从同一个域名拿取资源。确保所有页面 用同样的 URL 引用同一个资源。


例子

为显示地图图片,Google Maps 传送了多个叫做"tiles(瓦片)"的小图片,每张小图片呈现这张大地图的一部分。每张瓦片分别单独下载,再由浏览器把这些瓦片组装成完整的地图图片。 为了无缝程序地图,这些瓦片必须并行的且必须尽可能快的下载。为了实现并行下载,应用程序给这些瓦片图片分配了四个域名,mt0, mt1, mt2 and mt3。例如,Firefox 3.0+浏览器允许为每域名最多并行建立 6 个链接,地图瓦片最多可并行使用 24 个请求链接。下面来自 Firebug's Net 面板的截屏显示了这个技术在 Firefox 中的效果,15个请求被分配到域名 mt[0-3] 上并行工作:

Web 性能最佳实践 2-最小化往返时间 - Sunny_叶纷飞 - 网赚

附加资源

 

 

译者主页 www.our3w.com/ddm/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值