javascript代码性能优化

在这篇文章叙述的关于Javascript代码优化,其实并没有什么新意,关于这方面的优化,一直都是Nicholas C. Zakas的专利。我在这里将要说的是对当前众多关于Javascript代码优化的方式一个小总结,并尝试去归类各种不同的优化方式,方便于记忆众说纷纭的优化技巧。本人归类的结果如下图所示:

Javascript代码优化无非主要围绕:DOM操作、循环、闭包、对象重复出现、对象的声明方式、作用域链、字符串操作、类的声明方式等等。循环、闭包、对象重复出现是从作用域链的角度去优化的;DOM操作主要围绕HTMLCollection、NodeList等来优化;对象的声明方式主要是对象(Object)、数组(Array)、字符串(String)、函数(Function)、正则(RegExp)等内置的对象使用字面量的方式来声明,这个比使用new来实例化相应的对象在性能上要强很多;字符串操作的优化方式主要是通过数组的push和join方法;类的声明方式优化方式主要是分清属性和方法的声明的方式,方法使用prototype的方式来声明;Javascript语言本身的流程操作语句的优化(if、switch、with、eval等等)。

因此,我将Javascript代码优化主要分为六类:DOM“真空”空间缩短作用域链字面量声明方式字符串操作类声明方式流程操作语句。下面将逐个叙述:

(全文…)

@font-face的性能测试

对@font-face的作用,也是今晚在看这篇博文《@font-face and performance》的时候,才知道使用它可以自定义显示字体。我想在浏览器中加入@font-face的功能是处于一定的情况的,比如使用特殊的字体来显示页面某部分的字体,从而避免采用图片的方式来显示特定字体。出发点是很好的,但是……

解析@font-face的机制在不同的浏览器下是不同的。

对于@font-face更多的叙述,在上面提到的那篇博文中有了详细的描述。但是对于它提出的说“IE doesn’t render anything in the page until the font file is done downloading if there is a SCRIPT tag above the @font-face declaration.”,说的是当在声明@font-face的前面如果存在script标签的时候,IE下不会渲染任何东西,页面一片空白,直到@font-face指定的字体加载完为止,这点经过测试,确实如此。

在声明@font-face的标签之前没有script标签的时候IE下并没有出现阻塞页面渲染的情况,但是如果前面有script标签的时候,不管是否是紧跟这script标签,只要在@font-face标签前面有script标签,就阻塞页面渲染:《没有script标签》,《紧跟script标签》,《不紧跟script标签》。

同时,我测试了@font-face是否会阻塞页面其他资源加载,测试发现并没有这个问题:《是否阻塞资源加载的测试》,而且在字体加载下来之前,忙指示器会一直处于等待状态。下面是各种浏览器对@font-face的测试比较结果,结果分为三类:IE,Firefox、Opera,Safari、Chrome。

  1. 在Firefox、Opera中,并不会阻塞页面其他资源的加载,也不会影响页面渲染,而且在字体加载下来之前,对想应用该字体的内容会使用默认的字体显示,直到字体加载下来之后再渲染为指定的字体。因此,这两个浏览器测试效果最佳。
  2. 在Safari、Chrome中,并不会阻塞页面其他资源加载,也不会影响页面渲染,但是在字体加载下来之前,对应用该字体的内容将会首先空白显示,但是内容尺寸是存在的,直到字体加载完成,才渲染字体样式。
  3. 在IE下就分不同的情况了,如上所述,前面是否有script标签的情况。

对于@font-face的杯具的性能问题,如果没有必要,就尽量避免使用。如果一定要使用的话,可以使用使用lazy load的方式来加载,例如博文中给出的例子:

function lazyload() {
  var sRule1 =
	"@font-face {" +
	"  font-family: 'Yanone';" +
	"  src: url('/bin/resource.cgi?type=font&sleep=6');" +
	"  src: local('Yanone'), " +
        "url('/bin/resource.cgi?type=font&sleep=6') " +
        "format('truetype');" +
        "}";

  var style1 = document.styleSheets[0];
  if ( "function" === typeof(style1.insertRule) ) {
        // Firefox, Safari, Chrome
	style1.insertRule(sRule1, 0);
  }else if ( "string" === typeof(style1.cssText) ) {
	// IE
	style1.cssText = sRule1;
  }
}

将上面的lazyload函数添加到页面的onload事件中去执行,这样就保证了页面的正常渲染并且实现特定字体样式的功能:《lazy load测试》。这个主要是解决IE下@font-face前面有script标签的问题,在其他浏览器下显示的时候,也都还是会在应用该字体的内容中产生空白闪烁、或者是默认字体和指定字体之间的切换闪烁。也都还是在一定程度上影响了用户体验,感觉有些不爽。“Tradeoff is anywhere!!

上面的三个测试都是使用内联CSS的方式,考虑到使用外联CSS样式表的情况,又对此做了一番测试,发现了不同的状况:

  1. 当link放在head时,IE6、8(没有IE7,测试不了)都是死心眼,偏执要阻塞整个页面的加载,在字体下载下来之前整个页面一片空白,其他浏览器跟内联CSS一样的显示:《外联CSS测试(在head内)》。
  2. 当link放到body中时,IE8还是阻塞整个页面的渲染,可是IE6就不同了,IE6会阻塞整个页面样式的渲染,但是不会阻塞link前面的内容的显示,而是阻塞了其后面的内容的显示了,直到字体加载下来之后才开始渲染页面样式和显示link后面的内容。其他浏览器跟内联CSS一样的显示:
    外联CSS测试(在body最底部)》,《外联CSS测试(在body中部)
  3. 当link前面带有script标签的时候,因为外联在head头部的时候在IE各版本都阻塞了,所以这个条件link在页面的任何位置都无所谓,主要测试link前面带有script标签,那就把link放在body中来测试。这下IE6也杯具了,阻塞了整个页面的渲染,IE8照常阻止整个页面渲染,其他浏览器同上。《外联CSS测试(在body中部),前跟script标签》,将link放到body最底部也一样阻塞整个页面的渲染:《外联CSS测试(在body最底部),前跟script标签》,其他浏览器同上。

对于页面中同时有script和link、style标签是很正常的事情,但是当样式中有@font-face的时候,情况就复杂了。更多的测试条件大家可以自行测试。有错误的地方,请不吝留言交流。

小译“Best Practices for Speeding Up Your Web Site”

昨天接触到这篇文章《Best Practices for Speeding Up Your Web Site》,觉得太经典了,自己不由自主的想要翻译过来,鉴于本人的英语水平,只翻译了其中的某些条目,有错误在所难免。更多的还是推荐浏览原文。

首先的说明的是:优化页面显示速度,就是不管HTML内容多与少,都要尽量在最快的速度显示出来,这就是Front-end engineers首要明确的意图。所以下面所提及的优化方式,都是从这一目的出发的。下面的翻译的一些条目:

1.最小化HTTP request请求。因为一个页面显示所花费的时间很多都是花费在scripts,stylesheets,images,flashs等等,这些都会通过http request来加载,这些大概占用了页面加载时间的70%-80%,因此,减少HTTP request能很大的优化页面的加载速度。

2.使用第三方的代理服务器来加载scripts,stylesheets等等,这能降低本主服务器的负载,比如google ajax api提供的目前流行的js库:jquery,extjs,prototype,MooTools等等

3.在头部声明cache的expire。在用户第一次view页面的时候会加载全部的内容,包括html以及scripts,stylesheets,images等等,设置了cache的expire一方面能使得用户在下次view页面的时候能从缓存里读取一些数据,减少了HTTP Request的请求数,加快页面的显示。不过这要看用户view页面的回头率是否频繁。设置了cache的expire也要适当。一方面能加快加载速度,另一方面也有助于在cache的expire过期的时候使得用户获得页面的最新数据信息。

4.使用压缩技术(Gzip Components)。原理就是将scripts,stylesheet,images,flash等等压缩为一个zip文件,之后把这个zip文件链接进页面中来减少http request回调来加快页面的加载速度。通过摄自豪HTTP header的Accept-Encoding为gzip, deflate,之后在Response header里设置Content-Encoding: gzip来解压压缩包的文件,就可以将这些文件导入到页面的相应的部分。不过,如果页面中的Component比较少或者也是相对于非常小的话,还是不建议使用这种方法,还是使用普通的通过各自的连入方式加载Component。

5.将全部的Stylesheets放到head里来加载。Yahoo测试了这种方式证明了这一方式确实能加快页面的加载速度,“This is because putting stylesheets in the HEAD allows the page to render progressively.”。

6.将Scripts放置到Bottom(底部)。我们都知道,通过script标签引入文件会阻止下面的HTML内容的加载,等待script加载文件完成才加载下面的内容。因此:HTTP/1.1 specification建议在每一个主机名下不要超过两个Component同时下载。还有一点要说明的是:当script正在加载的时候,浏览器机会停止一切其他的加载项,甚至不同的主机下的文件都不会加载(通过iframe加载的内容)。
但是有时候有很难将scripts都放到bottom里,这里的主要问题就是通过document.write来添加页面内容以及作用域问题。因此,就需要使用其他的方式来解决这样的问题了。
这里有一种建议方案:就是使用deferred scripts(延迟执行scripts),通过使用script标签的defer属性来延迟执行不包含document.write的js代码。defer的机制就是先加载好scripts,之后在DOM加载完成之后,执行js代码(defer的延迟要比window.onload早),因此使用defer会在DOM加载完成,window.onload之前执行js代码。但是defer属性目前只有IE支持,Firefox等浏览器不支持,要通过检查DOM是否加载完成这样的方式来解决。因此,如果一个script可以defer延迟的话,同样也就可以把它放置到bottom里,这样就能加快页面的加载(显示)速度。

7.避免使用css的Expressions。CSS的expression虽然很强大,能动态修改css的样式规则,只有IE能识别expression属性。
但是,致命的问题在于:它执行的非常的频繁,页面的一个render,resize和scroll,以及用户的一些简单的鼠标操作,比如:移动鼠标等等都会触发expression的重复执行。一个测试表明:在页面内移动鼠标就非常容易的产生超过10000次的执行次数。这将是难以想象的耗费性能。
一种解决办法就是一次执行expression之后,就给css 样式指定确切的值,而不在需要依赖expression。

8.通过外联的方式来加载外部的Javascript和css到页面里。在第一条里我们谈到减少Http request请求来加载Component,而这里又提倡使用外联的方式来加载scripts、Stylesheet等等Component,不是冲突了吗?这里来解释一下:如果不是通过外联的方式来加载文件,这样在每次加载页面的时候,都需要去加载内联的Javascript和css内容,这就使得HTML容量变大了。而使用外联的方式的好处在于浏览器会缓存这些文件,第二次加载页面的时候就会在缓存里读取这些文件(如果缓存里还没有清除的话),这使得HTML页面“变小”了,不是更快加载HTML了吗。从这一角度来考虑,视具体情况而定,来综合考虑上面所提到的几种优化方案。

9.压缩Javascript和css代码。压缩代码就是清除掉一些不需要的characters,减小代码文件的大小,从而加快加载。可以通过使用JSMin或者YUI Compressor,还有Jspacker等等压缩工具。压缩代码的范围可以很广泛,包括独立的js或者css文件,当然也可以是内联的js或者css代码(google就使用了这种方式),也可以是上面所说的通过压缩Components联入页面的各个js或者css文件。

10.删除重复的Scripts Component。有时候如果不加注意,可能就会重复加载了同一个script联入文件,比如通过script联入,之后在某种形式下使用php的insertScript又联入了同一个文件,这都是有可能的。这将会有哪些不合理呢:一个是花费Http request的数目而且还会影响浏览器的缓存方面的问题,使得script重复加载。

(全文…)