小概述:
1.只有10%-20%的最终用户响应时间花在了下载HTML文档上,其余的80%-90%的时间花在了下载页面中的所有组件上【js,css,image,flash...】。
2.如果浏览器和服务器都支持的话,可以使用压缩来减小响应的大小。浏览器可以使用Accept-Encoding头来声明它支持的压缩,服务器使用Content-Encoding头来确认响应已被压缩。
3.条件GET请求
如果浏览器在其缓存中保留了组件一个副本,但并不确定它是否仍然有效,就会生成一个条件GET请求,即在请求头中使用If-Modified-Since头将最后修改时间发送给服务器,请求服务器验证该副本从最后修改时间以来有没有被更新过,如果 没有更新过,服务器会返回一个"304 Not Modified"状态码并不再发送响应体,浏览器直接使用缓存中的这个副本,从而得到一个更小且更快的响应。
请求: GET /us.js.yimg.com/lib/common/utils/2/yahoo_2.0.0-b2.js HTTP/1.1 Host: us.js2.yimg.com User-Agent: Mozilla/5.0 (...) Gecko/20061206 Firefox/1.5.0.9 Accept-Encoding: gzip,deflate If-Modified-Since: Wed, 22 Feb 2006 04:15:54 GMT 响应: HTTP/1.1 304 Not Modified Content-Type: application/x-javascript Last-Modified: Wed, 22 Feb 2006 04:15:54 GMT
4.Expires
条件GET请求和304响应有助于让页面加载的更快,但仍需要在客户端和服务器之间进行一次往返确认,以执行有效性检查。Expires头通过明确指出浏览器是否可以使用组件的缓存副本来消除这个需要。
响应: HTTP/1.1 200 OK Content-Type: application/x-javascript Last-Modified: Wed, 22 Feb 2006 04:15:54 GMT Expires: Wed, 05 Oct 2016 19:16:20 GMT
在第一次请求时,还是需要向服务器请求确认的,服务器会在响应中带上Expires头,告诉该缓存组件的过期时间。当浏览器看到响应中有一个Expires头时,会将Expires头和其相对应的缓存组件一起保存到缓存中。这样,在以后的请求中,浏览器只需要在其缓存中检查该组件的过期时间就可以了,不用再发送HTTP请求到服务器端进行过期确认。只要组件没有过期,浏览器就会使用缓存副本而不会进行任何HTTP请求。
规则1 减少HTTP请求
由于只有10%-20%的最终用户响应时间花在了下载HTML文档上,而其余的80%-90%的时间花在了为HTML文档所引用的所有组件【js,css,image,flash...】进行的HTTP请求上。因此,改善响应时间的最简单途径就是减少组件的数量,并由此减少HTTP请求的数量。
A.使用图片地图
<img usemap="#map1" border=0 src="/images/imagemap.gif"> <map name="map1"> <area shape="rect" coords="0,0,31,31" href="home.html" title="Home"> <area shape="rect" coords="36,0,66,31" href="gifts.html" title="Gifts"> <area shape="rect" coords="71,0,101,31" href="cart.html" title="Cart"> <area shape="rect" coords="106,0,136,31" href="settings.html" title="Settings"> <area shape="rect" coords="141,0,171,31" href="help.html" title="Help"> </map>
----从5个HTTP请求减少到只有1个HTTP请求,减少了4个HTTP请求
----使用图片地图也有缺点,在定义图片地图上的区域坐标时,如果采用手工的方式则很难完成且容易出错。
B.使用CSS Sprites(CSS 精灵)
C.使用内联图片(Inline Images)
data: URL模式-----允许将小块数据内联为‘立即数’,数据就在其URL自身之中,其格式如下:
data:[<mediatype>][;base64],<data>
eg: <IMG ALT="Red Star" SRC="data:image/gif;base64,R0lGODlhDAAMALMLAPN8ffBiYvWW lvrKy/FvcPewsO9VVfajo+w6O/zl5estLv/8/AAAAAAAAAAAAAAAACH5BAEA AAsALAAAAAAMAAwAAAQzcElZyryTEHyTUgknHd9xGV+qKsYirKkwDYiKDBia tt2H1KBLQRFIJAIKywRgmhwAIlEEADs=">
D.合并脚本和样式表
多个脚本应该合并为一个脚本,多个样式表应该合并为一个样式表。理想情况下,一个页面应该使用不多于一个的脚本和样式表
规则2 使用内容发布网络(Content Delivery Networks)
CDN是一组分布在多个不同地理位置的Web服务器,将网站的静态内容【图片、脚本、样式表和Flash】发布到最接近用户的网络的边缘,使用户可以就近取得所需的内容,提高用户访问网站的响应速度。
规则3 添加Expires头
浏览器(和代理)使用缓存来减少HTTP请求的数量,并减小HTTP响应的大小,使Web页面加载的更快。Web服务器使用Expires头来告诉Web客户端它可以使用一个组件的当前副本,直到指定的时间为止,而无需向服务器发送HTTP请求。
Expires: Thu, 15 Apr 2010 20:00:00 GMT ----该组件的有效期到2010年4月15号为止
Cache-Control头: Expires头之外的另一种选择
HTTP 1.1 引入了Cache-Control头来克服Expires头的限制。因为Expires头使用一个特定的时间,它要求服务器和客户端的时钟严格同步。另外,过期日期需要经常检查,并且一旦未来这一天到来了,还需要在服务器配置中提供一个新的日期
Cache-Control使用max-age指令来指定组件被缓存多久。它以秒为单位定义了一个更新窗。如果从组件被请求开始之后所经过的秒数少于max-age【即在max-age之内】,浏览器会将该组件缓存起来,在max-age之内再次访问该组件,则不会向服务器发送HTTP请求。
对于不支持HTTP 1.1的浏览器,可能仍然希望提供Expires头,也可以同时指定这两个响应头,如果两者同时出现,HTTP规范规定max-age指令将重写Expires头。
为图片使用长久的Expires头非常普遍,但这一最佳实践不应该局限于图片。长久的Expires头应该包含任何不经常变化的组件,包括脚本、样式表和Flash组件。但是,HTML文档不应该使用长久的Expires头,因为它包含动态内容,这些内容在每次用户请求时都将被更新。
修改文件名
如果我们将组件配置为可以由浏览器代理缓存,当这些组件改变时用户如何获得更新呢?当出现了Expires头时,直到过期日期为止一直会使用缓存的版本。浏览器不会检查任何更新,直到过了过期日期。在这段时间之内,即使在服务器上更新了组件,已经访问过网站的用户也不大可能获取最新的组件,因为前一个版本还没有过期,还在缓存中。为了确保用户能获取组件的最新版本,需要在所有HTML页面中修改组件的文件名。例如:可以将版本号嵌在组件的文件名中,有更新的话,直接修改版本号即可。
规则4 压缩组件
通过减小HTTP响应的大小来减少响应时间。如果HTTP请求产生的响应包很小,传输时间就会减少,因为只需要将很小的包从服务器传递到客户端。这一效果对速度较慢的带宽尤其明显。可以使用gzip编码来压缩HTTP响应包,并由此减少网络响应时间。这是减小页面大小的最简单的技术,但影响是最大的。
压缩是如何工作的
从HTTP 1.1开始,Web客户端可以通过HTTP请求中的Accept-Encoding头来标识对压缩的支持:
Accept-Encoding: gzip, deflate
如果Web服务器看到请求中有这个头,就会使用客户端列出来的方法中的一种来压缩响应。Web服务器通过响应中的Content-Encoding头来通知Web客户端。
Content-Encoding: gzip
---支持deflate的浏览器也支持gzip,但很多浏览器支持gzip却不支持deflate,因此gzip是最理想的压缩方法。
压缩什么
服务器基于文件类型选择压缩什么,但这通常受限于对其进行的配置。很多网站会压缩其HTML文档。压缩脚本和样式表也是非常值得的。图片和PDF不应该压缩,因为它们本来就已经被压缩了,试图对它们进行压缩只会浪费CPU资源,还有可能会增加文件大小。
压缩的成本有----服务器端会花费额外的CPU周期来完成压缩,客户端要对压缩文件进行解压缩。根据经验通常对大于1KB或2KB的文件进行压缩。
代理缓存
当浏览器直接与服务器通信时,Web服务器基于Accept-Encoding来检测是否对响应进行压缩,不管是否压缩过,浏览器都会基于响应中的其他HTTP头如Expires和Cache-Control来缓存响应。
当浏览器通过代理来发送请求时,情况就变得复杂了。假设针对某个URL发送到代理的第一个请求来自于一个不支持gzip的浏览器,由于这是到达代理的第一个请求,因此其缓存为空,代理会将请求转发到Web服务器,此时服务器的响应是未经过压缩的,然后会被代理缓存起来并发送给浏览器。假设紧接着到达代理的第二个请求访问的是同一个URL,来自于一个支持gzip的浏览器,代理会使用其缓存中(未被压缩)的内容进行响应。如果将这两个请求的顺序反一下,情况可能更严重,在这种情况下,代理的缓存中拥有内容的一个压缩版本,并将这个版本提供给后续的浏览器,而不管它们是否支持gzip。
解决这一问题的办法是,使代理缓存多个版本内容,即缓存一个压缩过的版本,一个未压缩过的版本,然后根据Accept-Encoding请求头来作分别响应。当浏览器带着Accept-Encoding:gzip访问代理时,它接收到的是压缩过的内容,而没有Accept-Encoding请求头的浏览器收到的未经压缩的内容。
实现这一方法只需在响应头中添加Vary: Accept-Encoding头即可
规则5 使用Link标签将样式表放在文档head中
CSS文件得优先下载,否则页面的呈现就会被阻塞。
规则6 将脚本放在页面底部
若将外部脚本放在head中,会阻塞页面的呈现并且阻止并行下载
规则7 避免CSS表达式
规则8 使用外部JS和CSS
纯粹而言,内联快一些,只需要一个HTTP请求就可以了。
但外部文件所带来的收益是:JS和CSS文件有机会被浏览器缓存起来。HTML文档----至少是那些包含动态内容的HTML文档,通常不会被配置为可以进行缓存。当遇到这种情况时,每次请求HTML文档都要下载内联的JS和CSS。另一方面,如果JS和CS是外部文件,浏览器就能缓存它们,HTML文档的大小减小,而且不会增加HTTP请求的数量。
组件重用
如果网站中的每个页面都使用了相同的JS和CSS,使用外部文件可以提高这些组件的重用率。在这种情况下使用外部文件更加具有优势,因为当用户在页面间导航时,JS和CSS组件已经位于浏览器的缓存中了。
相反的情况也很容易理解:如果没有任何两个页面共享相同的JS和CSS,重用率就会非常低。
加载后下载
主页通常伴随着后续页面的查看(在初始页之后,还要访问其他页面,如浏览商品信息等)。对于作为多次页面查看中的第一次的主页,我们希望为主页内联JS和CSS,但又能为所有后续页面查看提供外部文件。这可以通过在主页加载完成后动态下载外部组件来实现(通过onload事件)。这能够将外部文件放到浏览器的缓存中以便用户接下来访问其他页面。
<script type="text/javascript"> function doOnload( ) { setTimeout("downloadComponents( )", 1000); } window.onload = doOnload; // Download external components dynamically using JavaScript. function downloadComponents( ) { downloadJS("http://stevesouders.com/hpws/testsma.js"); downloadCSS("http://stevesouders.com/hpws/testsm.css"); } // Download a script dynamically. function downloadJS(url) { var elem = document.createElement("script"); elem.src = url; document.body.appendChild(elem); } // 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); } </script>
在这些页面中,JS和CSS被加载到页面中两次(先是内联的,然后是外部的)。要使其能够工作,必须处理双重定义。例如脚本,可以定义但不能执行任何函数(至少不能让用户察觉)。使用了相对单位(百分比或em)的CSS如果指定两次可能会产生问题。将这些组件放到一个不可见的IFrame中是一种更好的方式,能够避免这些问题。
规则9 减少DNS查找
规则10 精减JS
精简是从代码中移除不必要的字符以减小其大小,进而改善加载时间的实践。在代码被精简后,所有的注释以及不必要的空白字符(空格、换行和制表符)都将被移除。对于JS而言,这可以改变响应时间效率,因为需要下载的文件大小减小了
精简JS代码的最流行的工具是JSMin(http://crockford.com/javascript/jsmin)
压缩和精简
规则4强调了对内容进行压缩的重要性,并建议使用gzip来完成压缩,这通常可以使大小减小70%。gzip压缩比精简更能减小文件的大小,同时使用压缩和精简,能够达到更佳的效果。
规则11 避免重定向
重定向用于将用户从一个URL重新路由到另一个URL。重定向有很多种----301和302是最常用的两种。通常针对HTML文档进行重定向,但通常也可能用在请求页面中的组件(图片、脚本等)时。
当Web服务器向浏览器返回一个重定向时,响应中就会拥有一个范围在3xx的状态码。这表示用户代理必需执行进一步操作才能完成请求。
一个301响应: HTTP/1.1 301 Moved Permanently Location: http://stevesouders.com/newuri Content-Type: text/html
浏览器会自动将用户带到Location字段所给出的URL。重定向所必需的所有信息都出现在这个头中,响应体通常是空的。301和302响应在实际中都不会被缓存,除非有附加的头----如Expires或Cache-Control等要求它这么做。
还有其他方法可以自动将用户重定向到其他URL。HTML文档的头中包含的meta refresh标签可以在其content属性所指定的秒数之后重定向用户----
<meta http-equiv="refresh" content="0; url=http://stevesouders.com/newuri">
JS也可以用于执行重定向,将document.location设置为期望的URL即可。
重定向延迟了整个HTML文档的传输,在HTML文档到达之前,页面中不会呈现出任何东西,也没有任何组件会被下载。在用户和HTML文档之间插入重定向延迟了页面中的所有东西。
规则12 移除重复脚本
确保脚本只被包含一次
规则13 配置ETag
实体标签(Entity Tag, ETag):是Web服务器和浏览器用于确认缓存组件的有效性的一种机制。
前面已经介绍过:条件GET请求和304响应有助于让页面加载的更快,但仍需要在客户端和服务器之间进行一次往返确认,以执行有效性检查。Expires头通过明确指出浏览器是否可以使用组件的缓存副本来消除这个需要。类似的,ETag的作用也是用于检测缓存的组件是否和原始服务器上的组件相匹配。
ETag是唯一标识了一个组件的一个特定版本的字符串,唯一的格式约束是该字符串必须用引号引起来。原始服务器使用ETag响应头来指定组件的ETag。
请求: GET /i/yahoo.gif HTTP/1.1 Host: us.yimg.com 响应: HTTP/1.1 200 OK Last-Modified: Tue, 12 Dec 2006 03:03:59 GMT ETag: "10c24bc-4ab-457e1c1f" Content-Length: 1195
此后,如果浏览器必须验证一个组件,它会使用If-None-Match头将ETag传回原始服务器。如果ETag是匹配的,就会返回304状态码
请求: GET /i/yahoo.gif HTTP/1.1 Host: us.yimg.com If-Modified-Since: Tue, 12 Dec 2006 03:03:59 GMT If-None-Match: "10c24bc-4ab-457e1c1f" 响应: HTTP/1.1 304 Not Modified
ETag带来的问题
ETag的问题在于,通常使用组件的某些属性来构造它,这些属性对于特定的、寄宿了网站的服务器来说是唯一的。当浏览器从一台服务器上获取了原始组件,之后,又向另外一台不同的服务器发起条件GET请求时,ETag是不会匹配的,而对于使用服务器集群来处理请求的网站来说,这是很常见的一种情况。
如果你的组件必须通过最新修改日期之外的一些东西来进行验证,则ETag是一种强大的方法;
如果你无须自定义ETag,最好简单地将其移除,Last-Modified头可以提供完全等价的信息。而且移除ETag可以减小响应和后续请求的HTTP头的大小。
规则14 使Ajax可缓存
确保Ajax请求遵守性能指导,尤其应具有长久的Expires头
来源:https://www.cnblogs.com/notebook2011/archive/2013/03/29/2979960.html