浏览器中DOM操作的性能优化【+待补充】

优化手段:

一、给JS文件减肥。

有的人为了给网站增加炫目效果,往往会使用一些JS效果代码,这在上个世纪似乎还很流行,对于现在来说,最好在用户体验确实需要的情况下,使用这些东西。至于希望给自己的JS文件减肥的童鞋,网上的工具里有很多,在百度一搜就会有应用,功能很全。把一个已经完善的JS文件进行压缩是主流网站的一个惯性动作,因为压缩量确实很可观。以下是几个比较好的压缩工具:

YUI压缩工具 (http://developer.yahoo.com/yui/compressor/)
Dean Edwards Packer (http://dean.edwards.name/packer/)
JSMin (http://crockford.com/JavaScript/jsmin)

二、尽量减少DOM访问

使用JavaScript访问DOM元素很容易,代码更容易阅读,但是速度很慢。下面介绍几个要点:限制使用JavaScript来修饰网页布局,把针对访问元素的引用缓存起来。有时,当你的网站依赖大量的DOM改动时,就应该考虑限制你的标记。这是改用HTML5、舍弃那些原来的XHTML和HTML4的一个充分理由。

三、使用适当的CDN

现在许多网页使用内容分发网络(CDN)。它可以改进你的缓存机制,因为每个人都可以使用它。它还能为你节省一些带宽。你很容易使用ping检测或使用Firebug调试那些服务器,以便搞清可以从哪些方面加快数据的速度。选择CDN时,要照顾到你网站那些访客的位置。记得尽可能使用公共存储库。

四、把不着急用的JS文件放到页面的底部

当更多地考虑用户对网站的速度体验时,在页面底部装入JS文件是一个非常好的做法。易用性和用户放在首位,JavaScript放在末位。对于追求技术的很多前端人员来说,这似乎很难接受,但也应该有所准备,有些用户会禁用JavaScript。

五、在头部以异步方式装入JS

为了统计网站的各种信息,我们通常会借助网上提供的免费统计功能,比如cnzz的统计,比如google分析,比如百度统计,关键的是,好多统计为了保证统计效果,可能会建议用户将统计代码放在页面的头部。如果用户选择这么做,可能会在统计代码请求数据不稳定时给他的网站用户带来非常不好的体验。不过,目前大部分统计服务都允许你以异步方式载入放在头部的JS文件,在很大程度上解决了这一问题。

六、把你的JavaScript打包成PNG文件

这个办法是最近在网上看到老外的一种做法,思维很特别,还没有尝试过,有兴趣的朋友可以尝试一下。具体是这样:把你的JS和CSS添加到图片的末尾,然后用CSS来裁切,通过一次HTTP请求来获得应用程序中所需的所有信息,它把你的JavaScript/css数据打包成PNG文件,然后你还可以拆包,只要使用画布API的getImageData()。这种方法效率非常高,可以在不缩小数据的情况下,多压缩35%左右。

七、JavaSript页面模版化

以前的方法

比如以下的代码——摘自新浪首页的一段js代码,这种方式也是大多数人使用的方式。

上面的代码,你看完有什么反应?你也许会把+=这种形式改成数组的push形式,有人说push比这种字符链接要快,但是也快不了多少。

如果这种嵌入的html代码很多,你也要这样处理吗?如果修改(添加或者删除)某个html,那么你就要不断的翻代码,找对应的标签,然后在找结束标签,你不觉得麻烦吗?也许有人会这样写(这个是我早期写的,也是我最惯于使用的方式)。

上面的代码,我维护起来也是非常费劲的,很多时候如果ui那边添加了个标签,或者修改了下html结构,那我就头疼了。

页面模板化——全新的方式
这是我最近看了facebook的js代码,偶然间发现以下的方式看起来不错,希望与大家分享。
看我的index.html代码

看我的js代码

今天偶然间发现这种方式更加合理,因为那个for循环看着太闹心了

维护上面的html代码我们是很容易的,下次无论html代码结构有什么变动,改起来都很方便。

后期修复
昨天我项目中需要条件筛选功能,所以我又做了一个 if语法解析的功能。
亮代码:

对应方法

----------------------------------------------------------------------------------------------------------------------

DOM优化意见:

频繁地对于DOM进行操作的很是损耗性能,但在富网页应用中我们编写脚本无可避免地要跟DOM打交道,到底怎么才能优化这个性能瓶颈呢,大致从以下三种情况去考虑:

  1. 访问和修改DOM元素
  2. 修改DOM样式,会造成页面的重绘和重新排版
  3. 通过DOM事件处理程序来响应用户

  访问和修改DOM元素

  在浏览器中,DOM的实现和Javascript的实现通常是保持相互独立的。下面了解一下主流浏览器的渲染引擎和JS引擎:

浏览器渲染引擎(内核)JS引擎
IEmshtml.dll(Trident)JScript
ChromeWebCore(WebKit)V8
FireFoxGeckoSpider-Monkey/TraceMonkey
Safari  WebCore(WebKit)  JavaScriptCore/SquirrelFish

  为什么访问DOM对性能有影响?那是因为两个独立的部分通过他们各自的接口来连接就会带来性能损耗。打个比喻,把DOM看成一个岛屿,把Javascript看成另外一个岛屿,两者之间以一座要收费的桥连接,每次Javascript访问DOM都需要过桥,交一次桥费。来回多了费用自然就高了。所以我们得想方设法减少过桥的次数。
  访问DOM元素的代价就是交一次“桥费”,修改DOM元素则会导致浏览器重新计算页面的几何变化。如果是循环修改DOM元素,其代价可想而知。如下代码:

function innerHTMLLoop1(){
  for(var count=0;count<10000;count++){
    document.getElementById("test").innerHTML +="增加内容";
  }
}

  在这段代码中,每循环一次都要对DOM元素访问两次:一次是读取innerHTML属性的内容,另一次是把新的内容写入它。所以一个优化的办法就是使用一个局部变量存储更新后的内容,在循环结束时再一次性写入:

复制代码
function innerHTMLLoop2(){
  var content ="";
  for(var count=0;count<10000;count++){
    content +="增加内容";
  }
  document.getElementById("test").innerHTML = content ;
}
复制代码

  很明显,innerHTMLLoop2交的“桥费”明显要少,因为它只访问了两次DOM元素,一次读入,一次写入。所以一般的法则是:尽量在自己的范围内(Javascript岛屿)活动,别经常到外面鬼混去。(据说,在广州,月薪不到5K的别交女朋友,你懂的!)。

  更新页面的两种方法性能比较:innerHTML和DOM方法(如document.creatElment)。《高性能Javascript编程》的答案是:性能差别不大,innerHTML好一些,使用简单嘛。另外还有一个更新页面的方法是节点克隆--element.cloneNode()。

  HTML集合的操作

  HTML集合是用于存放DOM节点引用的类数组对象。可通过下列的方法或属性得到这样的集合:

  1. document.getElementsByName()
  2. document.getElementsByTagName()
  3. document.getElementsByClassName()
  4. document.images 返回对文档中所有 Image 对象引用
  5. document.links 返回对文档中所有 Area 和 Link 对象引用
  6. document.forms 返回对文档中所有 Form 对象引用
  7. document.forms[0].elements 返回对文档中第一个表单的所有元素

  HTML集合会实时查询文档信息,也就是说当你要用到这个集合时,它会自动查询文档的最新信息。请看如下代码:

1 var allDivs = document.getElementsByTagName("div");
2 for(var i=0;i<allDivs.length;i++){
3     document.body.appendChild(document.createElement("div"));
4 }

  例如像上面的那段代码其实是个死循环,因为每一次访问div集合的length属性,它都会重新计算文档中的div元素数目。这就是html集合低效率的来源。要改进代码就用一个局部变量保存div集合的length属性: for(var i=0,len=allDivs.length; i<len; i++){...}

  访问HTML集合的length比数组的length要慢,所以要访问这种集合类的数目length,我们都应该先用一个局部变量去保存它:var len = 集合.length;

  另外,访问数组的元素要比访问HTML集合的元素要快。所以我们可以先把HTML集合转换成数组才去进行相应的操作:

复制代码
//HTML集合转换成数组
function toArray(coll){
  for(var a=[], i=0, len=coll.legnth; i<len; i++){
    a[i] = coll[i];
  }
  return a;
}
//使用
var coll = document.getElementsByTagName("div");
var divs = toArray(coll);
复制代码

  有人可能会问,这样多用了一个数组副本到底值不值得?这个倒是要看情况吧。不过另外一种选择,使用局部变量:

复制代码
function loopColletion(){
  var coll = document.getElmentsByTagName("div"),
    len = coll.length,
    el = null;
  for(var i = 0; i<len; i++){
    el = coll[i];
    //然后访问局部变量el
  }
}
复制代码

  许多浏览器提供了API函数返回元素节点,这些API都是原生的,所以可用的话就尽量用。下图列举了一些DOM的属性:

  

  上图列举的所有属性能被FF,safari,chrome,opera所支持,ie6-8只支持children。

  遍历children比childNodes更快,因为集合项少了。HTML源码中的空格实际上是文本节点,但他们不包含在children中。

  另外还有两个比较好的选择器API:document.querySelectorAll()和document.querySelector()。前者接收一个CSS选择器字符串参数并返回一个NodeList类数组对象而不是返回HTML集合,后者只返回符合查询条件的第一个节点。很遗憾IE6、7不支持这两个API。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值