在网站获取 HTML 的时候,花费的时间不到总响应时间的 20%,80% 的时间花在了其他组件,例如,图片、JS、CSS、脚本等。在进行时,关键是剖析当前的性能,找到在那里可以获的最大的改进。所以,这种情况下我们应该关注前端性能。

1.减少 HTTP 请求

在性能的黄金法则里,80% 以上的时间都花在了 HTML 文档所引用的组件进行的请求上。因此,改善响应时间的最简单途径就是减少组件的数量,从而减少 HTTP 请求。包括:图片地图、CSS Sprites、内联图片和脚本、样式表的合并。

图片地图、CSS Sprites 通过合并图片来减少组件的数量,从而减少 HTTP 请求
内联图片则通过 data:URL 可以在 Web 页面包含图片而不需要额外的 HTTP 请求。由于内联在页面中,在跨越页面的时候不会缓存,而且不要内联公司 Logo,会使页面变大。
合并脚本和样式表,尽管这跟软件开发追寻的方式和模块化的原则不同。解决的办法就是遵循编译型的文件,保持 JS 的模块化。

2.使用 CDN

通过分布在不同地理位置的 Web 服务器,更有效的向用户发布内容。
可以提升性能,缩短时间,减少开销,缺点是响应时间可能会受到其他网站影响,无法修改 HTTP 响应头。

3.添加 Expires 头

浏览器使用缓存来减少 HTTP 请求的数量,并减少 HTTP 响应的大小,是 Web 页面加载的更快。Web 服务器使用 Expires 头告诉 Web 客户端可以使用组件的副本,知道指定的时间为止。简言
之:在这个时间之后,资源失效。

Exires 很好的改善了性能,但是他要求服务器和客户端的时钟严格同步,而且,过期日期也需要经常检查,并且一旦这天到来了,还需要在服务器的配置中提供一个新的日期。

有没有更好的方式呢?
当然,那就是 Cache-Control 使用 max-age 指定资源被缓存多久。以秒为单位,少于 max-age 则使用缓存。两者都用的时候,Cache-control 优先。

Expires 很多的利用缓存,减少了 HTTP 请求,有个问题,当资源改变时,如何更新呢?Mark Nottingham 在 Caching Tutorial for Web Authors and Webmasters 中给的建议是:

最有效的解决方案是修改其所有的链接,这样,全新的请求就会从原始服务器上下载最新的内容

在雅虎,是将所有组件的文件名使用变量。更新的时候,只需要在某个地方修改变量,并且将版本号嵌在组件中,这样不仅可以改变文件名,还能调试时更容易准确找到源代码的文件。

4.Gzip 压缩组件

Gzip 是通过减小响应包得大小来减少响应时间。Web 客户端是通过 HTTP 请求中的 Accept-Encoding 头来标示对压缩的支持。
request:Accept-Encoding:gzip, deflate
response:Content-Encoding: gzip

5. 将样式表放在顶部

讲样式表放在页面的底部会阻碍页面逐步呈现,可以通过将文档转移到文档的 HEAD 中解决这个问题。

6. 将脚本放在底部

脚本,比如 javascript 会引起类似的问题,但解决方案恰好相反,将脚本从页面顶部移到底部。

使用样式表,页面逐逐步呈现会被阻止,知道所有的样式表下载完成。这就是最好将样式表移到文档的 HEAD 中的原因,这样就会先下载他们而不会阻止页面呈现。使用脚本时,对于所有位于脚本一下的内容,逐步呈现都会被阻塞了。将脚本放在页面越靠下的地方,意味着越多的内容能够逐步逐步呈现

对响应时间影响最大的就是页面组件的数量。浏览器是并行的执行 HTTP 请求, HTTP 1.1 规范建议浏览器从每个主机并行下载两个组件。很多 Web 页面需要从一个主机下载所有的组件,所以你看到的 HTTP 请求是呈阶梯状的。

并行下载脚本是被禁用的,使用了不同的主机名,浏览器也不会启动其他的下载。这是为了保证脚本能够按照正确的顺序执行。

7. 避免 CSS 表达式

8. 使用外部 Javascript 和 CSS

该使用内联还是外部的 JS,CSS 要从三个方面考虑,页面查看、空缓存 or 完整缓存和组件重用

页面查看越多,使用外部效果越好
访问页面缓存的比例越大,外部效果越好
如果每个页面都用了相同的 JS 和 CSS,页面组件重用率越高,使用外部效果越好

9. 减少 DNS 查找

DNS 查找可以缓存起来提高性能。可以缓存在 ISP 或者局域网中一台特殊的缓存服务器上,但我们这里要探索的时操作系统的缓存。

服务器可以表明记录可以被缓存多久。查找返回的 DNS 记录包含了一个存活时间,该值告诉客户端可以对该记录缓存多久。

10. 精简 JavaScript

精简是从代码中一出不必要的字符以减少其大小,进而改善压缩时间,比如删除注释和不必要的空白字符。
压缩,使用 gzip 压缩产生的影响最大,但精简能够进一步减小文件大小。
精简 CSS,合并相同的类,移除不必要的类等。

11. 避免重定向

重定向用户将用户从一个 URL 重新路由到另外一个 URL。重定向有很多种,301 和 302 是最常用的两种。重定向会使你的页面变慢。

12. 移除重复脚本

导致重复脚本的两个主要因素——团队大小和脚本数量。
重复脚本损伤性能的方式有两个——不必要的 HTTP 请求和执行 JavaScript 所浪费的时间。
避免重复脚本的一种方法是,在你的模板系统中实现一个脚本管理模块。确保脚本只被包含一次,另外建议使用 getVersion ,这个函数会查找脚本,追加对应的版本号,上面我们介绍了可以设置一个长久的 Expires 头,一旦文件内容发生变化,文件名也必须改变,可以通过像组件中添加版本号来实现。

13. 配置 ETag

ETag (Entity Tag)是 Web 服务器和浏览器用于确认缓存组件的有效性的一种机制。

ETag 用于检测浏览器缓存中得组件和原始服务器上的组件是否匹配。ETag 是一个字符串,用来唯一标识一个组件。ETag 的加入提供了更加灵活的机制。跟请求头中的 If-Non-Match 搭配使用。如果ETag 是匹配的,就会返回 304 Not Midified

对于网站服务器集群来说,ETag 不同的服务器值不匹配。例如:你的集群中有 n 台服务器,下一次用户缓存的 ETag 能和服务器匹配的概率是 1/n。而且它还降低了代理缓存的效率。当 ETag 不匹配时,请求会被发送到原始的服务器,原始服务器到代理,再到用户,会产生两个 200 响应。

所以在要使用 ETag 必须恰当的配置才能发挥最佳效率。

14. 使 Ajax 可缓存

Ajax 表示「异步的 JavaScript 和 XML」,其中 XML 最著名的还是 JSON。Ajax 不是一个单独的、需要许可证的技术,而是一组技术,包括 JavaScript、CSS、DOM 和异步数据获取。其目的是为了突破 Web 本质的开始-停止交互方式。向用户显示一个白屏然后重绘整个页面不是一种好的用户体验。而 Ajax 在 UI 和 Web 服务器之间插入一层。这个 Ajax 层位于客户端,与 Web 服务器进行交互以获取请求的信息,并与表现层交互,仅更新必要的组件。它将 Web 体验从「浏览页面」转变为「与应用程序交互」。

异步不等于实时(Aynchronous = Instantaneoud?)
Ajax 一个明显的优点就是向用户提供了实时的反馈,因为他异步的从后端请求信息。

用户是否需要等待的关键因素在于 Ajax 的请求是被动的还是主动的。被动请求是为了将来使用而预先发起的,例如基于 Web 的邮件客户端,可能会被动请求在用户真正需要之前下载用户的地址簿。主动请求是基于用户当前操作而发起的。例如查找所有用户搜索匹配的邮件。

确保 Ajax 请求遵循性能指导,尤其应该具有长久的 Expires.

注:此文是阅读《High Performance Web Sites》阅读笔记