zoukankan      html  css  js  c++  java
  • DOM的相关优化

    为什么要进行DOM优化?

      DOM对象本身也是一个js对象,所以严格来说,并不是操作这个对象慢,而是说操作了这个对象后,会触发一些浏览器行为,比如布局(layout)和绘制(paint)。

    首先先说一些浏览器是怎么样把一个页面呈现出来的:

      一个浏览器有许多模块,其中负责呈现页面的是渲染引擎模块。

      这个过程大致如下:

      ·解析HTML,并生成一棵DOM tree
      ·解析各种样式并结合DOM tree生成一棵Render tree(渲染树)
      ·对Render tree的各个节点计算布局信息,比如box的位置与尺寸
      ·根据Render tree并利用浏览器的UI层进行绘制

    注意:DOM tree和Render tree上的节点并非一一对应,比如一个"display:none"的节点就只会存在于DOM tree上,而不会出现在Render tree上,因为这个节点不需要被绘制。

    Webkit的基本流程:

    Gecko的基本流程:

      paint是一个耗时的过程,然而layout是一个更耗时的过程,我们无法确定layout一定是自上而下或是自下而上进行的,甚至一次layout会牵涉到整个文档布局的重新计算。
      关键来了:layout是肯定无法避免的,所以我们主要是要最小化layout的次数。

    怎么样进行优化?

      前面提到,layout是肯定无法避免的,所以我们主要是要最小化layout的次数。在考虑如何最小化layout次数之前,要先了解什么时候浏览器会进行layout。
      layout(reflow)一般被称为布局,这个操作是用来计算文档中元素的位置和大小,是渲染前重要的一步。在HTML第一次被加载的时候,会有一次layout之外,js脚本的执行和样式的改变同样会导致浏览器执行layout,这也是本文的主要要讨论的内容。
      一般情况下,浏览器的layout是lazy的,也就是说:在js脚本执行时,是不会去更新DOM的,任何对DOM的修改都会被暂存在一个队列中,在当前js的执行上下文完成执行后,会根据这个队列中的修改,进行一次layout。[浏览器的layout为lazy模式,也就是说,并非每次我们对DOM进行修改时都会layout,而是将这些修改存储在一个队列中,在一定的情况下统一提交队列,进而实现layout操作。

      然而有时希望在js代码中立刻获取最新的DOM节点信息,浏览器就不得不提前执行layout,这是导致DOM性能问题的主因。


      如下的操作会打破常规,并触发浏览器执行layout:
        ·通过js获取需要计算的DOM属性[计算才能得到的属性]
        ·添加或删除DOM元素
        ·resize浏览器窗口大小
        ·改变字体
        ·css伪类的激活,比如:hover
        ·通过js修改DOM元素样式且该样式涉及到尺寸的改变

      所以我们要尽量减少上面layout的次数。

    具体怎么做

    1.批量读写
      当我们需要获取某一属性,这一属性需要计算才能得到,并且队列中存在尚未提交的DOM修改操作,则此时,DOM修改操作的队列将会被提交。
    为了提高效率,减少更新render tree的次数,可以先统一读取属性,然后统一修改DOM,这样,就可以减少更新render tree的次数。

    例如:

    var h1 = element1.clientHeight;
    element1.style.height = (h1 * 2) + 'px';
    var h2 = element2.clientHeight;
    element2.style.height = (h2 * 2) + 'px';
    var h3 = element3.clientHeight;
    element3.style.height = (h3 * 2) + 'px';

    clientHeight,这个属性是需要计算得到的,于是就会触发浏览器的一次layout。

    优化后:

    var h1 = element1.clientHeight;  
    var h2 = element2.clientHeight;  
    var h3 = element3.clientHeight;
    
    element1.style.height = (h1 * 2) + 'px';  
    element2.style.height = (h2 * 2) + 'px';  
    element3.style.height = (h3 * 2) + 'px';

    2.虚拟结点
    当我们需要对DOM做出大量修改时,可以先创建一个虚拟结点,将所有修改附加在该节点,最后将该虚拟节点一次性提交给在render tree上存在的结点,则相当于只提交了一次修改操作。

    var fragment = document.createDocumentFragment();  
    for (var i=0; i < items.length; i++){  
      var item = document.createElement("li");
      item.appendChild(document.createTextNode("Option " + i);
      fragment.appendChild(item);
    }
    list.appendChild(fragment);  

    3.动画的每一帧都会导致layout,这是无法避免的,但是为了减少动画带来的layout的性能损失,可以将动画元素绝对定位,这样动画元素脱离文本流,layout的计算量会减少很多。

    未完待续...

    参考:

    http://kb.cnblogs.com/page/534571/

    --2017年3月06号补充:

    前端开发者应知必会:浏览器是如何渲染网页的

    关键字:层叠样式表模型CSSOM、重绘Repaint、回流Reflow、强制回流

  • 相关阅读:
    python中不可变数据类型和可变数据类型
    你分得清Python中:“索引和切片”吗?
    Python Django中一些少用却很实用的orm查询方法
    jQuery on()方法
    jquery.flexslider-min.js实现banner轮播图效果
    jQuery 树型菜单插件(Treeview)
    jQuery Growl 插件(消息提醒)
    jQuery Autocomplete 用户快速找到并从预设值列表中选择
    jQuery Accordion 插件用于创建折叠菜单
    jquery.validate.js 验证框架详解
  • 原文地址:https://www.cnblogs.com/Chen-XiaoJun/p/6187429.html
Copyright © 2011-2022 走看看