网站性能,javascript性能相关知识点

时间:2021-06-20 18:46:12

一、高性能网站

《高性能网站建设指南》一书中提出用户只有10%-20%最终用户响应时间是花在从web服务器获取html文档并传送到浏览器中,80%的时间都花在了等待页面组件中,由此提出了构建高性能网站的14个规则。按照优先级排序依次是:

1.减少http请求数

直接方法是减少组件的个数,由此来减少http请求数量,可以采用的方法包括图片地图,css sprites(利用css的background-position属性,可以讲html元素放置到背景图片中期望的位置上),样式表的合并等等。

2.使用内容发布网络CDN

CDN用于发布静态内容,包括图片,脚本,样式表,和flash。

3.添加expires头

条件GET请求和304响应有助于让页面加载得更快,然而仍然需要在客户端和服务器进行一次往返确认。expries头部通过明确指出浏览器是否可以使用组件的缓存副本来消除这个需要。通过服务器会返回一个expires头。expires:thu 15.。。当浏览器看到响应有一个expries头时,它会和响应的组件一起保存在缓存中,只要组件没有过期,就不会发生任何http请求。

长久的expries头部应该包含任何不经常变化的组件,包括脚本,样式表和flash组件。这通常是在服务器的配置文件中配置。与之类似的另一个http头部是cache-control:max-age.如果二者同时出现,http规范规定max-age指令将重写expire头。

如果确保用户能获取最新的组件,最有效的解决方案是修改器所有链接。因此为所有组件的命名最好采用变量。

4.压缩组件

客户端通过appect-encoding:gzip,deflate来表示对压缩的支持,html,文档,样式表通常是值得压缩的。图片和pdf不应该压缩,因为它们本来就已经压缩了,试图再次压缩不仅浪费CPU资源,还可能会增加文件大小。

服务器端在apache的配置文件中进行配置。

5.将样式表放在顶部

将样式表放在底部会导致在浏览器中阻止内容逐步呈现,出现白屏或者FOUC(无样式内容闪烁,即内容呈现后,待样式表加载完成再次重绘)。

6.将脚本放在底部或者采用无阻塞脚本

一般,http规范建议每个主机名可以并行下载两个组件(注意,仅仅是建议)。但是在下载脚本时,并行下载是被禁用的,页面的下载和与渲染都必须停下来等待脚本下载和执行完成。这一方面是为了保证脚本的正确执行顺序,另一方面是为了防止document.write修改页面。因此最好将样式表放在底部。

此外,可以采用无阻塞脚本(在页面加载完成之后才加载javascript代码,意味着在DOMContentLoaded事件,或者window的load事件触发后再下载脚本,此时脚本可以放在页面任何位置)的方法有:

1)延迟的脚本

为script标签添加defer或者async属性,用于异步加载脚本。二者的相同点是采用并行加载,在下载过程中不会产生阻塞。不同点在于执行时机,async是加载完成后自动执行,而defer需要等到页面加载完成才执行。

2)动态脚本元素

var script=document.createElement('script');
script.type='type/javascript';
script.src='file1.js';
document.getElementsByTagName('head')[].append('script');

动态脚本可用来实现jsonP。此外,也可以实现脚本的无阻塞加载。文件在元素被添加到页面时开始下载。无论何时启动下载,文件的下载和执行均不会阻塞页面其他进程。使用动态脚本节点下载文件时,返回的代码通常会立即执行。如果加载多个动态脚本,只有firefox和opera会保证脚本按照指定的顺序执行,其他浏览器则会按照服务器返回的顺序下载和执行。

动态脚本加载凭借其跨浏览器的优势,成为最通用的无阻塞加载的解决方案。使用这种方法需要两步骤:先添加动态加载所需要的代码,然后加载初始化页面所需要的其他脚本。下面是一个通过的无阻塞加载的方法:

function loadScript(url,callback){
var script=document.createElement('script');
script.type='type/javascript';
script.src=url;
if(script.readyState){//IE
script.onreadystatechange=function(){
if(script.readyState==='loaded'||script.readyState==='complete'){
script.readyState=null;
callback();
}
}
}else{
script.onload=function(){
callback();
}
}
document.getElementsByTagName('head')[].appendChild(script);
}

7.避免CSS表达式

这个一般都不会这么做了,因此不展开详细说明了。

8.使用外部javascript和css

使用外部文件,可缓存,可重用。

9,减少DNS查找,

通过keep-alive和较少的域名来减少DNS查找。浏览器有DNS的缓存,操作系统也有DNS的缓存,减少唯一主机名会减少DNS查找次数,但同时也减少了页面中并行下载的数量,因此建议将组件分别放到至少2个,至多4个主机名下,达到二者的权衡。

10.精简javscript

方法有两种:压缩(去除空白等),混淆(使用更短的变量、函数名)

11.避免重定向

http状态码如301,302代表重定向,304不是真正的重定向,只是响应条件GET请求,避免下载已经存在在缓存中的数据。一种重定向最为浪费,发生在URL结尾必须出现/却没有出现的时候(允许自动索引到index.html的情况下),注意主机名后面缺少斜线并不会引起重定向。如www.baidu.com

重定向延迟了整个html文档的传输,在html文档到达之前,页面不会呈现任何东西,也没有任何组件被下载。

12.删除重复脚本

引入脚本模块化管理,防止同一个脚本多次引入。

13.配置ETag

如果缓存的组件到期了(或者用户refresh或者reload了页面),浏览器会发送一个条件GET请求,如果浏览器检测到缓存的组件是有效的,原始服务器不会返回整个组件,而是返回一个304码。

服务器检测缓存的组件是否和原始服务器的组件匹配时有两种方式:

1)比较最新修改日期

条件get请求的请求头包含IF-MODIFED-SINCE头部,响应头包含LAST-MODIFED头部,通过比较来判断

2)实体标签ETAG

ETAG提供了另外一种方式,用于检测浏览器缓存中的组件与原始服务器的组件是否匹配(实体是组件的另一种称呼)。这种方式的http 条件GET请求会返回一个ETAG头部。

ETAG提供了另外一种方式,ETAG通常基于实体的某些属性来构造,实体的状态可以反映在ETAG中,对于特定的,寄宿了网站的服务器来说是唯一的。Apache中ETAG的格式是innode-size-timestamp,文件系统采用innode来存储诸如文件类型,所有者等信息。因此ETAG的问题是,当从一台服务器获取了原始组件,当向另一台服务器发送条件GET请求时,ETAG是不会匹配的,这对于集群服务器来说很常见。因此最好服务器移除或者配置默认的ETAG。

14.使ajax请求结果可缓存

二、高性能javascript

回到javascript自身的性能问题,很赞同《你不知道的javascript(中卷)》中的一句话,不要沉迷于微性能的调优,比如a++和++a。应该关注优化的大局,即代码是否运行在关键路径上。所谓关键路径,比如用户会注意到的UX关键位置,如动画循环或css风格更新,或者一段会运行多次的“热”代码等。

1.数据访问

访问字面量和局部变量速度最快,访问数组元素和对象成员相对较慢。局部变量存在作用域链其实位置,因此最快,全局变量最慢。因此可以把常用的对象成员,数组元素,跨域变量保存在局部变量中来该删javascript性能。

2.DOM编程

  2.1DOM模型

文档对象模型(DOM)是一个独立于语言的,用于操作XML和HTML文档的程序接口(API)。尽管DOM是个和语言无关的API,但是它在浏览器中的接口是javascrcipt实现的。浏览器通常会把DOM和javascript(这里指ECMAscript)独立实现,比如chrome使用Webkit来渲染页面,但javascript引擎则是他们自己开发的,命名为V8.因此DOM编程中一个通用的做法是,尽量减少DOM访问次数,把运算尽量留在ECMAscript这一端。

  2.2HTML集合

html集合是包含了DOM节点引用的类数组对象,以下方法返回的就是html集合:

document.getElementsByTagName
document.getElementsByClassName
document.getElementsByName

此外,以下属性也同样返回html集合:

document.images
document.links
document.forms
document.forms[].elements

html集合是类数组的列表,它们并不是真的数组,不具有数组的push,slice等方法,但是具有length属性,也可以使用数字下标访问元素。

根据DOM标准的定义,html集合一直和文档保持联系,文档更新时,他们也会自动更新,这意味着,每次需要最新的信息时,都会重复执行查询的过程,哪怕只是获取集合中的元素个数(即length属性),也是如此,这就造成了低效之源。例如,下面的代码会陷入死循环:

var allDivs=document.getElementsByTagName('div');
for(var i=0;i<allDivs.length;i++){
document.body.appendChild(document.createElement('div'));
}

此时建议将集合的长度缓存到一个变量中,如果经常操作集合,可以将它拷贝到一个数组中。

  2.3重绘与重排

javascript在下载完所有组件(html,js,css,图片)后会生成两个内部的数据结构:DOM树(表示页面结构),渲染树(表示DOM节点如何展示)。

当DOM变化影响了元素的几何属性时(宽和高)时,浏览器会使得渲染树中受到影响的部分失效,就会发生重排。重排之后,会将受影响的部分重新绘制到屏幕中,称为重绘。如果没有影响几何属性,如仅仅改变了背景色,那么只会发生重绘。

重排和重绘代价可能非常昂贵,因此要尽量减少其次数。当需要对DOM元素进行一系列操作时,可采用以下步骤:

1).使元素脱离文档流。

脱离文档的方法有以下几种:

  A. 隐藏元素,应用修改,重新显示;

  B.使用文档片段,在当前DOM之外构建一棵子树,再把它拷贝到文档中。

  C.使用绝对定位,比如对动画元素使用绝对定位,当动画结束时恢复定位。  

2).对其应用多重改变。

3).将元素带回文档。

3.快速响应的用户界面

用于执行javascript和更新用户界面的进程通常被称为“浏览器UI线程”。

一般浏览器会限制javascript任务的运行时间,有的浏览器采用调用栈大小限制(比如500万条语句),有的采用长时间限制(比如100ms)。当遭遇这种情况时,考虑的解决方法有两种:

1)分时函数和延迟函数

参见之前博客:http://www.cnblogs.com/bobodeboke/p/5594647.html

2)web workers,这是js新引入的特性,每个work在一个独立线程中执行,不会造成阻塞。但是web workers直接访问DOM会出现错误,因此适用于解决数据计算,解析等费时的操作。