高性能网站建设指南-下

2017-09-12 | 性能优化

# 前言

在上一篇文章《高性能网站建设指南-上》,我们了解了雅虎团队在性能优化上的技术技巧和最佳实践。

这篇文章将一一介绍书中剩下的规则。

# 使用外部JavaScript和CSS

# 内联VS外置

我们首先来对比一下内联JavaScript和CSS与将其外置之间的区别。

# 纯粹而言,内联快一些

通常在没有缓存的情况下,内联总的响应会比外置总的响应快30%~50%。这主要是因为外部的JavaScript和CSS需要承担多个HTTP请求带来的开销(上一篇文章介绍了减少HTTP请求的重要性)。

尽管结果如此,现实中还是使用外部文件会产生较快的页面。这是由于外部文件所带来的收益——JavaScript和CSS文件有机会被浏览器缓存起来。当遇到这种情况时,每次请求HTML文档都要重新下载内联的JavaScript和CSS;但是如果JavaScript和CSS是外部文件,浏览器就能缓存它们,HTML文档的大小减小,而且不会增加HTTP请求的数量。

# 页面浏览量

每个用户产生的页面浏览量越少,使用内联JavaScript和CSS就更有优势。

另一方面,如果用户能够产生很多的页面浏览量,使用外部的JavaScript和CSS的收益会随着页面浏览量的增长而增加。

# 组件重用

如果你的网站中多个页面都使用了相同的JavaScript和CSS,使用外部文件可以提高这些组件的重用率。

# 加载后下载

对于拥有较大浏览量的首页来说,有一种技术可以平衡内置代码带来的HTTP请求减少与通过使用外部文件进行缓存带来的好处。其中一个就是在首页中内置JavaScript和CSS,但是在页面下载完成后动态下载外部文件,在子页面中使用到这些文件时,它们已经缓存到浏览器了。这可以通过在主页加载完成后动态下载外部组建来实现(通过onload事件)。

window.onloag = function() {
  downloadCSS('...');
  downloadJS('...');
}

// Download a stylesheet dynamically
function downloadCSS(url) {
  var elem = document.createElement("link");
  elem.rel = "stylesheet";
  elem.type = "text/css";
  elem.href = url;
  document.body.appendChild(elem);
}

// Download a script dynamically
function downloadJS(url) {
  var elem = document.createElement("script");
  elem.src = url;
  document.body.appendChild(elem);
}

# 减少DNS查找

域名系统(DNS)提供了域名和IP的对应关系,就像电话本中人名和他们的电话号码的关系一样。

当在浏览器地址栏输入https://blog.wongjz.com后,DNS解析服务器就会返回这个域名对应的IP地址。通常这一过程要花费20~120毫秒,在DNS查找完成之前,浏览器不能从主机名那里下载到任何东西。响应时间依赖于DNS解析器、它所承担的请求压力、你与它之间的距离和你的带宽速度。

# DNS缓存和TTL

DNS查找可以被缓存起来以提高性能。这种缓存需要一个特定的缓存服务器,这种服务器一般属于用户的ISP提供商或者本地局域网控制,但是它同样会在用户使用的计算机上产生缓存。DNS信息会保留在操作系统的DNS缓存中(微软Windows系统中DNS Client服务)。我们可以使用ipconfig命令来查看刷新DNS Client服务——

ipconfig /displaydns
ipconfig /flushdns

大多数浏览器拥有独立于操作系统的缓存。由于浏览器有自己的缓存记录,所以浏览器会先在自己的缓存记录中查找。只有当缓存丢弃了记录时,它才会向操作系统询问地址——然后操作系统或者通过其缓存来响应这个请求,或者将请求发送给一台远程服务器。

# TTL

查找返回的DNS记录包含了一个存活时间(Time-to-live,TTL)值。该值告诉客户端可以对该记录缓存多久。

HTTP协议中的Keep-Alive特性可以覆盖TTL的时间限制。只要浏览器和Web服务器愉快地通信着,并保持着TCP连接打开的状态,就没有理由进行DNS查找。

浏览器对缓存的DNS记录的数量也有限制,而不管缓存记录的时间。在chrome浏览器下,进入chrome://net-internals/#dns可以查看和清除DNS缓存记录。

# 减少DNS查找

当客户端的DNS缓存为空时,DNS查找的数量与Web页面中的唯一主机名的数量相等。减少DNS查找次数可以节省响应时间,但是减少并行下载却会增加响应时间。书中的指导原则是,如果页面有大量的组件,那么把这些组件分别放到至少2个,但不超过4个主机名下。这种结果就是在减少DNS查找次数和保持较高程度并行下载两者之间的权衡了。

# 精简JavaScript

# 精简

精简Minification)是从代码中移除不必要的字符(空格、换行、制表符和注释)以减小文件大小,进而改善加载时间的实践。

# 混淆

混淆Obfuscation)是在精简的基础上,将函数和变量的名字改写成更短的字符串。混淆后的代码更加精炼,也更难阅读,通常这样做是为了增加对代码进行反向工程的难度,但这对提高性能也有帮助,在jQuery源码一开始定义变量就有体现:

var
  location = window.location,
  document = window.document,
  docElem = document.documentElement

混淆后:

i=e.location,o=e.document,s=o.documentElement

这样的好处就是,混淆的代码中location, document, docElem都会被替换成i, o, s,减小了精简后文件的大小。

# 压缩和精简

上一篇文章强调了压缩组件的重要性,这通常可以使大小减小70%。

gzip压缩比精简更能减小文件的大小,但精简能够进一步减小文件大小。随着JavaScript的使用量和大小不断增长,精简JavaScript代码能够得到更多的节省。

# 避免重定向

重定向Redirect)用于将用于从一个URL重新路由到另一个URL。重定向会使你的页面变慢。

# 重定向的类型

重定向的响应会拥有一个范围在3XX的状态码。状态码301 Moved Permancently302 Moved Temporarily是使用最多的。304 Not Modified并不真的是重定向——它是用来响应条件GET请求,避免下载已存在的缓存文件。

重定向的响应包含了一个Location头,它所给出的URL就是会自动跳转到的地址。

# 重定向是如何损伤性能的

如果一个HTML文档的响应是重定向,那在重定向完毕并且真正需要的HTML文档下载完毕前,是没有任何东西显示给用户的,这影响了页面呈现的速度

# 删除重复脚本

在同一个页面中重复引用相同的JavaScript文件会影响页面的性能。有两种主要因素导致相同的脚本被重复引用的奇怪现象发生:团队规模和脚本数量。

如果真的存在这种情况,重复脚本会引起不必要的HTTP请求和无用的JavaScript运算,这降低了网站性能。

一个避免偶尔发生的两次引用同一脚本的方法是在模板中使用脚本管理模块引用脚本。在HTML页面中使用<script />标签引用脚本的最常见方法就是:

<script type="text/javascript" src="menu_1.0.17.js"></script>  

# 配置ETag

实体标签ETag)是Web服务器和浏览器用于确认缓存组件有效性的一种机制。

在进入ETag的细节之前,我们先来回顾下上篇文章添加Expires头

# 条件GET请求

如果缓存的组件过期了,浏览器在重用它之前会先检查它是否仍然有效,这称作为条件GET请求

服务器在检测缓存的组件是否和原始服务器上的组件匹配时有两种方式:

# 实体标签

ETag头是唯一标识了一个组件的一个特定版本的字符串。ETag为验证实体提供了比Expires头更为灵活的机制。例如,如果实体一句User-AgentAccept-Language头而改变,实体的状态可以反映在ETag中。

如果浏览器需要验证一个组件,它会使用If-None-Match头将ETag传回服务器。如果ETag匹配,就会返回304状态码。

ETag 304

ETag 304

如果ETag匹配,就会返回200状态码,并重新下载整个文件,响应增加了10163字节。

ETag 200

ETag 200

# 使Ajax可缓存

Ajax经常被提及的一个好处就是由于其从后台服务器传输信息的异步性而为用户带来的反馈的即时性。但是,使用Ajax并不能保证用户不会在等待异步的Ajax响应上花费时间。“异步”并不异味着“即时”。

# 优化Ajax请求

# Ajax缓存

改善Ajax请求最重要的方式就是使响应可缓存。

如果我们在一个Ajax的响应头中使用一个长久Expires或者Cacke-Control头来实现缓存,响应将被缓存并从磁盘上进行读取,从而得到更快的用户体验。

即使你的Ajxa响应是动态生成的,哪怕它只适用于一个用户,那么它也应该被缓存起来。这样做可以使你的Web2.0应用程序更加快捷。

# 其他优化规则

除了使Ajax可缓存之外,我们之前介绍的一些规则也适用于Ajax请求:

# 最后

关于《高性能网站建设指南》的总结到这就结束了,鉴于我自己能力的有限,对于书中一些自己不理解的地方,并没有将其内容包含进来。如果你对某条规则感兴趣,想获得一手信息的话,建议你找书来看看。