zoukankan      html  css  js  c++  java
  • 操作dom影响性能的原因

    为什么dom操作会影响性能?

    在浏览器当中,dom的实现和ECMAScript的实现是分离的。

    例如,在IE中,ECMAScrit的实现在jscript.dll中,而DOM的实现在mshtml.dll中;在Chrome中使用WebKit中的 WebCore处理DOM和渲染,但ECMAScript是在V8引擎中实现的,其他浏览器的情况类似。

    因此,操作dom,就是通过js代码调用dom的接口,就相当于两个相互独立的模块发生了交互。这样,相比于在同一个模块当中互相调用,这种跨模块的调用它的性能损耗是非常高的。

    然而,dom操作影响性能最主要是因为它导致了浏览器的重绘(repaint)和重排(reflow)。

    浏览器渲染原理

    为了可以更加深刻地理解重绘和重排对性能的影响,需要简单了解一下浏览器的渲染原理。

    从下载文档到渲染页面的过程中,浏览器会通过解析HTML文档来构建DOM树,解析CSS产生CSS规则树。JavaScript代码在解析过程中, 可能会修改生成的DOM树和CSS规则树。之后根据DOM树和CSS规则树构建渲染树,在这个过程中CSS会根据选择器匹配HTML元素。渲染树包括了每 个元素的大小、边距等样式属性,渲染树中不包含隐藏元素及head元素等不可见元素。最后浏览器根据元素的坐标和大小来计算每个元素的位置,并绘制这些元 素到页面上。无论何时总会有一个初始化的页面布局伴随着一次绘制。

    重绘

    重绘,就是指页面某些部分需要重新绘制,由于节点的几何属性发生改变或者由于样式发生改变,例如改变元素背景色时,屏幕上的部分内容需要更新,而元素的位置和尺寸并没有改变。

    重排

    元素的位置或尺寸发生了改变,浏览器需 要重新计算渲染树,导致渲染树的一部分或全部发生变化。渲染树重新建立后,浏览器会重新绘制页面上受影响的元素。

    也就是说,重排,改变的是dom文档的结构,例如dom元素的位置或者尺寸发生了变化,需要重新布局,就会发生重排。

    优化方法:

    1、将dom操作积累起来作批量操作

    现代浏览器中会有优化方法,就是把dom操作积累起来,做批量处理。但是在有些情况下,浏览器会立即重排或重绘。比如请求如下的DOM元素布局信息:offsetTop/Left/Width/Height、scrollTop/Left/Width/Height、clientTop/Left/Width/Height、getComputedStyle()或 currentStyle。因为这些值都是动态计算的,所以浏览器需要尽快完成页面的绘制,然后计算返回值,从而打乱了重排或重绘的优化。

    2、合并多次的DOM操作为单次的DOM操作

    最常见频繁进行DOM操作的是频繁修改DOM元素的样式,代码类似如下:

    element.style.borderColor = '#f00';
    element.style.borderStyle = 'solid';
    element.style.borderWidth = '1px';

    这种编码方式会因为频繁更改DOM元素的样式,触发页面多次的重排或重绘,上面介绍过,现代浏览器针对这种情况有性能的优化,它会合并DOM操作,但并不是所有的浏览器都存在这样的优化。推荐的方式是把DOM操作尽量合并,如上的代码可以优化为:

    // 优化方案1
    element.style.cssText += 'border: 1px solid #f00;';
    // 优化方案2
    element.className += 'empty';


    示例的代码有两种优化的方案,都做到了把多次的样式设置合并为一次设置。方案2比方案1稍微有一些性能上的损耗,因为它需要查询CSS类。但方案2的维护性最好,这在上一章曾经讨论过。很多时候,如果性能问题并不突出,选择编码方案时需要优先考虑的是代码的维护性。

    类似的操作还有通过innerHTML接口修改DOM元素的内容。不要直接通过此接口来拼接HTML代码,而是以字符串方式拼接好代码后,一次性赋值给DOM元素的innerHTML接口。

    3、使用文档片段

    文档片段是一个轻量级的document对象,并不会和特定的页面关联。通过在文档片段上进行DOM操作,可以降低DOM操作对页面性能的影响,这 种方式是创建一个文档片段,并在此片段上进行必要的DOM操作,操作完成后将它附加在页面中。对页面性能的影响只存在于最后把文档片段附加到页面的这一步 操作上。代码类似如下:

    var fragment = document.createDocumentFragment();
    // 一些基于fragment的大量DOM操作
    ...
    document.getElementById('myElement').appendChild(fragment);

    4、通过设置DOM元素的display样式为none来隐藏元素

    这种方式是通过隐藏页面的DOM元素,达到在页面中移除元素的效果,经过大量的DOM操作后恢复元素原来的display样式。对于这类会引起页面重绘或重排的操作,就只有隐藏和显示DOM元素这两个步骤了。代码类似如下:

    var myElement = document.getElementById('myElement');
    myElement.style.display = 'none';
    // 一些基于myElement的大量DOM操作
    ...
    myElement.style.display = 'block';

    5、克隆DOM元素到内存中

    这种方式是把页面上的DOM元素克隆一份到内存中,然后再在内存中操作克隆的元素,操作完成后使用此克隆元素替换页面中原来的DOM元素。这样一来,影响性能的操作就只是最后替换元素的这一步操作了,在内存中操作克隆元素不会引起页面上的性能损耗。代码类似如下:

    var old = document.getElementById('myElement');
    var clone = old.cloneNode(true);
    // 一些基于clone的大量DOM操作
    ...
    old.parentNode.replaceChild(clone, old);

    6、设置具有动画效果的DOM元素的position属性为fixed或absolute

    把页面中具有动画效果的元素设置为绝对定位,使得元素脱离页面布局流,从而避免了页面频繁的重排,只涉及动画元素自身的重排了。这种做法可以提高动 画效果的展示性能。如果把动画元素设置为绝对定位并不符合设计的要求,则可以在动画开始时将其设置为绝对定位,等动画结束后恢复原始的定位设置。在很多的 网站中,页面的顶部会有大幅的广告展示,一般会动画展开和折叠显示。如果不做性能的优化,这个效果的性能损耗是很明显的。使用这里提到的优化方案,则可以 提高性能。

  • 相关阅读:
    开学第二周java作业暨动手动脑p1
    开学第一周
    暑假第九周
    暑假第八周
    bzoj4033:[HAOI2015]树上染色
    bzoj4472:[Jsoi2015]salesman
    dp专题复习
    bzoj1864:[Zjoi2006]三色二叉树
    bzoj1190:[HNOI2007]梦幻岛宝珠
    bzoj2794:[Poi2012]Cloakroom
  • 原文地址:https://www.cnblogs.com/ziyoublog/p/9989797.html
Copyright © 2011-2022 走看看