zoukankan      html  css  js  c++  java
  • 高性能JavaScript(DOM编程)

    首先什么是DOM?为什么慢?

    DOM:文档对象模型,是一个独立于语言的,用于操作XML和HTML文档的程序接口(API)

    用脚本进行DOM操作的代价很昂贵。那么,怎样才能提高程序的效率?

    1、DOM访问与修改

    访问DOM元素是有代价的,修改元素代价更是昂贵,因为它会导致浏览器重新计算页面的几何变化(重排和重绘)。

    尤其是在循环中访问或者修改元素,看下面两段代码:

    var times = 15000;
    console.time(1);
    for(var i = 0; i < times; i++) {
      document.getElementById('myDiv1').innerHTML += 'a';
    }
    console.timeEnd(1); // 2846.700ms

    这段代码的问题在于,每次循环迭代,该元素就会被访问两次,一次读取,一次重写。

    console.time(2);
    var str = '';
    for(var i = 0; i < times; i++) {
      str += 'a';
    }
    document.getElementById('myDiv2').innerHTML = str;
    console.timeEnd(2); // 1.046ms

    这种方法明显效率更高,循环结束后一次性写入。

     

    1.1、HTML集合

    HTML是包含了DOM节点引用的类数组对象。

    Document.getElementsByTagName(); document.links  获取的都是一个集合。是个类似数组的列表,但不是真正的数组(因为没有push或slice之类的方法),但提供了一个length的属性。可以通过下标访问元素。

    高性能JavaScript指出在相同内容和数量下,遍历一个数组的速度明显快于遍历一个HTML集合。

    例子

    console.time(0);
    var lis0 = document.getElementsByTagName('li');
    var str0 = '';
    for(var i = 0; i < lis0.length; i++) {
      str0 += lis0[i].innerHTML;
    }
    console.timeEnd(0); // 0.974ms
    
    console.time(1);
    var lis1 = document.getElementsByTagName('li');
    var str1 = '';
    for(var i = 0, len = lis1.length; i < len; i++) {
      str1 += lis1[i].innerHTML;
    }
    console.timeEnd(1); // 0.664ms

    注意:因为额外的步骤带来消耗,而且会多遍历一次集合,因此需结合实际情况下使用数组拷贝是否有帮助。

     

    1.2、选择器API

    如果是处理大量组合查询,使用querySelectorAll的话会更效率。

    var elements = document.querySelectorAll('#menu a');
    var elementss = document.querySelectorAll('div.warning, div.notice');

     

    2、重绘和重排

    DOM的变化影响了元素的几何属性(宽或高),浏览器需要重新计算元素的几何属性,同样其他元素的几何属性和位置也会因此受到影响。浏览器会使渲染树中受到影响的部分失效,并重新构造渲染树。这个过程称为重排。

    完成重排后,浏览器会重新绘制受影响的部分到屏幕,该过程称为重绘。

     

    2.1、重排何时发生

    每次重排,必然会导致重绘,那么,重排会在哪些情况下发生?

    1.添加或者删除可见的DOM元素

    2.元素位置改变

    3.元素尺寸的改变(padding、margin、border、height、width)

    4.内容改变(文本改变或图片尺寸改变)

    5.页面渲染初始化(这个无法避免)

    6.浏览器窗口尺寸改变

    不间断地改变浏览器窗口大小,导致UI反应迟钝(某些低版本IE下甚至直接挂掉),正是一次次的重排重绘导致的!

    改变样式

    思考下面代码:

    var ele = document.getElementById('myDiv');
    ele.style.borderLeft = '1px';
    ele.style.borderRight = '2px';
    ele.style.padding = '5px';

    示例中,元素的三个样式被改变,而且每一个都会影响元素的几何结构。在最糟糕的情况下,这段代码会触发三次重排(大部分现代浏览器为此做了优化,只会触发一次重排)。

    优化

    var el = document.getElementById('mydiv');
    
    // method_1:使用cssText属性:
    el.style.cssText = 'border-left: 1px; border-right: 2px; padding: 5px'; 
    
    // method_2:修改类名:
    el.className = 'anotherClass';

    2.2、批量修改DOM

    看如下代码,考虑一个问题:

    <ul id='fruit'>
      <li> apple </li>
      <li> orange </li>
    </ul>

    如果代码中要添加内容为peach、watermelon两个选项,你会怎么做?

    var lis = document.getElementById('fruit');
    var li = document.createElement('li');
    li.innerHTML = 'peach';
    lis.appendChild(li);
    
    var li = document.createElement('li');
    li.innerHTML = 'watermelon';
    lis.appendChild(li);

    很容易想到如上代码,但是很显然,重排了两次,怎么破?这时,fragment元素就有了用武之地了。

    var fragment = document.createDocumentFragment();
    
    var li = document.createElement('li');
    li.innerHTML = 'peach';
    fragment.appendChild(li);
    
    var li = document.createElement('li');
    li.innerHTML = 'watermelon';
    fragment.appendChild(li);
    
    document.getElementById('fruit').appendChild(fragment);

    createdocumentfragment()方法创建了一虚拟的节点对象,节点对象包含所有属性和方法。

    它的设计初衷就是为了完成这类任务——更新和移动节点。

     

    3、事件委托(Event Delegation)

    当页面中有大量的元素,并且这些元素都需要绑定事件处理器。每绑定一个事件处理器都是有代价的,要么加重了页面负担,要么增加了运行期的执行时间。再者,事件绑定会占用处理时间,而且浏览器需要跟踪每个事件处理器,这也会占用更多的内存。还有一种情况就是,当这些工作结束时,这些事件处理器中的绝大多数都是不再需要的(并不是100%的按钮或链接都会被用户点击),因此有很多工作是没有必要的。

    使用事件委托,只需要给外层元素绑定一个处理器,就可以处理在其子元素上触发的所有事件。

    有以下几点需要注意:

    1.访问事件对象,判断事件源

    2.按需取消文档树中的冒泡

    3.阻止默认动作

     

    小结

    访问DOM是现代WEB应用的重要部分,但每次穿越连接DOM和ECMAScript之间都会消耗性能

    1.最小化DOM访问次数,尽可能在JavaScript端处理

    2.如果需要多次访问某个DOM节点,可以使用局部变量储存它的引用。

    3.如果要操作一个HTML元素集合,建议把它拷贝到一个数组中

    4.如果可能的话,使用速度更快的API 比如 querySelectorAll 和 firstElementChild 

    5.使用事件委托来减少事件处理器的数量

     

     

  • 相关阅读:
    设计模式之访问者模式
    设计模式之命令模式
    设计模式之迭代器模式
    tomcat8.0.11性能优化
    java 基础 --集合--012
    StringBuffer和StringBuilder的区别
    jquery 入门
    java 基础 --匿名内部类-008
    java 基础 --多态--009
    java 基础--继承--007
  • 原文地址:https://www.cnblogs.com/wyhlightstar/p/10221428.html
Copyright © 2011-2022 走看看