加速,加速,再加速:来自Google的网站加速技巧大全

困扰许多网站所有者很久的一个问题是网站访问速度总是那么慢。想购买独立带宽,预算不允许,想购买CDN加速,价格又太贵。那有没有经济实惠的解决办法呢?从目前的大环境来分析,我们也只有通过技术手段来解决这个问题了。

对Web开发有着深刻理解的著名搜索引擎厂商Google前段时间发布了一款Firefox /Firebug插件Page Speed(为什么是Firefox 而不是Chrome?),并已开源,网络管理员和Web开发人员使用这个插件对Web页面的性能进行评估,并提供改善性能的有益建议。并于今日呼吁大家共同努力,一起为互联网加速,可不是提什么空口号,Google已经将其研究结果和数据共享了出来,并提供了大量的网站加速教程,本文就对这些教程加以整理,与诸位共勉

css:每个声明只使用一次

要想让你的网站速度更快,其中重要的一条就是发送到浏览器的客户端代码要尽可能小,在优化css文件时,一个重要的原则就是每个声明只使用一次。这样就可以严格使用选择器组合。

例如,你可以将这两个规则

  1. h1 { color: black; } 
  2. p { color: black;  

合并成一个规则

  1. h1, p { color: black; }

这个例子是最简单的了,实际开发中的情况要复杂得多,根据我个人的开发经验,每个声明只使用一次后可以将css文件的大小降低20%-24%。下面再看一个复杂一点的例子:

  1. h1, h2, h3 { font-weight: normal; } 
  2. a strong { font-weight: normal !important; } 
  3. strong { font-style: italic; font-weight: normal; } 
  4. #nav { font-style: italic; } 
  5. .note { font-style: italic; }

根据每个声明只使用一次的原则,将其缩减为

  1. h1, h2, h3, strong { font-weight: normal; } 
  2. a strong { font-weight: normal !important; } 
  3. strong, #nav, .note { font-style: italic; } 

注意!important声明可以与众不同。

在应用这个方法时有几点需要牢记于心:

◆首先,太长的选择器可能致使这个方法无效,重复选择器如html body table tbody tr td p span.example不能减少文件尺寸大小,实际上,每个声明只使用一次可能意味着选择器的数量会上升,甚至可能导致css文件更大,使用紧凑的选择器是更有帮助的,并且可以提升样式表的可读性。

◆第二,小心css规则,当一个用户代理不能解析选择器时,必须忽略声明,当你遇到这种情况时,只需要修改一下规则,让其可以使用多次即可。

◆第三,也是最重要的一点,如果你对样式表进行了排序,这个原则会改变你的排序,但这个顺序可以由浏览器来决定,如果你使用这种方法运行出了问题,最好的解决办法就是产生一个异常,并在出现问题的地方让声明使用多次就可以了。

在使用一个声明只使用一次的方法维护样式表时要格外小心,可以通过优秀的编辑器来跟踪变化(如显示变化行),然后在需要的地方插入声明,这种做法需要合并到工作流中。其实一个有效的方法是使用缩进排版,这样就很容易发现重复的声明的了。

使用gzip压缩页面

Gzip压缩需要Web服务器的支持,在没有gzip压缩的情况下,Web服务器直接把html页面发送给浏览器,而支持gzip的Web服务器将把html压缩后再发给浏览器,支持gzip的浏览器再在本地进行解压和解码,并显示出来。Gizp会将css文件,js文件,html文件中那些重复的字符串、标签、空格和样式定义进行压缩。我们还是举一个例子加以说明。

下面的代码未压缩前69字节,压缩后85字节。

  1. < h1>One< /h1> 
  2. < h2>Two< /h2> 
  3. < h3>Three< /h3> 
  4. < h4>Four< /h4> 
  5. < h5>Five< /h5> 

我们将标题标签替换成div标签,源代码大小增加了10字节,变成了79字节,但经过压缩后只有66字节。

  1. < div>One< /div> 
  2. < div>Two< /div> 
  3. < div>Three< /div> 
  4. < div>Four< /div> 
  5. < div>Five< /div> 

HTTP缓存

如果你正确设置了HTTP缓存报头,不但Web页面载入速度更快了,而且Web服务器的负荷也减少了,是一个双赢的举措。

缓存也就是资源的本地副本,因为很多资源都不会频繁更新,这时浏览器就可以直接从本地获取资源,不用到服务器去取,节省了连接时间和资源下载时间,使用HTTP缓存真正起关键作用的是HTTP缓存报头,这个报头指定了资源的有效时间和最后修改时间。

http协议有两种方法定义资源的有效时间:Expires和Cache-Control: max-age报头。Expires报头指定资源的过期日期,而max-age指定的是当资源下载到本地后多长时间有效。实际上它们要表达的最终含义是一致的。建议将资源的有效日期指定为1个月,长一点的话可以指定为1年。这两个报头一般只需要设置一个即可,如果同时进行了设置,那么max-age的优先级比Expires要高一些。如果你的资源比这个设定期限更新得要频繁一些,那么你可以采取对资源重新命名的方法,通常的做法是在URL中给资源加上版本号,当然这是引用资源的html页面也得做对应的修改。

当本地资源是有效状态时,浏览器有两个选择,其一是全部重新下载所有资源,其二是仅下载有变化的资源,为了让浏览器只下载有变化的资源,我们需要在GET请求中加上条件,http协议给了我们两个选项:Last-Modified和ETag报头。

Last-Modified报头指资源的最后修改时间,ETag报头是每个资源不同版本的唯一身份识别号,我们推荐你使用Last-Modified报头,因为日期如果足够早,浏览器可以跳过请求全部资源的。下面是两个测试页。

关闭Expires

开启Expires

在第二次载入页面时,可以再次点击链接,或者在地址栏中再按一次回车,如果点击刷新按钮,如果资源还在缓存中的话,会强制浏览器重新使用附条件的GET请求去获取资源。

为了更清晰地看到这个完整过程,可以使用HttpWatch工具,它可以查看http报头。如果使用Apache的话,可以参考mod_expires模块的资料。

 

使用Page Speed提升网站性能

Page Speed是由Google发布了一款开源Firefox 插件,它可以评估Web页面的性能问题,并能够给出合理的优化建议,同时它也可以将资源变得更小。

◆图片压缩

图片文件中通常都包含了一些额外的信息,如JPEG图片包括创建图片所用工具的信息,PNG图片通过创新编码可以缩小文件大小,这些转换都是无损的,压缩后的图片和未压缩的图片看起都是一样的,但占用的字节数却少了,

Page Speed使用优化图像功能尝试为页面中的所有图片进行无损压缩,如果成功的话,就显示压缩后的版本,为了使用图片的最低版本,在Page Speed面板中点击链接到压缩版本,保存,使用它代替原始图片。

◆削减JavaScript

移除大型JavaScript文件中的注释和空格可以大幅度减少文件的尺寸,但功能却无丝毫损伤。Page Speed提供了削减JavaScript的功能,削减成功后Page Speed会显示一个到该文件的链接,如果要使用削减后的文件,点击那个链接,保存文件,然后到html页面中重新引用这个更小的JavaScript文件。

但这样做有一个问题,削减后的JavaScript文件的移动性就降低了,如果你经常更新的话,建议你使用另一个削减命令行工具JSMIN。

◆移除不用的css

移除css文件没有使用到的规则将有助于减少css文件的大小,因为浏览器在浏览Web页面时往往会将css文件下载到本地,这样就可以减少发送到浏览器的信息量,从而加速页面反应速度,但值得注意的是,有些外部css文件是由多个Web页面引用的,在移除时要特别小心。

Page Speed提供了移除不同的css功能,它可以告诉你那些css规则是没有被使用的。

除了上面提到的这三个功能外,Page Speed还有更多的功能等待你去发掘。

减少浏览器reflow

Reflow是浏览器为重新渲染文档或文档中的一部分,重新计算文档元素的位置和几何大小的统称,因为reflow在浏览器中是一个用户拦截操作,这对于开发人员理解如何提升reflow时间和不同文档属性(DOM深度、css规则效率、不同类型的样式变化)对reflow时间的影响。有时reflow一个元素可能需要reflow文档中的所有元素,以及跟着它的所有元素。

有很多中用户行为和DHTML变化可以触发reflow,如改变窗口大小、使用包含计算样式的JavaScript函数、从DOM中添加或移除元素以及改变某个元素的类别都可以触发reflow,值得注意的是有些行为可能导致reflow的时间比你想象中的要多。来看一张图。

主流浏览器reflow时间对比
图- 1 主流浏览器reflow时间对比

从上图可以看出不是JavaScript中任何样式的变化都会触发reflow,还可以看出浏览器版本越高reflow时间更好了。

在Google,我们对我们的页面进行了大量的测试,最终我们认为要往页面中添加UI元素时最值得考虑的因素就是reflow时间了。下面是我们整理的可以帮助减少reflow时间的方法:

◆减少不必要的DOM深度,在DOM树上任意一层发生的变化都会影响到DOM树的所有层,当然这在reflow时花的时间肯定比较多了。

◆缩小css规则,移除不用的css规则。

◆如果你使用更复杂的演示变化,如动画,那么你最好使用绝对位置或将位置固定下来。

◆避免不必要的复杂的css选择器,特别是衍生选择器,它需要更多的CPU时间处理选择器匹配。

 

优化JavaScript代码

客户端代码可以让你的应用程序动起来变得更有活力,但这些代码可能使浏览器变得更低效,不同的客户端性能也不一样,这里我们讨论几个优化JavaScript的技巧。

◆字符串的使用

在IE6和IE7中因字符串级联导致的主要问题是垃圾回收性能,虽然这些问题在IE8中已经得到解决,但如果你的用户大部分仍然在使用IE或IE7,你就得格外注意这个问题了。看一个例子先:

  1. var veryLongMessage = 
  2. 'This is a long string that due to our strict line length limit of' + 
  3. maxCharsPerLine + 
  4. ' characters per line must be wrapped. ' + 
  5. percentWhoDislike + 
  6. '% of engineers dislike this rule. The line length limit is for ' + 
  7. ' style purposes, but we don't want it to have a performance impact.' + 
  8. ' So the question is how should we do the wrapping?';

◆使用连接代替级联:

  1. var veryLongMessage = 
  2. ['This is a long string that due to our strict line length limit of', 
  3. maxCharsPerLine, 
  4. ' characters per line must be wrapped. ', 
  5. percentWhoDislike, 
  6. '% of engineers dislike this rule. The line length limit is for ', 
  7. ' style purposes, but we don't want it to have a performance impact.', 
  8. ' So the question is how should we do the wrapping?'
  9. ].join();

与此类似,在条件语句中使用级别也是很低效的,错误的做法:

  1. var fibonacciStr = 'First 20 Fibonacci Numbers 
  2. '; 
  3. for (var i = 0; i <  20; i++) { 
  4. fibonacciStr += i + ' = ' + fibonacci(i) + ' 
  5. '; 
  6. }

正确的方法:

  1. var strBuilder = ['First 20 fibonacci numbers:']; 
  2. for (var i = 0; i <  20; i++) { 
  3.   strBuilder.push(i, ' = ', fibonacci(i)); 
  4. var fibonacciStr = strBuilder.join('');

◆定义类函数

下面的函数是低效的,因为每次构造baz.Bar的实例时,会创建一个新的函数和闭包:

  1. baz.Bar = function() { 
  2.   // constructor body 
  3.   this.foo = function() { 
  4.   // method body 
  5.   }; 

正确的方法是:

  1. baz.Bar = function() { 
  2.   // constructor body 
  3. }; 
  4.  
  5. baz.Bar.prototype.foo = function() { 
  6.   // method body 
  7. }; 

使用这个方法时,无论baz.Bar构造了多少个实例,都只会为foo创建一个函数,而且不会创建闭包。

◆初始化实例变量

使用实例变量值类型初始值初始化实例变量声明,如数值、布尔值、空值、未定义或字符串,这样可以避免每次调用构造器时运行不必要的初始化代码。还是来看一个例子:

  1. foo.Bar = function() { 
  2.   this.prop1_ = 4; 
  3.   this.prop2_ = true
  4.   this.prop3_ = []; 
  5.   this.prop4_ = 'blah'; 
  6. }; 

可以使用:

  1. foo.Bar = function() { 
  2.   this.prop3_ = []; 
  3. }; 
  4.  
  5. foo.Bar.prototype.prop1_ = 4; 
  6.  
  7. foo.Bar.prototype.prop2_ = true
  8.  
  9. foo.Bar.prototype.prop4_ = 'blah'; 
  10.  

进行代替。

◆避免使用with

避免在代码中使用with,它会影响到性能,因为它修改了范围链,因此需要花费更多时间到其它范围去查找。

◆避免浏览器内存泄漏

对于Web应用程序而言,内存泄漏是很常见的事情,可能因此导致巨大的性能问题。因为浏览器的内存使用在增长,你的Web应用程序和其它程序就会变慢,最常见的内存泄漏是在JavaScript引擎和浏览器C++对象实现的DOM之间形成了循环引用,如在JavaScript引擎和IE浏览器的COM基础结构之间,以及JavaScript引擎和Firefox  XPCOM基础结构之间。

 

优化网页图片

当你在优化网页代码时,别忘了那些静态内容,包括图片,经过简单的优化就可以大大减少下载大小,并且也不会降低网站的质量。

我们总结了一些技巧,可以帮助你加快网页图片的载入速度:

◆剪裁掉多余的空白

剪裁掉多余的空白
图- 2 剪裁掉多余的空白

如果确实需要空白内边距,可以使用css代码来实现。

◆使用最佳的文件格式

可以通过一些专业的图片处理工具,如photoshop就可以直接保存为网页图片,如果将图片颜色从256色调整到32色,那文件大小会明显变小,下面来看看一张图片不同文件格式时的大小对比。

示例图片
图- 3 示例图片

上面这张图片不同文件格式时的大小对比结果如下:

◇JPG, 60 quality - 32K

◇PNG-8, 256 colors - 37K

◇GIF, 256 colors - 42K

◇PNG-24 - 146K

由于8位png和gif格式相对而言图像质量不错,文件大小更小,因此在网页中建议多使用这两种格式。

PHP性能技巧

51CTO编辑注:对于以下这部分内容,PHP小组已发布公开信反驳,其中的有些建议究竟是否正确还有待进一步研究。

由于PHP的使用量非常大,所以有必要放在这里说一说。首先有一点需要阐明的是,网站的性能与PHP版本,Web服务器环境和代码的复杂度都是有关的。

◆升级PHP版本

首先要做的是升级PHP版本,如果你还在使用PHP3或PHP4,那赶快升级吧!

◆使用缓存

使用缓存模式,如memcached,可以将数据库查询结果或网页缓存起来,加快Web服务器的响应速度。

◆使用缓冲输出

PHP使用内存缓冲存储你的脚步尝试打印的所有数据,这个缓冲看起来好像让你的网页变慢,因为用户必须等待缓冲被填满,幸运的是,你可以做一些修改强制让PHP快速清空输出缓冲。

◆没有原因尽量不要复制变量

有些PHP新手试图通过拷贝预先定义的变量使代码变得清晰,但事实是这样会导致双倍的内存消耗。我们来看一个例子,假设有个不怀好意的用户向文本区域插入了512KB的内容,那么会使用1MB内存:

  1. $description = strip_tags($_POST['description']); 
  2. echo $description; 

上面这个例子就是没有原因的拷贝变量,其实可以简化它,避免额外的内存消耗:

  1. echo strip_tags($_POST['description']); 

◆避免在循环中执行SQL查询

一个常犯的错误是在循环中使用SQL查询,这样会引起多次数据库往返,降低脚本速度,在下面的例子中,你可以修改循环构建一个SQL查询,一次性插入所有的用户:

  1. foreach ($userList as $user) { 
  2.   $query = 'INSERT INTO users (first_name,last_name) VALUES("' . $user['first_name'] . '", "' . $user['last_name'] . '")'; 
  3.   mysql_query($query); 
  4. }

改为:

  1. INSERT INTO users (first_name,last_name) VALUES("John", "Doe") 

不用循环,你可以合并成一次数据库查询:

  1. $userData = []; 
  2. foreach ($userList as $user) { 
  3.   $userData[] = '("' . $user['first_name'] . '", "' . $user['last_name'] . '")'; 
  4. $query = 'INSERT INTO users (first_name,last_name) VALUES' . implode(',', $iserData); 
  5. mysql_query($query);Produces: 
  6. INSERT INTO users (first_name,last_name) VALUES("John", "Doe"),("Jane", "Doe")...

◆为长字符串使用单引号

PHP允许对字符串变量封装同时使用单引号和双引号,但是是有区别的,使用双引号时,告诉PHP引擎读取字符串内容并查找变量,然后使用它们的值替换它们,对于一个很长的但没有包含变量的字符串,将导致性能下降。

◆使用switch/case替代if/else

使用switch/case比使用if/else性能更好,易读性和易维护性也更佳。

 

预取资源

预取资源就是当用户还没有请求该资源就预先下载到本地,那当用户真正请求时,可以大大减小网络延迟时间,甚至消除网络延迟时间。

对于交互式网站,优化速度不仅仅是减小初始下载大小,当用户交互时可以下载额外的资源,这些行为的速度依赖于下载这些资源的时间,将下载资源变小可以加快网站速度。在设计预取资源时要考虑以下几个问题:

◆研究用户行为

如果你的用户在某个特定的页面或行为上花费了大量的时间,那这就是你该优化的,你可以通过查找服务器日志来找出这些页面和行为。对于picasa网络相册,我们知道最多的操作是一张张浏览图片,那么这就是非常合适的预取对象。

◆衡量网页准好的时间

在当前页面没有显示完时不应该提取新页面的数据,对于动态页面,你需要为页面上每个外部文件添加JavaScript载入处理程序,当这些资源全部下载下来后,为用户下一个行为预取数据就非常合理了。

◆使用工具测试

在正式修改代码前,先用Page Speed这类工具进行测量,要避免一次提取太多数据造成网站变慢。

适当的包含样式和脚本

网页载入时间一般80-90%的时间都花在等待网络上了,最有力的方法是消除序列化下载资源。

联合外部JavaScript文件

下载外部脚本文件时一般浏览器会等它下载完了再继续后面的下载,这与并行下载图片的行为是完全相反的。

例1:两个外部脚本文件

例2:两个外部图片

在例2中,两个需要1秒的图片合在一起时,总共也只需要1秒就完成下载了,在例1中,两个需要1秒的脚步合在一起时,却需要2秒才能完成下载,这是两个不同的并行和串行下载。因此,如果你有多个外部脚本文件,最好将它们合并到一个文件中。

在外部JavaScript文件前包含额外的css文件

由于脚本文件会阻止后面的下载,那些已经在下载中的将会继续下载。

例3:在css之前的脚本

例4:在脚本前的css

在例3中,脚本在css文件前会导致浏览器堵塞,要花2秒时间,但只需要将css移到脚本前面就可以让下载过程并行执行,如例4。因此,如果你既有外部css文件,又有外部脚本文件,那么记住一定要将css文件放到脚本文件的前面。

在外部css和其它资源之间不包含内联JavaScript

使用内联脚本标签时,即使它没有下载任何东西,也会阻碍后面的下载。

例5:css后的内联脚本

例6:css前的内联脚本

例5中内联脚本块位于样式和脚本之间,下载时是串行的,在例6中,移到样式前面后,再一次恢复到并行下载模式。因此,如果你有一个外部css文件,不要在你的css文件和下一个下载的资源之间插入内联脚本标签。

 

减小HTML文档的文件大小

想要减少网站的载入时间最佳的办法就是减小HTML文件的大小,有很多种办法可以实施,首先我们看看html哪些标签是可以瘦身的。

◆HTML 4

根据HTML 4 DTD标准,可以有很多标记都省略掉,下面以斜体表示的标记都可省略掉:

< /area>

< /base>

 

< body>

 

< /body>

< /br>

< /col>

 

< /colgroup>

< /dd>

< /dt>

< head>

< /head>

< /hr>

 

< html>

 

< /html>

< /img>

< /input>

 

< /li>

 

< /link>

< /meta>

 

< /option>

 

< /p>

< /param>

 

< tbody>

 

< /tbody>

< /td>

< /tfoot>

< /th>

< /thead>

< /tr>

例如,如果你有标记为< li>List item< /li>的列表项目,那么你可以将其省略为< li>List item,在表示段落结束时一般使用< /p>标记,但这下也可以使用< p>My paragraph了,这种省略语法对html、body、head都有效,在html中它们不是必须的。这样一通省略后,往往可以使整个html文件大小减小5-20%。

◆HTML 5

虽然HTML 5仍然在开发阶段,它向你提供了更多减小文件大小的选项。例如。HTML 5允许你象下面这样设置文档类型:

  1. < !DOCTYPE html> 

相对

  1. < !DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> 

你会注意到HTML 5 DTD更短了。当你指定文档的编码时,HTML 5更易于使用,并且更轻巧了,< meta charset="utf-8">和< meta http-equiv="content-type" content="text/html; charset=utf-8">表示的含义是一样的,浏览器一看也是知道要处理HTML。

在HTML 5中另一个值得一提的是可以省略掉与MIME类型关联的类型属性了,这些属性如type="text/css"或type="text/javascript"描述了内容的类型,现在可以使用< script>代替< script type="text/javascript">,使用< style>代替< style type="text/css">。

如果你将上面这些方法集合起来使用,将会节约10-20%的文件大小。请浏览Google的隐私页面,它就全部采用了这些方法。

UI消息和感觉等待时间

对于很多用户而言,速度并不等于性能,用户对一个网站的速度感觉会受到他们的总体体验影响,包括他们是否能够有效地获取到想要的内容,以及系统是如何人响应的。

在设计你的网站时,要考虑用户访问该站点的目的是什么,越快(越容易)完成访问网站越好,如果用户在获得内容时遇到了很多困难,他们在完成任务后肯定会马上离开你的站点,并可能再也不愿意回来。

其实要让用户觉得网站使用起来感觉很舒服,你有很多事情要做,这里只讨论3个主题。

1. 网站设计是否足够简单,即使是第一次使用该网站的用户也能够顺利地找到他们想要的内容。要达到开箱即用的效果。你可以花10天时间来考虑界面流程如何优化,这样对于10万用户而言,节省的时间和价值都大多了。

2. 某个消息会打断或增加用户工作步骤吗?

3. 用户在等待期间该做点什么?例如显示进度条,注意不要把进度条本身设计得过于庞大,一般建议使用“正在上传......”,“载入中.......”等等这样的纯文本就可以了。

小结

本文列举了12类加速网站的办法,在实际工作,可以视具体情况灵活运用,当你完全掌握了这些方法后,也许会触类旁通,引起你深入地追寻更深层次的加速办法。

posted @ 2010-03-30 15:20  gybsoft  阅读(550)  评论(0编辑  收藏  举报