高性能前端优化的十四条准则

来源:互联网 发布:c定义二维数组下标越界 编辑:程序博客网 时间:2024/05/29 14:55

说明:本文是在阅读完Steve Souder的《高性能网站建设指南》后写的。

1.规则1——减少HTTP请求

只有10%到20%的最终用户响应时间花在接收请求的HTML文档上面。剩下80%到90%的 时间花在为HTML文档所引用的所有组件(图片,脚本,flash,样式表等)进行的HTTP请求上。因此改善响应的最简单途径就是减少组件数量,由此减少HTTP请求的数量。

1.1 图片地图

图片地图允许在一个图片上关联多个URL,目标URL的选择取决于用户单击了图片上的哪个位置。下图给出了一个例子,在一个导航栏上有5张图片,单击一个图片会跳转到不同的超链接,使用图片地图可以将五个HTTP请求减少为一个HTTP请求。响应时间会降低,因为减少了HTTP开销。
图片地图有两种类型:
服务器端图片地图:将所有的点击提交到同一个目标URL,向其传递用户单击的想x、y坐标。Web应用程序将该x、y坐标映射为适当的操作。
客户端图片地图:将用户的点击映射到一个操作,而无需向后端程序发送请求。映射通过HTML的MAP标签实现,下面的HTML将图2中的导航栏转换成了图片地图。

这里写图片描述

图1是不使用图片地图的例子,我们可以看到响应时间是3992ms,图2是使用图片地图的例子,响应时间是1656ms,这是因为图片地图减少了四个HTTP请求。

这里写图片描述
图1:无图片地图的示例

这里写图片描述
图2:图片地图的示例

使用图片地图的缺点:在定义图片地图上的区域坐标时,采用手工方式很难完成且容易出错,而且除了矩形外几乎无法定义其他形状,通过DHTML创建的图片地图则在IE中无法工作。

1.2 CSS Sprites

和图片地图相比,CSS Sprites更加灵活。使用CSS Sprites将多幅图片合并为一幅单独的图片,可以有效的减少HTTP请求,同时还会减少下载量。很多人会认为合并后的图片会比分离的图片的总和大,因为合并的图片中包含附加的空白区域,实际上,合并后的图片会比分离的图片总和要小,因为它降低了图片自身的开销(如颜色表、格式信息等)。
Sprite generator 可以将图片打包成 zip 上传上去,然后把图片组合成 sprite,还可以帮你生成 CSS 代码。
中文网址:http://www.cn.spritegen.website-performance.org/

这里写图片描述
图3:Sprite generator中文网站

使用方法:
将合并后的图片作为背景图片,一个名为navbar的div包含了5个链接,每个链接包围在一个 span 中,它们使用同一个背景图片,定义在 #navbar span中,每个span都有一个不同的类,通过background-position属性指定CSS Sprites的偏移量。

这里写图片描述
图4.1:CSS Sprites使用样例

这里写图片描述
图4.2:CSS Sprites使用样例

1.3 内联图片
使用 data:URL的模式在Web页面中包含图片但无需任何额外的HTTP请求。我们都很熟悉包含http:模式的URL,其他类似模式包括ftp:,file:和mailto:。除此之外还有很多的模式,如:smtp:、pop:、dns:、whois:、finger:、daytime:、news:和run:。
data:URL模式在1995年被首次提议,规范对它的描述为:允许将小数据块内联为“立即数”,数据就在其URL自身中。格式如下:

这里写图片描述

data:URL模式主要的缺陷是不受IE的支持,直到版本7都是如此。另一个缺陷就是可能存在数据大小的限制,Firefox1.5支持高达100KB的内联图片,Base64编码会增大图片的代销,因此下载量会增加。将内联图放置在外部样式表中会增加一个额外的HTTP请求,但被缓存后可以得到额外的收获。

1.4 合并脚本和样式表
根据模块化原则,我们应该将代码放到多个小文件中,但是这样会降低性能,因为每个文件都会导致一个额外的http请求。世界前十的网站其首页上平均使用六到七个脚本和一到二个样式表。如果它们没有被缓存在用户的浏览器中,则每个文件都需要一个额外的请求,将这些单独的文件合并到一个文件中,可以减少HTTP请求的数量并缩短最终用户的响应时间。理想情况下,一个页面应该使用不多于一个脚本和样式表。

2.规则2——使用内容分发网络 CDN

内容分发网络(conten delivery network)是一组分布在多个不同地理位置的Web服务器,用于更加有效地向用户发布内容。CDN除了缩短响应时间外,还可以用来做备份、扩展存储和进行缓存,并有助于缓和Web流量峰值压力。
CDN用于发布静态图片(将所有静态组件转移到CDN),图片,脚本样式表,Flash,静态文件更易存储,有较少的依赖性,对于地理上分散的人群来说,CDN能更加容易地得到响应速度上的提高。

3.规则3——添加Expires头

Web页面包含大量组件,首次访问时间并不是唯一需要考虑的,页面的初访者会进行很多HTTP请求,但是可以使用一个长久的Expires头,使得这些组件被缓存。
3.1 Expires头
长久的expires经常用于图片,然而可以用于所有组件,很多顶级网站并没有做到这一点,因为添加长久的ecpires头会带来额外的开发成本。
Expires:Mon,15 Apr 2025 00:00:00 GMT
它会告诉浏览器该响应的有效性会持续到2025年。

这里写图片描述
图1:不使用Expires头

这里写图片描述
图2:使用Expires头

3.2 Max-Age 和mod_expires

因为expires使用一个特定的时间,要求客户端和服务器端时钟严格同步,过期日期需要检查,还要配置新的日期,所以使用麻烦。HTTP1.1引入了Cache-Control头来克服它的限制。Cache-Control使用max-age指令来指定组件被缓存多久。以秒为单位定义了一个更 新窗。
对于不支持HTTP1.1的浏览器,你可以同时指定两个响应头——Expires和max-age.如果两者同时出现,后者将会重写前者。如果你很尽责,你仍然会担心Expires过期问题以及时钟同步问题。
幸运的是,mod_expires使你通过ExpirsDefault指令以相对方式设置日期。
ExpirsDefault ‘access plus 10 years’
时间可以设置为年月日时分秒。它同时向响应中发送Expires头和max-age头。实际过期日期根据何时得到请求而变,但是max-age有优先权。时钟同步问题和固定日期更新不用担心了。
跨浏览器改善缓存最佳方案就是使用 ExpirsDefault设置的Expires.

3.3 空缓存vs完整缓存

用户第一次访问你的网站它不会对HTTP的请求的数量产生任何影响。此时浏览器的缓存是空的。性能改进取决于是否有完整缓存。
在那些每日一次一更新的网站,带有完整缓存的页面浏览百分比很少。旅游网站,email网站中每个用户会话可能产生多次页面浏览,百分比就高。
只要用户每个月至少访问一次,或者每次会话产生多次页面浏览,完整缓存就很有用,使用长久Expires就很有必要。

3.4 不仅仅是图片

脚本,样式表,flash都可以缓存,但是HTML文档不应该使用,因为包含动态内容,每次都要更新。
大型网站,图片,样式表,脚本大部分都要缓存30天以上。但是经常需要变化的新闻图片等等,不应该使用。我们可以查看Last-Modifed中的值来看改变时间以及频率。

3.5 修订文件名

使用长久的Expires缺点是 :浏览器不会检查任何更新,直到过了过期日期。即使在服务器上更新了组件,浏览器因为缓存也不能获得最新组件。
为了确保用户能获得更新过的组件,需要在所有HTML页面中修改组件的文件名。最有效的解决方案是修改其所有链接,这样。全新的请求将从原始服务器下载最新的内容。 如果一个组件没有长久的Expires头,它仍然会存储在浏览器的缓存中,在后续的请求中,浏览器会向原服务器发送一个条件GET请求,如果组件没有改变,原始服务器可以免于发送整个组件,而是发送一个很小的头,告诉浏览器可以使用缓存组件。这些请求加起来就是节省的时间,通过使用Expires头来避免额外的HTTP请求,可以减少一半的响应时间。

4.规则4——压缩组件

规则1–3都是限制不必要的HTTP请求来减少响应时间,现在我们通过减少响应大小来减少响应时间。压缩通常可以将响应的数据量减少将近70%,gzip是典型的压缩选择,图片和PDF不应该压缩,因为它们本来就已经被压缩了,试图对它们进行压缩只会浪费CPU的资源,还会增加文件的大小。

4.1压缩是如何工作的

用于减小文件体积的文件压缩已经在email应用和ftp站点中使用了十年,同样的技术也可以用于向浏览器发布压缩的web页面。
从HTTP1.1开始,web客户端可以通过请求中的Accept-Encoding头来表示对文件压缩的支持。

Accept-Encoding:gzip

如果web服务器看到请求中有这个头,就会使用客户端列出来的方法中的一种来压缩响应。并通过响应中的Content-Ecoding来通知客户端。

Content-Ecoding:gzip

4.2 压缩什么

很多网站会压缩HTML文档,压缩脚本和样式表也是非常值得的,还包括XML和JSON在内的任何文本响应。根据经验,通常对大于1KB或2KB的文件进行压缩。mod_gzip_minimum_file_size指令控制着希望压缩文件的最小值,默认值是500B。美国十大流行网站中9个压缩了html,七个压缩了大多数脚本和样式表,只要五个压缩了所有脚本和样式表。这可以将页面减少70%。
无压缩、压缩HTML、压缩所有组件的对比:
这里写图片描述
图1:无压缩

这里写图片描述
图2:压缩HTML

这里写图片描述
图3:压缩所有组件

5.规则5——将样式表放在顶部

5.1 白屏
将css放在底部的时候(有观点觉得DHTML特性东西在最后展现,所以会把css放在底部觉得更优化。)实则不然,这样容易发生白屏和无样式内容的闪烁。
白屏容易产生的地方,特别是在IE中:

    1. 新窗口中打开时    2.  重新加载时    3.  作为主页(打开新的浏览器窗口)

5.2 无样式内容的闪烁FOUC
FOUC flash of unstyles content 产生原因是没有把样式表放在head顶部,或者使用了@import导入(即便放在前面了,样式表还是会最后下载)。
所以避免无样式内容闪烁最好方法就是使用link标签将其放在head顶部,这样不管页面是如何加载的,在新窗口打开、重新加载或者作为主页,页面都是逐步呈现的。
将样式表包含在文档中有两种方式:使用link标签和@import规则。

这里写图片描述

但是@import规则必须放在所有其他规则之前,使用@import时,会导致组件下载时的无序性,而link不仅语法简单,还能带来性能上的收益。@import规则有可能会导致霸屏现象,即便把其放在文档的HEAD标签中也是如此。

6.规则6——将脚本放在底部

脚本放在顶部会阻塞后面内容的呈现和组件的下载,进而产生白屏现象。 放在底部将既可以逐步呈现,也可以提高下载的并行度。使用脚本时,所有位于脚本以下的内容,逐步呈现都被阻塞了,将脚本放在页面靠下的地方,意味着越多的内容能够逐步呈现。将其放在底部,虽然请求时间较长,但对页面的影响很小。

7.规则7——避免css表达式

表达式的问题在于对其进行的求值频率比人们期望的要高,当页面滚动、甚至鼠标在页面上移过时都要求值,这就导致其性能低下。为了避免该低下的性能可以使用一次性表达式或者事件处理器来完成同样的功能。绝大多数使用CSS表达式的情况,都可以找到不需要CSS表达式的替代方法,事件处理器可以避免鼠标移动、滚动等事件产生的成千上万次不必要的求值。

8.规则8—— 使用外部的js和css

单纯比较而言,内联在第一次加载时要快一点,因为内联只有一个http请求。尽管如此,现实中还是使用外部文件会产生较快的页面,这是由于外部的JavaScript和CSS文件会被浏览器缓存起来,HTML文档的大小减小,而且不会增加HTTP请求的数量。
如何取决依赖三个因素:页面浏览量、空缓存VS完整缓存、组件重用。
每个用户产生的页面浏览量越少,则每次访问之间,缓存就越可能过期而从浏览器中移除,则更推荐内联。如果网站能够为用户带来高完整缓存率,使用外联的收益就更大。JavaScript和CSS外部文件边界相关的决定影响组件的重用程度。

9.规则9——减少DNS查找

DNS也是开销。通常浏览器查找一个给定主机名的IP地址要花费20~120毫秒。在DNS查找完成之前,浏览器不能从主机名那里下载到任何东西。DNS查找可以被缓存起来提高性能。首先,服务器可以表明DNS记录可以被缓存多久。查找返回的DNS记录包含了一个存活时间值(TTL,Time-to-live)。该值可以告诉客户端可以对该记录缓存多久。
但是尽管操作系统会考虑TTL值,但浏览器通常忽略该值,并设置它自己的时间限制。此外,HTTP协议中的Keep-Alive特性可以同时覆盖TTL和浏览器的时间限制。也就是说,只要浏览器和Web服务器在愉快的通信中,并保持TCP连接打开的状态,就没有理由进行DNS查找。Keep-Alive通过重用现有连接避免了重复的DNS查找,避免了TCP/IP开销来减少响应时间。
可以通过减少唯一主机名的数量来减少DNS查找的数量从而降低响应时间,但同时也会潜在的减少页面中并行下载的数量。

10.规则10——精简javascript

精简(Minification)是从代码中移除不必要的字符,比如所有的注释和不必要的空白字符,以减少其大小,进而改善加载时间的实践。
混淆(Obfuscation)是可以应用在源代码上的一种优化方式。通过移除注释和空白,同时还会改写代码,将函数和变量的名字转换为更短的字符串。通常这样做是为了增加对代码进行反向工程的难度,但也对提高性能有帮助。
精简css
精简css带来的节省通常小于js,因为注释和空白比较少。最大的潜在节省来自于优化CSS,合并相同的类,移除不使用的类等。css依赖顺序的本质(成为层叠样式表的原因)决定了这是一个复杂的问题。这个领域还需要进一步的研究和工具开发。
通常解决方案有使用颜色缩写(用#606代替#660066),移除不必要的字符串(用0代替0px)。

11.规则11——避免重定向

11.1 重定向的类型和内容

通常针对HTML文档进行重定向,但也可能用在请求页面中的组件(图片脚本)。
实现重定向有很多原因:

  1. 网站重新设计
  2. 跟踪流量
  3. 记录广告点击
  4. 建立易于记忆的URL

浏览器会自动将用户带到Location字段给出的URL。重定向所必需的信息都出现了。301和302不会被缓存,除非有附加头,如Expires和Cache-Control

11.2 将用户重定向到其他URL的方法

HTML文档中的meta refresh 标签可以在其content属性所指定的秒数之后重定向用户
http://www.baidu.com” >
js也可以执行重定向,documet.location设置为期望的url
如果你必须重定向,最好的技术是使用标准的3XX HTPP状态码,主要是为了确保后退按钮可以正常工作

11.3 HTML重定向

页面定期刷新,如果加url的,则会重新定向到指定的网页,content后面跟的是时间(单位秒),把这句话加到指定网页的里。一般也用在实时性很强的应用中,需要定期刷新的,如新闻页面,论坛等,不过一般不会用这个,都用新的技术比如ajax等。

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

重定向时的第一个HTTP请求会阻塞后面html文档的加载,四个重定向请求就会将用户带到期望HTML文档的时间多花费一半。

12.规则12——删除重复脚本

重复脚本损伤性能的方式有两种:不必要的HTTP请求和执行JavaScript所浪费的时间。

13.规则13——配置ETag

实体标签(Entity Tag,ETag)是服务器和浏览器用于确认缓存组件有效性的一种机制。
实体是我们之前提到的组件的另一种称呼。ETag是唯一标识了一个组件的一个特定版本的字符串,必须带上引号。这为验证实体提供了更为灵活的机制,可以根据user-agent,accept-language头而改变。从ETag中移除ChangeNumber或者完全移除ETag可以避免当数据已经位于浏览器缓存中时进行不必要的和低效的下载。

14.规则14——使Ajax可缓存

用户是否需要等待的关键因素在于Ajax请求是被动的还是主动的。确保Ajax请求遵守性能指导,尤其应具有长久的Expires头。

1 0
原创粉丝点击