《高性能网站建设指南》笔记

 

规则1:减少HTTP请求

        1. 图片地图(map)

        2. CSS Sprites

        3. 内联图片  data:[<mediatype>][;base64],<data>

        4. 合并脚本和样式表

规则2:使用内容发布网络(CDN)

规则3:添加Expires头

        更新方法:修改链接/组件文件名(比如设置版本号)

规则4:压缩组件(gzip)

规则5:将样式表放在顶部(使用link标签将样式表放在文档head中)

规则6:将脚本放在底部

规则7:避免CSS表达式

        (IE:expression【推荐一次性表达式/事件处理器】)

规则8:使用外部JavaScript和CSS

规则9:减少DNS查找

        通过keep-alive和较少的域名来减少DNS查找

规则10:精简JavaScript (精简、混淆)

规则11:避免重定向

规则12:  删除重复脚本

规则13:配置ETag(配置或移除ETag)

规则14:使Ajax可缓存

 


 

绪言A:


 

  至少有80%的时间花在了显示Web页面上,并且这些时间是花在html文档下载完毕后发生的。

 

14条规则:


 

 

规则1: 减少http请求


 

  方法:

    1. 图片地图(Image Map):在一个图片上关联多个url,目标url的选择取决于用户点击了图片上的哪个位置。

      图片地图有两种类型:服务器端图片地图(将所有点击提交到同一个目标url,向其传递用户点击的x,y坐标。Web应用程序将该x,y坐标映射为适当的操作)和客户端图片地图(将用户的点击映射到一个操作)。映射通过html的map标签实现。

      缺点:在定义图片地图上的区域坐标时,如果采用手工的方式则很难完成且容易出错,而且除了矩形意外无法定义其他星座。通过dhtml创建的图片地图则在ie中无法工作。

    2. CSS Sprites

      也可以合并图片,但更为灵活。

      方法:使用同一个背景图片,然而每个元素有一个不同的类,通过background-position属性指定了CSS Sprites的偏移量:

 1 <style type="text/css">
 2     #navbar span {
 3         width: 31px;
 4         height: 31px;
 5         display: inline;
 6         float:left;
 7         background-image: url(/img/sprite.gif);
 8     }
 9     .home {background-position: 0 0; margin-right: 4px; margin-left: 4px;}
10     .gifts {background-position: -32px 0; margin-right: 4px;}
11     .cart {background-position: -64px 0; margin-right: 4px;}
12     .settings {background-position: -96px 0; margin-right: 4px;}
13     .help {background-position: -128px 0; margin-right: 0;}
14 </style>
15 
16 <div id="navbar" style="background-color: #F4F5EB; border: 2px ridge #333; width:180px; height: 32px; padding: 4px 0 4px 0">
17     <a href="javascript:alert('Home')"><span class="home"></span></a>
18     <a href="javascript:alert('Gifts')"><span class="gifts"></span></a>
19     <a href="javascript:alert('Cart')"><span class="cart"></span></a>
20     <a href="javascript:alert('Settings')"><span class="settings"></span></a>
21     <a href="javascript:alert('Help')"><span class="help"></span></a>
22 </div>

      CSS Sprites还可以通过合并图片减少http请求,还降低了下载量。虽然会有人任务合并后的图片会比分离的图片总和要大,因为合并的图片中包含有附加的空白区域。实际上,合并后的图片会比分离的图片总和要小,因为它降低了图片自身的开销(颜色表,格式信息等)

    3. 内联图片

      data:[<mediatype>][;base64],<data>

      通过使用data:URL模式可以在Web页面中包含图片但无需任何额外的http请求。IE还不支持。同时可能存在数据大小上的限制。

      由于data:URL是内联在页面中的,在跨越不同页面时不会被缓存,所以不要去内联公司logo

    4. 合并脚本和样式表

 

规则2: 使用内容发布网络(CDN,Content Delivery Network)


 

  如果应用程序Web服务器离用户更近,则一个http请求的响应时间将缩短。另一方面,如果组件Web服务器离用户更近,则多个http请求的响应时间将缩短。

  概念:

    CDN是一组分布在多个不同地理位置的Web服务器,用于更加有效地向用户发布内容。

  优点:

    1) 可缩短响应时间

    2) 有助于缓和Web流量峰值压力

  缺点:

    1) 响应时间可能会受到其他网站的影响。因为CDN服务提供商在其所有客户之间共享Web服务器组。

    2) 无法直接控制组件服务器所带来的特殊麻烦。

  CDN用于发布静态内容,比如图片、脚本、样式表和Flash。提供动态html页面会引入特殊的存储需求-----数据库连接,状态管理,验证,硬件和OS优化等。这些复杂性超过了CDN的能力范围。另一方面,静态文件更容易存储并具有较少的依赖性。

 

规则3: 添加Expires头 


 

  【Expires: Thu,15 Apr 2010 20:00:00 GMT】

  作用: 

    Web服务器使用Expires头来告诉Web客户端它可以使用一个组件的当前副本,直到指定的时间 为止。HTTP规范中简要的称该头为“在这一日期/时间之后,响应将被认为是无效的”。它在HTTP响应中发送。
    如果页面中的一个图片返回了这个头,浏览器在后续的页面浏览中会使用缓存的图片,将HTTP请求的数量减少一个。
 
  缺点:
    Expires头使用一个特定的时间,它要求服务器和客户端的时钟严格同步
    另外,过期日期需要经常检查,并且一旦未来这一天到来了,还需要在服务器配置中提供一个新的日期
 
  另一种选择:Cache-Control: 【Cache-Control:max-age=315360000】
    Cache-Control可以克服Expires头的限制。
    Cache-Control使用max-age指令指定组件被缓存多久。它以秒为单位定义了一个更新窗。
 
  更新的方法:
    Expires头会使浏览器直接从硬盘上读取组件而无需生成任何新的http流量。因此,即使在服务器上更新了组件,已经访问过网站的用户也不大可能获取新的组件。为了确保用户能获取组件的最新版本,需要在所有html页面中修改组件的文件名
    最有效的解决方案是修改其所有链接,这样,全新的请求将从原始服务器下载最新的内容。
    比如可以通过嵌入版本号
 

规则4: 压缩组件


 

  如何压缩:
    Web客户端可以通过http请求中的Accept-Encoding头来表示对压缩的支持:
      Accept-Encoding: gzip,deflate
    如果web服务器在请求信息中看到这个头信息,它就可以通过响应的 Content-Encoding头信息来返回服务器可用的压缩方式。
      Content-Encoding: gzip
 
  压缩什么:
    基于文本的资源如html,js,css,xml都适用于压缩。然而对于图片而言,却不应该对图片进行压缩,因为图片本身是已经被压缩过了,如果再进行gzip压缩,有可能得到的结果是和图片本身大小相差不大或更大,这样就浪费了服务器的CPU资源来做无用功了。
 
  优缺点:
    优点:压缩组件可以减少Http响应时间,提升传输效率。
    缺点:服务器要通过花费额外的CPU周期来完成压缩,客户端要对压缩文件进行解压缩。
 
  代理缓存的问题
    浏览器直接与服务器通信时,基于Accept-Encoding 都可以很好地工作。
    但如果通过代理:

      代理缓存服务器是一个中间层,位于客户端和服务器之间。使用代理缓存的情况下,浏览器将不直接与服务器通信,而是通过代理发送请求。这种情况下,压缩就要考虑额外的东西了。

      首先,假设到达代理的是一个来自不支持gzip的浏览器的请求,代理会将请求转发到web服务器,此时web服务器的响应是未经过压缩的,这个响应会把代理服务器缓存起来并发给浏览器。现在,假设到达代理的第二个请求来自一个支持gzip浏览器,请求的是与之前相同的URL,代理会直接使用未经压缩的缓存响应,那么久失去了进行压缩的机会了。考虑更糟糕的情况,第一个请求来自支持gzip的浏览器,第二个请求来自不支持gzip的浏览器,这样第二个请求得到的缓存响应将无法被解码,导致出错。

      解决这一问题的方法就是在Web服务器的响应中添加Vary头,Vary:Accept-Encoding,表示web服务器告诉缓存服务器分别为每一个Accpet-Encoding请求头缓存。在前面的例子中,代理通过识别Vary头,对响应缓存不同的版本,避免出错。

    
 

规则5: 将样式表放在顶部(使用Link标签将样式表放在文档head中)


 

  将样式表放在文档底部会导致在浏览器中阻止内容逐步呈现。为避免当样式变化时重绘页面中的元素,浏览器会阻塞内容逐步呈现。
  两种方法:
    1. link标签:
      <link rel="stylesheet" href="style.css">
    2. @import规则:
      <style>    
        @import url("style.css");
      </style>
      @import规则可能会导致白屏,或组件下载时的无序性。
 

规则6: 将脚本放在底部


 

  使用并行下载:要考虑带宽和CPU速度。使用两个主机名更好
  脚本阻塞下载:然而,在下载脚本时不可以使用并行下载。这是为了保证脚本能够按照正确的顺序执行。
 

规则7: 避免css表达式


 

  CSS表达式适用于ie5及之后版本的支持。
    background-color: expression( (new Date()).getHours()%2 ? "#B8D4FF" : "#F08A00");  //ie
  其问题在于:
    表达式的求值频率太高了。它们不止在页面呈现和大小改变时求值,当页面滚动、甚至鼠标拖拽都要求值。
  解决方法:
    1. 一次性表达式
    2. 事件处理器
 

规则8: 使用外部JavaSript和CSS


 

  纯粹而言,内联快一些。因为外部示例需要承担多个http请求带来的开销。
  但是!现实中还是外部文件较快。因为JS和CSS文件有机会被浏览器缓存起来
  
  主要权衡方法:
    页面浏览量:
      每个用户产生的页面浏览量越少,则内联的更好。
    用户缓存外部组件的可能性——携带完整缓存和空缓存的页面浏览量。
      如果网站本质上可以为用户带来更高完整缓存率,使用外部文件的收益就更大。如果不大可能产生完整缓存,则内联是更好选择。
  
    重用率
      此外,如果网站中每个页面都使用了相同的js和css,使用外部文件可以提供这些组件的重用率,这时候使用外部文件更有优势。所以这时候还要考虑重用度。
  
  两全其美的方法:
    1) 加载后下载:
      作为多次页面浏览量中的第一次的主页,我们希望为主页内联Js和CSS,但又能为所有后续页面浏览量提供外部文件。这可以通过在主页加载完成后动态下载外部组件来实现(通过onload事件)。这能够将外部文件放到浏览器中的缓存中以便用户接下来访问其他页面。
    2) 动态内联:
      如果主页服务器知道一个组件是否在浏览器的缓存中,它可以在内联或使用外部文件之间做出最佳的选择。尽管服务器不能查看浏览器缓存中有什么,但可以用cookies做指示器。如果cookies不存在,就内联js和css。如果cookie出现了,则可能外部组件位于浏览器的缓存中,并使用了外部文件。
 

规则9: 减少DNS查找(通过使用Keep-Alive和较少的域名来减少DNS查找)


 

  DNS:将主机名映射到IP地址上。 同时,这也是开销。
 
  减少页面花在DNS查找上的时间的方法:
    DNS缓存和TTL
      将DNS查找缓存起来。
      影响DNS缓存的因素:
        TTL(Time-to-live, 存活时间):查找返回的DNS记录中含有这个值,该值告诉客户端可以对该记录缓存多久
    减少DNS查找
      当客户端的DNS缓存为空(浏览器和操作系统都是)时,DNS查找的数量与Web页面中唯一主机名的数量相等。这包括url,图片,脚本文件,样式表,Flash对象等的主机名。减少唯一主机名的数量就可以减少DNS查找的数量。
      但是,减少唯一主机名的数量就会潜在地减少页面中并行下载的数量。避免DNS查找降低了响应时间,单减少并行下载可能会增加时间。
      权衡之策:
        将这些组件分别放到至少2个,但不要超过4个主机名下。
    使用Keep-Alive:通过重用现有连接,从而通过避免TCP/IP开销来减少响应时间。
 

规则10: 精简JavaScript


 

  精简(Minification):
    是从代码中移除不必要的字符以减小其大小,进而改善加载时间的实践。在代码被精简后,所以的注释以及不必要的空白字符(空格,换行和制表符)都将被移除。对js而言,这可以改善响应时间效率,因为需要下载的文件大大减小了。
  混淆(Obfiscation):
    移除注释和空白,同时还会改写代码。作为改写的一部分,函数和变量的名字也将会被转换成更短的字符串,这时的代码更加精炼,也更难阅读。通常这样做的目的是为了增加对代码进行反向工程的难度,但这对提高性能而言也有帮助,因为这比精简更能减小代码的大小。
    缺陷:
      因为其更为复杂,混淆过程本身很有可能会引入错误
    维护:
      由于混淆会改变js符号,因此需要对任何不能改变的符号(比如api函数)进行标记,防止混淆修改它们。
    调试:
      更加困难。
  其他方法减少js的时间:
    内联脚本:也应该精简。
    压缩和精简:
    精简css:
      优化css——合并相同的类,移除不使用的类等。
      移除注释和空白:比如使用缩写(“#666”代替“#666666”)和不必要的字符串(“0”代替“0px”)
 

规则11: 避免重定向


 

  概念:
    重定向(Redirects)用于将用户从一个url重新路由到另一个URL。
    状态码:3xx,最常用是301和302
  类型:当Web服务器向浏览器返回一个重定向时,响应中就会拥有一个范围在3xx的状态码。这表示用户代理必须执行进一步操作才能完成请求。几种3xx状态码:
1         300     Multiple Choices (基于Content-type)
2         301     Moved Permancently
3         302     Moved Temporarity(亦作Found)
4         303     See Other(对302的说明)
5         304     Not Modified    并非真的重定向,用来响应条件GET请求,避免下载已经存在于浏览器缓存中的数据
6         305     Use Proxy
7         306     (不再使用)
8         307     Temporary Redirect(对302的说明)

  

  重定向之外的选择:

    1. 一种重定向的发生情况:url的结尾必须出现斜线(/)而没有出现时

        当主机名后缺少结尾斜线时是不会发生重定向的

    2. 用于:1)将旧url转移到新url,通过重定向整合代码库;2)将一个网站的不同部分连接起来,以及基于一些条件(浏览器类型、用户账号类型等)来引导用户

      替代方法:

        1)Alias, mod_rewrite和DirectorySlash要求除url外还要提交到一个接口(处理器或文件名),但是易于实现

        2)如果两个后端位于同一台服务器上,则它们的代码可能能够自己连接。利于,旧的处理器代码可以通过编程调用新的处理器代码

        3)如果域名变了,可以使用一个CNAME(一条DNS记录,用于创建从一个域名指向另一个域名的别名)让两个主机名指向相同的服务器。如果能做到这一点,这里提到的技术(Alias、mod_rewrite、DirectorySlash和直接连接代码)就是可行的。

    3. 用于跟踪内部流量: 重定向常用于跟踪用户流量的流向。

      替代方法:

        使用Referer日志来跟踪流量去向。(内部流量)

    4. 跟踪出站流量

      替代方法:

        1)使用信标(beacon)

        2)使用XMLHttpRequest

    5. 美化url:使用重定向使得url更加美观并易于记忆

      使用alias, mod_rewrite, DirectorySlash和直接链接代码来避免重定向

 

规则12: 删除重复脚本


 

  解决方法:
    1)在模板系统中实现脚本管理模块
    2)在php中创建一个称作insertScript的函数<?php insertScript("menu.js") ?>。 即判断该脚本是否被包含
  

规则13: 配置ETag


 

  定义:
    实体标签(Entity Tag, ETag)是Web服务器和浏览器用于确认缓存组件有效性的一种机制。
 
  组件如何被缓存和确认?
    1) Expires头:如果组件没有过期,那么它就是新鲜的。这取决于Expires头的值
    2)条件get头:如果缓存的组件过期了(或者用户明确地重新加载了页面),浏览器在重用之前必须检查它是否仍然有效。这称作一个条件GET请求。如果浏览器缓存中的组件是有效的,原始服务器不会返回整个组件,而是返回一个“304 Not Modified”状态码。
      服务器在检查缓存的组件是否和原始服务器上的组件匹配时有两种方法:
        a. 比较最新修改日期: 通过Last-Modified响应头来返回组件的最新修改日期
        b. 比较实体标签: (“实体”是“组件”的另一种称呼)。ETag是唯一标识了一个组件的一个特定版本的字符串。唯一的格式约束是该字符串必须使用引号引起来。
 
  ETag带来的问题:
    通常使用组件的某些属性来构造,这些属性对于特定的、寄宿了网站的服务器来说是唯一的。当浏览器从一台服务器上获取了原始组件,之后,又向另一台不同的服务器发起条件GET请求时,ETag是不会匹配的。
    如果ETag不匹配,用户就会收到普通的200响应以及组件的所有数据。而对组件进行不必要的重新加载还会影响服务器的性能并增加带宽开销。
    同时,ETag还降低了代理缓存的效率。
 
  解决方法:
    从ETag中移除changeNumber或完全移除ETag。
 

规则14: 使Ajax可缓存(确保Ajax请求遵守性能指导,尤其应具有长久的Expires头【使响应可缓存】)


 

  1)优化Ajax请求:
    规则3. 使响应可缓存(最重要)
    规则4. 压缩组件
    规则9. 减少DNS查找
    规则10. 精简JS
    规则11. 避免重定向
    规则13. ETag——用还是不用

posted on 2016-08-14 21:08  杠子  阅读(1663)  评论(0编辑  收藏  举报

导航