页面重绘及回流的优化办法-
阅读器把猎取到的 HTML 代码解析成1个 DOM树,HTML 中的每个 tag 都是 DOM树中的1个节点,根节点就是我们常用的 document 对象。DOM 树里包括了所有 HTML标签,包含 display:none隐蔽,还实用 JS动态增加的元素等。
阅读器把所有样式(会员定义的CSS和会员代理)解析成样式构造体,在解析的历程中会去除阅读器不克不及辨认的样式,比方IE会去除-moz开头的样式,而FF会去除_开头的样式。
3、DOM Tree 和样式构造体组合后构建 render tree, render tree 相似于DOM tree,但区别很大,render tree 能辨认样式,render tree 中每个NODE都有本人的 style,并且 render tree不包括隐蔽的节点 (比方display:none 的节点,还有 head 节点),由于这些节点不会用于呈现,并且不会影响呈现的,所以就不会包括到 render tree中。注意 visibility:hidden 隐蔽的元素还是会包括到 render tree中的,由于 visibility:hidden 会影响布局(layout),会占有空间。依据CSS2的规范,render tree 中的每个节点都称为Box (Box dimensions),了解页面元素为一个拥有添补、边距、边框和位置的盒子。
一旦 render tree 构建结束后,阅读器就可以依据 render tree 来绘制页面了。
回流与重绘
当 render tree 中的一局部(或全部)由于元素的规模尺寸,布局,隐蔽等转变而需要从新构建。这就称为回流(reflow)。每个页面至少需要一次回流,就是在页面首先次加载的时候。在回流的时候,阅读器会使渲染树中挨到影响的局部失效,并从新结构这局部渲染树,完成回流后,阅读器会从新绘制挨影响的局部到屏幕中,该历程成为重绘。
当 render tree 中的一些元素需要更新属性,而这些属性只是影响元素的外不雅,格调,而不会影响布局的,比方 background-color。则就叫称为重绘。
注意:回流必将引起重绘,而重绘纷歧定会引起回流。
回流何时产生:
当页面布局和几何属性转变时就需要回流。下述状况会产生阅读器回流:
1、增加或者删除可见的DOM元素;
2、元素位置转变;
3、元素尺寸转变——边距、添补、边框、宽度和高度
4、内容转变——比方文本转变或者图片大小转变而引起的盘算值宽度和高度转变;
5、页面渲染初始化;
6、阅读器窗口尺寸转变——resize事件产生时;
让我们看看下面的代码是怎样影响回流和重绘的:
var s = document.body.style; s.padding = "2px"; // 回流+重绘 s.border = "1px solid red"; // 再一次 回流+重绘 s.color = "blue"; // 再一次重绘 s.backgroundColor = "#ccc"; // 再一次 重绘 s.fontSize = "14px"; // 再一次 回流+重绘// 增加node,再一次 回流+重绘 document.body.appendChild(document.createTextNode('abc!'));
说到这里大家都晓得回流比重绘的代价要更高,回流的花销跟 render tree 有多少节点需要从新构建有关系,假如你直接操纵 body,比方在 body 最前面插入1个元素,会致使整个 render tree 回流,这样代价固然会比拼高,但要是是指 body 背面插入1个元素,则不会影响前面元素的回流
聪慧的阅读器
从上个实例代码中可以看到几行简略的JS代码就引起了6次摆布的回流、重绘。并且我们也晓得回流的花销也不小,要是每句JS操纵都去回流重绘的话,阅读器可能就会挨不了。所以许多阅读器都会优化这些操纵,阅读器会保护1个队列,把所有会引起回流、重绘的操纵放入这个队列,等队列中的操纵到了一定的数目或者到了一定的工夫隔断,阅读器就会 flush 队列,进行一个批处置。这样就会让屡次的回流、重绘酿成一次回流重绘。
虽然有了阅读器的优化,但有时候我们写的一些代码可能会强迫阅读器提早 flush 队列,这样阅读器的优化可能就起不到作用了。当你要求向阅读器要求一些 style 信息的时候,就会让阅读器flush队列,比方:
offsetTop, offsetLeft, offsetWidth, offsetHeight scrollTop/Left/Width/Height clientTop/Left/Width/Height width,height 要求了getComputedStyle(), 或者 IE的 currentStyle
当你要求上面的一些属性的时候,阅读器为了给你最精准的值,需要flush队列,由于队列中可能会有影响到这些值的操纵。即便你猎取元素的布局和样式信息跟比来产生或转变的布局信息无关,阅读器都会强行刷新渲染队列。
怎样减少回流、重绘
减少回流、重绘 其实就是需要减少对 render tree 的操纵(合并屡次多DOM和样式的修改),并减少对一些style信息的要求,尽量应用好阅读器的优化战略。概括办法有:
直接转变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;";
让要操纵的元素进行”离线处置”,处置完后一起更新
a) 运用 DocumentFragment 进行缓存操纵,激发一次回流和重绘;
b) 运用 display:none 技术,只激发两次回流和重绘;
c) 运用 cloneNode(true or false) 和 replaceChild 技术,激发一次回流和重绘;
3.不要时常拜访会引起阅读器 flush 队列的属性,要是你的确要拜访,应用缓存
// 别这样写,大哥 for(轮回) { 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"; }
让元素离开动画流,减少回流的Render Tree的规模
$("#block1").animate({left:50}); $("#block2").animate({marginLeft:50});
实例测试
最后用2个工具对上面的理论进行一些测试,离别是:dynaTrace(测试ie),Speed Tracer(测试Chrome)。
首先个测试代码不转变元素的法则,大小,位置。只转变色彩,所以不存在回流,仅测试重绘,代码如下:
打赏