写在前面:

  大家都知道DOM的操作很昂贵。 

  然后贵在什么地方呢?

  一、访问DOM元素

  二、修改DOM引起的重绘重排

一、访问DOM  

  像书上的比喻:把DOM和JavaScript(这里指ECMScript)各自想象为一个岛屿,它们之间用收费桥梁连接,ECMAScript每次访问DOM,都要途径这座桥,并交纳“过桥费”,访问DOM的次数越多,费用也就越高。因此,推荐的做法是尽量减少过桥的次数,努力待在ECMAScript岛上。我们不可能不用DOM的接口,那么,怎样才能提高程序的效率?

  1. 既然无法避免,那就减少访问。(width、offsetTop、left。。。能少就少,可以缓存起来的,就缓存)

    // code1错误
    console.time();
    for(var i = ; i < times; i++) {
    document.getElementById('div1').innerHTML += 'a';
    }
    console.timeEnd(); // code2正确
    console.time();
    var str = '';
    for(var i = ; i < times; i++) {
    str += 'a';
    }
    document.getElementById('div2').innerHTML = str;
    console.timeEnd();
    ////////////////////////

  2. html集合&遍历DOM

    html集合类似数组,但是跟数组还是不一样的。如: document.getElementsByTagName('a') 返回的html集合。这个集合是实时更新的,即后面代码修改了DOM,会反映在这个html集合里面。可尝试代码。

<body>
<ul id='fruit'>
<li> apple </li>
<li> orange </li>
<li> banana </li>
</ul>
</body>
<script type="text/javascript">
var lis = document.getElementsByTagName('li');
var peach = document.createElement('li');
peach.innerHTML = 'peach';
document.getElementById('fruit').appendChild(peach); console.log(lis.length); //
</script>

    正因为这个原因:html集合,读取 length 属性比数组消耗大多了。

    要解决这个问题并不难,在遍历DOM集合的时候,缓存length就好了。不要每次使用就获取,主要体现在for循环中(你应该知道,for循环中,每一次都会执行判读语句,读取length)

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

 二、重绘重排

  1.什么是重绘重排?

  浏览器下载完页面中的所有组件——HTML标记、JavaScript、CSS、图片之后会解析生成两个内部数据结构——DOM树渲染树

   在文档初次加载时,浏览器引擎通过解析 html文档 构建一棵DOM树,之后根据DOM元素的几何属性构建一棵用于展示渲染的渲染树。渲染树中的节点被称为“帧”或“盒",符合CSS模型的定义,可理解为(包括理解页面元素为一个具有大小,填充,边距,边框和位置的盒子)。由于隐藏元素不需要显示,渲染树中并不包含DOM树中隐藏的元素(知道这点有用)。 当渲染树构建完成,浏览器把每一个元素放到正确的位置上,然后再根据每一个元素的其他样式,绘制页面。

  由于浏览器的流布局,对渲染树的计算通常只需要遍历一次就可以完成。但table及其内部元素除外,它可能需要多次计算才能确定好其在渲染树中节点的属性,通常要花3倍于同等元素的时间。这也是为什么我们要避免使用table做布局的一个原因。

  重绘:是一个元素外观的改变所触发的浏览器行为,例如改变visibility、outline、背景色等属性(上面说到的其他属性)。浏览器会根据元素的新属性重新绘制,使元素呈现新的外观。重绘不会带来重新布局,并不一定伴随重排。

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

  2. 触发重排的操作:

  2.1 修改DOM元素几何属性

    修改元素大小,位置,内容(一般只有重绘,但是内容可能导致元素大小变化)

  2.2 DOM树结构发生变化

    当DOM树的结构变化时,例如节点的增减、移动等,也会触发重排。浏览器引擎布局的过程,类似于树的前序遍历,是一个从上到下从左到右的过程。 通常在这个过程中,当前元素不会再影响其前面已经遍历过的元素。所以,如果在body最前面插入一个元素,会导致整个文档的重新渲染,而在其后插入一个元 素,则不会影响到前面的元素。

   2.4 改变浏览器大小

  

  3.渲染树变化的排队和刷新

  思考下面代码:

 var ele = document.getElementById('myDiv');
ele.style.borderLeft = '1px';
ele.style.borderRight = '2px';
// var _top = ele.offsetTop; //刷新队列
ele.style.padding = '5px';

  三行代码,三次修改元素的几何属性,浏览器应该发生三次重排重绘。

  但是浏览器并不会这么笨,它也是有做优化的。它会把三次修改“保存”起来(大多数浏览器通过队列化修改并批量执行来优化重排过程,也有设置时间片段的),一次完成!

  然而,如果你在三行代码中,以下获取DOM布局信息。(为了返回最新的布局信息,将立即执行渲染树变化队列的更新)

  如上面被注释的第4行,如果取消注释会导致(2+3)、(5)两次重排;

  获取关于DOM布局信息的属性:

  1. offsetTop, offsetLeft, offsetWidth, offsetHeight
  2. scrollTop, scrollLeft, scrollWidth, scrollHeight
  3. clientTop, clientLeft, clientWidth, clientHeight
  4. getComputedStyle() (currentStyle in IE)

  4 应对方法:尽量减少重绘次数、减少重排次数、缩小重排的影响范围。

  4.1 合并多次操作,如上面的操作

ele.style.cssText = 'border-left: 1px; border-right: 2px; padding: 5px;';

  4.2 将需要多次重排的元素,position属性设为absolute或fixed,这样此元素就脱离了文档流,它的变化不会影响到其他元素。例如有动画效果的元素就最好设置为绝对定位。

   4.3 由于display属性为none的元素不在渲染树中,对隐藏的元素操作不会引发其他元素的重排。如果要对一个元素进行复杂的操作时,可以先隐藏它,操作完成后再显示。这样只在隐藏和显示时触发2次重排。但是这可能导致浏览器的闪烁。

  4.4 在内存中多次操作节点,完成后再添加到文档中去(可使用fragment元素)。例如要异步获取表格数据,渲染到页面。可以先取得数据后在内存中构建整个表格的html片段,再一次性添加到文档中去,而不是循环添加每一行。

var fragment = document.createDocumentFragment();    // 未使用的虚拟节点,appendChild(fragment)  //append的是里面的子元素

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

参考文档:

关于DOM的操作以及性能优化问题-重绘重排的更多相关文章

  1. 前端性能优化--为什么DOM操作慢? 浅谈DOM的操作以及性能优化问题-重绘重排 为什么要减少DOM操作 为什么要减少操作DOM

    前端性能优化--为什么DOM操作慢?   作为一个前端,不能不考虑性能问题.对于大多数前端来说,性能优化的方法可能包括以下这些: 减少HTTP请求(合并css.js,雪碧图/base64图片) 压缩( ...

  2. DOM操作的性能优化

    DOM操作的真正问题在于 每次操作都会出发布局的改变.DOM树的修改和渲染. React解决了大面积的DOM操作的性能问题,实现了一个虚拟DOM,即virtual DOM,这个我们一条条讲. 所以关于 ...

  3. DOM性能瓶颈与Javascript性能优化

    这两天比较闲,写了两篇关于JS性能缺陷与解决方案的文章(<JS特性性能缺陷及JIT的解决方案>,<Javascript垃圾回收浅析>),主要描述了untyped,GC带来的问题 ...

  4. Dom 重绘重排

    https://juejin.im/entry/590801780ce46300617c89b8   DOM 重绘重排

  5. 关于DOM操作的性能优化

    最著名的有关用js操作dom的观点是:js和dom是独立的小岛,用桥实现两者的联系,但桥很窄,要过路费,所以我们要尽最大可能减少过桥的次数.下面代码演示了用js操作dom的innerHTML,且一下修 ...

  6. DOM 操作成本究竟有多高,HTML、CSS构建过程 ,从什么方向出发避免重绘重排)

    前言: 2019年!我准备好了 正文:从我接触前端到现在,一直听到的一句话:操作DOM的成本很高,不要轻易去操作DOM.尤其是React.vue等MV*框架的出现,数据驱动视图的模式越发深入人心,jQ ...

  7. js 性能篇--dom 重绘 重排 节流

    浏览器下载完页面中的所有组件----HTML标记,Js,CSS,图片等之后会解析并生成两个内部数据结构: DOM树  -------- 表示页面结构 渲染树   -------- 表示DOM节点如何显 ...

  8. Attribute操作的性能优化方式

    Attribute是.NET平台上提供的一种元编程能力,可以通过标记的方式来修饰各种成员.无论是组件设计,语言之间互通,还是最普通的框架使 用,现在已经都离不开Attribute了.迫于Attribu ...

  9. 【redis常用的键值操作及性能优化】

    服务端 启动redis服务 { // -a:指定密码 -h:指定主机 -p:指定端口 } //让redis 服务中断崩溃 //保存和关闭 //后台备份 //设置登录密码 //redis-benchma ...

随机推荐

  1. Javaweb学习随笔_JSP的九大内置对象

    JSP内置对象整理 1. 九大内置对象: out,request,response,session,application,page,pageContext,config,Exception. 1.1 ...

  2. Single Number

    Given an array of integers, every element appears twice except for one. Find that single one.Your al ...

  3. CSS,点击去除虚线边框代码

  4. [Redux] Passing the Store Down Implicitly via Context

    We have to write a lot of boiler plate code to pass this chore down as a prop. But there is another ...

  5. Mybatis Sql片段的应用

    在一个查询里,针对各种不同数据库,有时候只是一部分 SQL 是不相同的,为避免相同的部分复制多次,所以将不相同的部分进行适当的隔离,再重用就可以了. 在 MyBatis 里声明两段 <sql d ...

  6. Java引入的一些新特性

    Java引入的一些新特性 Java 8 (又称为 jdk 1.8) 是 Java 语言开发的一个主要版本. Oracle 公司于 2014 年 3 月 18 日发布 Java 8 ,它支持函数式编程, ...

  7. 第29月第2天 charles

    1. https://www.jianshu.com/p/55a8c84e0f24

  8. Fedora 25 安装 Bugzilla

    最近领导要我们装一个Bugzulla,虽然我们项目已经有了JIRA(我就呵呵),不过我还是找些资料安装了,在此备忘. Bugzilla 是一个开源的缺陷跟踪系统(Bug-Tracking System ...

  9. js gettext

    test.php 1 <?php $locale='zh_CN'; if(isSet($_GET["locale"]))$locale = $_GET["local ...

  10. 每日英语:China to Move Slowly on One-Child Law Reform

    BEIJING—China's family-planning agency is projecting a slow rollout for an easing of its one-child p ...