游览器性能优化

125 阅读4分钟

HTML页面进行重绘和重排(回流)

1.1问题分析

该问题主题要考核html中的优化 与 重点概念

1.2核心问题讲解

游览器的运行机制

1.构建DOM树(parse) :渲染引擎解析HTMl文档,首先将标签转换成DOM树中的DOM node(包括js生成的标签)生成内容树(Content Tree/DOM Tree);

2.构建渲染树(construct)解析对应的css样式文件信息(包括js生成的样式和外部css文件),而这些文件信息一级HTML中可见的指令,构建渲染树(Rendering Tree/Frame Tree);render tree中每个node都有自己的style,而且render tree 不包含隐藏的节点(比如 displayL:none的节点,还有head节点),因为这些节点不会用于呈现

3.布局渲染树(reflow/layout)从根节点递归调用,计算也没一个元素的大小、位置等,给出每个节点所应该在屏幕上出现的 精确坐标;

4.绘制渲染树(paint/repanit) :遍历渲染树,使用UI层来绘制每个节点

重绘(repaint或redraw):

当盒子的位置、大小以及其它属性,列入颜色,字体大小都确定下来之后,游览器便把这些颜色都按照各自的特性绘制一遍,呈现在页面上。

重绘是指一个元素外观的改变所触发的游览器行为,游览器会根据元素的新属性重新绘制,使元素呈现新的外观。

触发重绘的条件:改变元素外观属性。 如:color,background-color等

注意:table以及其内部元素可能需要多次计算才能确定好其在渲染树中节点的属性值,比如同等元素要多花两倍时间,这就是我们尽量避免使用table布局页面的原因之一。

重排(重构或回流/reflow): 当渲染树中的一部分(或全部)因为元素的规模尺寸、布局,隐藏等改变而需要重新构建,这就称为回流。每个页面至少需要一次回流,就是在页面第一次加载的时候。

重绘和重排的关系:在回流的时候,游览器回事渲染树中受到影响的部分失效,并重新构造这部分渲染树,完成回流后,游览器会重新绘制受影响的部分到屏幕中,该过程称为重绘。

所以重排(回流)必引发重绘,但重绘不一定会引发重排

触发重排条件:任何页面布局和几何属性的改变都会触发重排,比如

1、页面渲染初始化;(无法避免)

2、添加或删除可见的 DOM 元素;

3、元素位置的改变,或者使用动画;

4、元素尺寸的改变——大小,外边距,边框;

5、浏览器窗口尺寸的变化(resize 事件发生时);

6、填充内容的改变,比如文本的改变或图片大小改变而引起的计算值宽度和高度的改变; 7、读取某些元素属性:(osetLe/Top/Height/Width, clientTop/Le/Width/Height, scrollTop/Le/Width/Height, width/height, getComputedStyle(), currentStyle(IE) )

重绘重排的代价:耗时,导致浏览器卡慢。

优化:

1、浏览器自己的优化:浏览器会维护 1 个队列,把所有会引起回流、重绘的操作放入这个队列,等队 列中的操作到了一定的数量或者到了一定的时间间隔,浏览器就会 flush 队列,进行一个批处理。这样 就会让多次的回流、重绘变成一次回流重绘。

2、我们要注意的优化:我们要减少重绘和重排就是要减少对渲染树的操作,则我们可以合并多次的 DOM 和样式的修改。并减少对 style 样式的请求。

(1)直接改变元素的 className

(2)display:none;先设置元素为 display:none;然后进行页面布局等操作;设置完成后将元素设置 为 display:block;这样的话就只引发两次重绘和重排;

(3)使用 cloneNode(true or false) 和 replaceChild 技术,引发一次回流和重绘;

(4)将需要多次重排的元素,position 属性设为 absolute 或 fixed,元素脱离了文档流,它的变化不会 影响到其他元素;

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

项目结合使用

不要边渲染边循环

<script>

varfragment=document.createDocumentFragment()

*for* (leti=0;i<100;i++) {

varli=document.createElement('li')

li.innerHTML='apple'+i

fragment.appendChild(li)

}

document.getElementById('fruit').appendChild(fragment)

</script>