88bf必发唯一娱乐官网_www.bifa888.com
做最好的网站

有关介绍,怎样压缩Dom操作中的回流和重绘难题

作者: web前端  发布:2019-08-12

render : 描述 DOM 节点 (nodes) 在页面上如何呈现

如何减少回流和重绘

减少回流、重绘其实就是需要减少对 render tree的操作(合并多次Dom和样式的修改),并减少对一下style信息的请求,尽量利用好浏览器的优化策略。具体方法如下:

图片 1

offsetTop, offsetXXX…

常见的重绘和回流操作?

(1)添加、删除元素(回流 重绘)
(2)隐藏元素,display:none(回流 重绘)
(3)移除元素,比如改变top,left(jQuery的animate方法就是,该变top,left不一定会回流),或者移动元素到另外1个父元素中。(回流 重绘)
(4)对style的操作(对不同的属性操作,影响不一样)
(5)以下属性,只要改动他们的值,就会造成回流,这些属性包括:offsetLeft、offsetTop、offsetWidth、offsetHeight;scrollTop、scrollLeft、scrollWidth、scrollHeight;clientWidth、clientHeight、clientLeft、clientTop;
(6)还有一种是用户的操作,比如改变浏览器大小,改变浏览器的字体大小。

4、绘制渲染树:遍历渲染树,每个节点将使用UI后端层来绘制。即图中第四个黄色椭圆。

这两个过程是很耗费浏览器性能的, 从 IE 系列和 Chrome 渲染页面速度上的差距即可看出渲染引擎计算对应值和呈现并不一定高效, 而每次对元素的操作都会发生 repaints 或 reflow, 因此编写 DOM 交互时如果不注意就会导致页面性能低下.

3.不要经常访问会引起浏览器flush队列的属性,如果你确实要访问,利用缓存

//BAD WAYfor(循环)
{el.style.left = el.offsetLeft 5 "px";el.style.top = el.offsetTop

  • 5 "px";} // 这样写好点
    var left = el.offsetLeft,top = el.offsetTop,s = el.style;
    for (循环) { left = 10; top = 10; s.left = left "px"; s.top = top
  • "px"; }

repaint 重绘

1、什么是repaint

当盒子的位置、大小以及其他属性,例如颜色、字体大小等都确定下来之后,浏览器便把这些原色都按照各自的特性绘制一遍,将内容呈现在页面上,这个过程称为repaint

2、什么情况下会导致repaint

“A repaint occurs when changes are made to an elements skin that changes visibility, but do not affect its layout.” --重绘发生在元素的可见的外观被改变,但并没有影响到布局的时候。比如,仅修改DOM元素的字体颜色(只有Repaint,因为不需要调整布局)

3、为什么repaint 不可取

repaint的开销是昂贵的,因为浏览器必须验证DOM Tree中所有结点的可见性。

“repaint is expensive because the browser must verify the visibility of all other nodes in the DOM tree.”

////

改变浏览器窗口的大小

2. 让要操作的元素进行”离线处理”,处理完后一起更新

a) 使用DocumentFragment进行缓存操作,引发一次回流和重绘;

//不好的写法(模式中所说的反模式)
var p, t;
p = document.creatElement('p');
t = document.creatTextNode('fist paragraph');
p.appendChild(t);document.body.appendChild(p); //将引起一次回流
p = document.creatElement('p');
t = document.creatTextNode('second paragraph');
p.appendChild(t);document.body.appendChild(p); //将再引起一次回流
//好的写法
var p, t, frag;
frag = document.creatDocumentFragment();
p = document.creatElement('p');
t = document.creatTextNode('fist paragraph');
p.appendChild(t);
farg.appendChild(p);
p = document.creatElement('p');
t = document.creatTextNode('second paragraph');
p.appendChild(t);
farg.appendChild(p);
document.body.appendChild(frag); //相比前面的方法,这里仅仅引起一次回流,倘若页面里有很多这样的操作,利用文档随便将会提升很多
b) 使用display:none技术,只引发两次回流和重绘; ( 只是减少重绘和回流的次数,display:none 是会引起重绘并回流,相对来说,visibility: hidden只会引起重绘 )
c) 使用cloneNode(true or false) 和 replaceChild 技术,引发一次回流和重绘;
//建立克隆镜像var oldNode = document.getElementById('target'), clone = oldNode.cloneNode(true); //深复制// 处理克隆对象的操作....//完成后oldNode.parentNode.replaceChild(clone,oldNode);

可以看出, reflow 和repaint分别对应步骤中的第三步和第四步

当发生以下情况的时候, 会引发 reflow:

4. 让元素脱离动画流,减少回流的Render Tree的规模**

怎么样优化

Reflow是不可避免的,只能将Reflow对性能的影响减到最小

(1)DOM离线后修改。如:

    a)先把DOM给display:none(有一次reflow),然后你想怎么改就怎么改。比如修改100次,然后再把他显示出来。

    b)如果需要创建多个DOM节点,可以使用DocumentFragment创建完后一次性的加入document

        var fragment = document.createDocumentFragment();

        fragment.appendChild(document.createTextNode('test 111'));

        fragment.appendChild(document.createElement('br'));

        fragment.appendChild(document.createTextNode('test 222'));

        document.body.appendChild(fragment);

    c)clone一个DOM节点(使用cloneNode()方法)到内存里,然后想怎么改就怎么改,改完后,和在线的那个的交换一下。克隆一个结点cloneNode(true)深度克隆(将deep设置成了true),即同时会克隆该结点的所有子结点。cloneNode(false)浅克隆(将deep设置成false),即仅仅克隆当前结点。如何将结点克隆到内存?如何和在线的交换?(暂时不会做)

(2)集中修改样式

    a)尽可能少的修改style上的属性

    b)尽量通过修改className来修改样式。

    c)通过cssText属性来设置样式。因为 element.style.width=”80px”;  //reflow  。调用一次就会产生一次reflow 调用越多产生越多。所以,element.style.cssText=”width:80px;height:80px;border:solid 1px red;”; //reflow  。 只产生一次reflow。

(3)缓存layout的值。var left = ele.offsetLeft; 多次使用left也就产生一次reflow。

(4)设置元素的position的属性为absolute 或者fixed 。元素脱离标准流也就从DOM Tree中脱离出来,在需要reflow的时候只需要reflow自身以及下级元素。

(5)尽量不要使用table布局。table元素一旦触发reflow就会导致table里所有的其它元素 reflow。在适合用table的场合,可以设置table-layout为auto或fixed,这样可以让table一行一行的渲染,这种做法也是为了限制reflow的影响范围

(6)避免使用expression,因为expression对浏览器资源要求比较高。它会每次调用都会重新计算一遍(包括加载页面)

在CSS中使用expression,用来把CSS属性和Javascript表达式关联起来,这里的CSS属性可以是元素固有的属性,也可以是自定义属性。就是说CSS属性后面可以是一段Javascript表达式,CSS属性的值等于Javascript表达式计算的结果。 在表达式中可以直接引用元素自身的属性和方法,也可以使用其他浏览器对象。

#container { width: expression((documentElement.clientWidth < 725) ? "725px" : "auto" ); }相当于:min-width: 725px

el.style.cssText = "width: 100px; height: 100px;";
// or el.className = "square";
var width = el.style.width;
el2.style.width = width * 1.5;
el3.style.width = width * 2;

1.直接改变className,如果动态改变样式,则使用cssText(考虑没有优化的浏览器)

// 不好的写法
var left = 1;
var top = 1;
el.style.left = left "px";
el.style.top = top "px";
// 比较好的写法
el.className = " className1";
// 比较好的写法
el.style.cssText = ";
left: " left "px;
top: " top "px;";

3、布局渲染树:从根节点递归调用,计算每一个元素的大小、位置等,给出每个节点所应该在屏幕上出现的青雀坐标。即图中第三个黄色椭圆

把被剥离的元素恢复到 DOM 流

什么是回流和重绘?

(1)当render tree中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建。这就称为回流(reflow)。然而,每一个页面至少需要回流一次,就是在页面第一次加载的时候。在回流的时候,浏览器会使渲染树中受影响的一部分失效,并重新构造这部分渲染树,完成回流后,浏览器会重新绘制受影响的部分到屏幕中,该过程为重绘。
(2)当render tree中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的,比如background-color,这就是重绘。

*注意:回流必然会引起重绘,而重绘不一定会引起回流。

/-----------------------------实际应用---------------------------/

页面滚动时,网站导航条固定悬浮在页面顶部,同时固定悬浮条的出现和消失都是transition:ease-out动画效果。

1、具体实现

图片 2

<一>页面HTML代码

(1)当页面滚动一定距离(具体值自己根据需求设定)的时候隐藏。

图片 3

下面是对应的css

图片 4

注!如果没有margin-top便没有动画ease-out的效果

(2)页面继续滚动到一定距离(具体值自己根据需求设定)的时候显示

图片 5

js动态添加的class ,下面是具体样式

图片 6

注!z-index为了防止被页面中其他脱离文档流的元素覆盖。

有了以上的代码,页面的效果也就出来了,但是有点小bug。导航条元素原本有一定高度,因为使用fixed的定位之后,元素就会脱离文档流,原本在文档中的位置也就会消失,页面就会整体上移导航条高度的距离,会产生一次reflow,这次的reflow不可避免,因为是效果的需要。页面整体的上移就会是页面又一点抖的bug,如果文档的高度恰巧在浏览器窗口的高度与hidden的值之和浏览器窗口的高度与hidden的值之和之间,而文档的高度又小于浏览器窗口的高度与一次scroll的高度(100px或者114px,不同内核的浏览器有些不一样),这段距离就是临界区域,鼠标滚动一下页面就会上下的闪一下,效果感觉就不会很好。

2、解决办法:当元素position:fixed脱离文档流的时候,原来导航条高度不让它消失。

(1)给body添加padding-top:导航条高度

(2)给导航条元素外再加一个父级div并设置height:导航条高度,当导航条脱离的时候仍有其父元素占位。

(3)给导航条的下一个元素(弟弟元素)添加margin-top:导航条高度。如果导航条所在页面是模板,有好多的页面都在引用,那么这种方法不可取,因为会有好多页面都会引用导航条所在的模板页面,这就导致了导航条的下一个元素不唯一,总不能所有的页面中的导航条的下一个元素都用js添加样式。

(4)初始状态给body添加一个padding-top,并且初始状态就将导航条用position:absolute脱离文档流

第一种方法和第三种方法的原理是一样的,都是用js操作css动态给DOM结点添加样式,会产生一次reflow。而第二种和第四种方式就不会产生reflow。那么这个效果的实现至少就会减少一次的reflow。

Copy to Clipboard图片 7引用的内容:[www.bkjia.com] // DocumentFragment
var fm = document.createDocumentFragment();
var a;
for(var i = 0; i < 20; i ){
a = document.createElement("a");
a.href = i ".html";
a.innerHTML = "page " i;
fm.appendChild(a);
}
document.getElementById("tar").appendChild(fm);

我们先看一下浏览器解析的工作流程

IE lte 8 有一个 bug — :hover, 大量出现使用该选择器的元素的话会降低页面响应速度

从图上可以看出,浏览器的工作流程可分为四步:

幸运地, 浏览器对 reflow 的处理是和 PHP 的 Output Control 相似的 — 把 reflow 放进一个队列, 达到一定程度或时限就进行 flush. 但不幸的是, 这个过程可能会被我们强制提前执行 — 只要使用下面所列的任一个都会迫使浏览器 flush, 因此产生 reflow:

1、解析HTML构建DOM树:渲染引擎开始解析HTML文档,转换树中的HTML标签或者js生生的标签到DOM节点,它被称为内容树。即图中第一个黄色椭圆

在不鸟 older browsers 的情况下, 可以大量使用 firstElementChild(),querySelectorAll() 等 API, 可实现的功能和 jQuery 等库的 CSS 选择器和遍历函数相差不大

2、构建渲染树:解析css(包括外部css文件和样式元素以及js生成的样式),根据css选择器计算出节点的样式,创建另一个数,渲染树。即图中第二个黄色椭圆

大致优化思路如下:

reflow 重写

1、什么是reflow

对于DOM结构中的各个元素都有自己的盒子模型,这些都需要浏览器根据各种样式(浏览器的、开发人员定义的等)来计算并根据计算结果将元素放到它该出现的位置,这个过程称为 reflow

2、什么情况下会导致reflow

(1)DOM元素的添加、修改(内容)、删除( Reflow Repaint);

(2)当你移动 DOM 的位置,或是搞个动画的时候;

(3)当你 Resize 窗口的时候(移动端没有这个问题),或是滚动的时候。

(4)读取元素的某些属性(offsetLeft、offsetTop、offsetHeight、offsetWidth、 scrollTop/Left/Width/Height、clientTop/Left/Width/Height、 getComputedStyle()、currentStyle(in IE))

注:display:none 会发生reflow ,而,visibility:hidden 只会触发repaint ,因为它没有发现位置的变化。

3、为什么reflow不可取

DOM Tree里每一个结点都会有reflow方法,一个结点的reflow可能会导致其子结点,甚至父级结点以及同级结点的reflow,同时他会触发repaint,并且reflow的开销非常昂贵。在一些高性能的电脑上或许没什么,但是如果reflow发生在低性能比如手机上,那么这个过程非常痛苦和耗电的。

“A reflow is even more critical to performance because it involves changes that affect the layout of a portion of the page (or the whole page). Reflow of an element causes the subsequent reflow of all child and ancestor elements as well as any elements following it in the DOM. Reflows are very expensive in terms of performance, and is one of the main causes of slow DOM scripts, especially on devices with low processing power, such as phones. In many cases, they are equivalent to laying out the entire page again.”

在被剥离的的元素上进行各种操作

一个页面由两部分组成:

Copy to Clipboard图片 8引用的内容:[www.bkjia.com] var el = document.getElementById("id");
el.style.width = 100px;
el.style.height = 100px;
// suppose there are two els : el2, el3
el2.style.width = el.style.width * 1.5;
el3.style.width = el.style.width * 2;

  1. 什么是 repaint 和 reflow

写成这样会有更好的性能:

getComputedStyle / currentStyle

页面第一次渲染

当 DOM 元素的属性发生变化 (如 color) 时, 浏览器会通知 render 重新描绘相应的元素, 此过程称为 repaint.

如果该次变化涉及元素布局 (如 width), 浏览器则抛弃原有属性, 重新计算并把结果传递给 render 以重新描绘页面元素, 此过程称为 reflow.

可见元素的增删

// cloneNode
var ul = document.getElementById("ul");
var clone = ul.cloneNode(true); // true = deep copy
var li;
for(var i = 0; i < 20; i ){
li = document.createElement("li");
li.appendChild(document.createTextNode(i));
clone.appendChild(li);
}
ul.parentNode.replaceChild(clone, ul);

clientTop, clientXXX…

DOM : 描述该页面的结构

操作 DOM 时尽量使用 DocumentFragment 和 cloneNode:

  1. 怎么优化

此外对事件的绑定上尽可能使用 delegation, 也就是合理利用 event 的冒泡特性, 我认为这对 repaint & reflow 的影响是比较小的, 不过对于代码优化, 复用性和灵活性都有很大的帮助, 有空再继续

. 什么是 repaint 和 reflow 一个页面由两部分组成: DOM : 描述该页面的结构 render : 描述 DOM 节点 (nodes) 在页面上如何呈现 当 DOM 元素的属性发生...

元素的位置, 大小, 内容的改变

  1. Reflow

scrollTop, scrollXXX…

注意:

把元素从 DOM 流中剥离 (clone, fragment, position: absolute, 绝对定位对动画或拖曳对象尤为有用)

首先是减少对 DOM 中有关布局的操作, 假设我们要动态改变一个元素的大小为 100*100px:

本文由88bf必发唯一娱乐官网发布于web前端,转载请注明出处:有关介绍,怎样压缩Dom操作中的回流和重绘难题

关键词: web前端

上一篇:附演示和科目,40款非常的厉害的jQuery
下一篇:没有了