只懂JS是不够的,前端开发人员必须知道如何提高浏览器端性能。本文记录《高性能网站建设指南》的若干技术要点。
###用到的HTTP基础知识
HTTP是一种客户端/服务器协议,由请求和响应构成。浏览器向一个特定的URL发送HTTP请求,URL对应的宿主服务器发回HTTP响应。和很多Internet服务一样,该协议使用简单的纯文本格式。请求的类型有GET、POST、HEAD、PUT、DELETE、OPTIONS和TRACE。最常见的是GET请求。
###规则1:减少HTTP请求
####图片地图
####CSS Sprites
最大优点——通过合并图片减少HTTP请求。次要优点——合并后的图片比分离的图片总尺寸要小。如果需要在页面中为背景、按钮、导航栏、链接等提供大量图片,此技术绝对是一种优秀的解决方案——干净的标签、很少的图片和很短的响应时间。
####内联图片
####合并脚本和样式表
前端工程师必须选择是对JavaScript和CSS进行“内联”还是将其放在外部的脚本和样式表文件中。一般来说,使用外部脚本和样式表对性能更有利。然而,如果遵循软件工程师所推崇的方式和模块化的原则将代码分开放倒多个小文件中,会降低性能,因为每个文件都会导致一个额外的HTTP请求。
多个脚本应该合并为一个脚本,多个样式表也应该合并为一个样式表。理想情况下,一个页面应该使用不多于一个的脚本和样式表。
###规则2:使用内容发布网络
Use a Content Delivery Network
CDN用于发布静态内容,如图片、脚本、样式表和Flash。
不过,通常是大中型的商业网站才有考虑使用CDN的必要。
###规则3:添加Expires头
为组件添加长久的Expires头。
HTTP规范建议这个有效期不要超过一年——建议而已。
###规则4:压缩组件
对于前端人员而言,可以做的只有:压缩脚本和样式表。
规则3和4,很多工作是需要在服务器端完成的。例如让服务器开启Gzip压缩,此举非常有必要,一般来说,至少可以将数据传输体积减少50%,是加速网站的首要工作。
【附】Tomcat下开启Gzip压缩服务的方法:
在{tomcat_root}/conf/server.xml
文件中的<Connector />
中添加如下语句
1 | compression="on" |
###规则5:将样式表放在顶部
可用性工程师先驱Jakob Nielson在其web文章中从进度指示器的角度强调了可视化回馈的重要性:
进度指示器有三个主要优势——它们让用户知道系统没有崩溃,只是正在为他或她解决问题;它们指出了用户大概还需要等多久,以便用户能够在漫长的等待中做些其他事情;最后,它们能给用户提供一些可以看的东西,使得等待不再是那么无聊。最后一点优势不可低估,这也是为什么推荐使用图形进度条而不仅仅以数字形式显示预期的剩余时间。
在我们这里,HTML页面就是进度指示器。当浏览器逐步地加载页面时,页头、导航栏、顶端logo等,所有这些都会为等待页面的用户提供视觉反馈。这改善了整体用户体验。
将样式表放在文档底部会导致在浏览器中阻止内容逐步呈现。为避免当样式变化时重绘页面中的元素,浏览器会阻塞内容逐步呈现。规则5对于加载页面所需的实际时间没有太多影响,它影响的更多的是浏览器对这些组件顺序的反应。
具体而言,本规则是:使用LINK标签将样式表放在文档HEAD中。
###规则6:将脚本放在底部
HTTP1.1规范建议浏览器从每个主机名并行地下载两个组件。当然,最终具体并行下载几个组件,是由浏览器说了算的。很明显,并行下载数是8,总比并行下载数是2要好很多。但增加并行下载数量是由开销的:这取决于你的带宽和CPU速度,过多的并行下载反而会降低性能。Yahoo!的研究表明,使用两个主机名比使用1、4、10个主机名能带来更好的性能提升。
并行下载组件的优点很明显,但下载脚本时并行下载实际上是被禁用的——即使使用了不同的主机名,浏览器不不会启动其他的下载。其中一个原因是,脚本可能使用document.write
来修改页面内容,因此浏览器会瞪大,以确保页面能够恰当地布局。
总体而言:
- 脚本会阻塞对其后面内容的呈现。
- 脚本会suze对其后面组件的下载。
另外一个原因是为了保证脚本能够按照正常的顺序执行。如果并行下载多个脚本,就无法保证响应是按照特定的顺序到达浏览器的。
如果一个脚本可以延迟,那么它一定可以移到页面的底部,这是加速web页面的最佳方式。所以,尽可能将脚本移到页面底部。
###规则7:避免CSS表达式
###规则8:使用外部JavaScript和CSS
每天,用户可能只有开始的一次访问携带的是空缓存,之后的多次后续页面查看都具有完整缓存。
###规则9:减少DNS查找
Reduce DNS Lookups.
通过使用Keep-Alive和较少的域名来减少DNS查找。
PS:然后我就将本博客的DNS TTL 从600s改为3600s了。现在还不知道Github具体的作息规律……
###规则10:精简JavaScript
对JavaScript而言,就是常用的压缩。但对于CSS,能做的有限:
- 合并相同的类、移除不使用的类
- 移除注释与空白(包括换行),使用素偶写(用#606 代替 #660066 )和移除不必要的字符串(用 0 代替 0px)
###规则11:避免重定向
JavaScript也可以用于执行重定向,将document.location
设置为期望的URL即可。但是,如果你必须进行重定向,最好的技术是使用标准的 3xx HTTP状态码,这主要是为了确保后退按钮能够正常工作。方法见Use standard redirects: don’t break the back button!。
###规则12:移除重复脚本
Remove Duplicate Scripts
这个很简单:确保脚本只被包含一次。
###规则13:配置ETag
####条件GET请求
如果缓存的组件过期了(或者用户明确地重新加载了页面),浏览器在重用它之前必须首先检查它是否仍然有效。这称作一个条件GET请求。不幸的是浏览器必须产生这个HTTP请求,执行有效性检查,但这仍比简单地下载所有已过期的组件效率要高。如果浏览器缓存中的组件是有效的(即它能够和原始服务器上的组件相匹配),原始服务器就不好返回整个组件,而是返回一个“304 Not Modified”状态码。
服务器在检测缓存的组件是否和原始服务器上的组件匹配时有两种方式:
- 比较最新修改日期
- 比较实体标签
最后,看作者意思是,要么仔细配置ETag,要么就干脆别用它。
###规则14:使Ajax可缓存
Ajax不是一个单独的、需要许可证的技术,而是一组技术,包括JavaScript、CSS、DOM和异步数据获取。
Ajax是异步的、非阻塞的、未必“即时”的技术。Ajax的心脏——XMLHttpRequest。
HTTP头的Cache-Control: no-store
会使得Ajax不被缓存。
使这些Ajax请求可缓存,除了改变HTTP头之外还需要进行更多的工作。响应的个性化和动态本质必须被反映到缓存中。可供采用的最好的方式是使用查询字符串参数。
##总结
推荐度:★★★★★
大部分技术指导如今已基本成为前端开发的惯例。而书中列举的大部分网站例子,都是2007年及之前的,此书一出,这些网站应该都统统不再具有书里所描绘的前端性能问题了。这本书是读其续作之前应该大致看一遍的。相信读完续作,也可以体会到互联网发展的变迁。