Email:longsu2010 at yeah dot net
我现在的工作对页面的性能要求很高,这一年多以来对这方面有了更深刻的认识,早就想写一些关于这些内容的文章,今天抽时间先写repaint和reflow。
使用js操作DOM时repaint和reflow是经常发生的,如果处理不好这就是页面性能的瓶颈,表现出来现象可能是用户操作响应不及时,浏览器进程cpu特别高。
什么是repaint?
repiant或者redraw遍历所有的节点检测各节点的可见性、颜色、轮廓等可见的样式属性,然后根据检测的结果更新页面的相应部分。
什么是reflow?
reflow指的是计算页面布局。某个节点reflow时会重新计算节点的尺寸和位置,而且还有可能触发其子节点、祖先节点和页面上的其他节点reflow。在这之后再触发一次repaint。reflow对性能的影响非常大,但是很不幸,reflow很容易被触发。
下面的操作会触发reflow:
1、插入、删除或者更新页面(文档树)中的节点。
2、修改页面的内容,例如在input标签中输入内容。
3、移动节点。
4、页面中的动画效果。
5、获取节点的尺寸,例如获取节点的offsetHeight,或者使用getComputedStyle函数获取节点尺寸。
6、改变节点样式。
7、改变节点的className属性。
8、增加或者删除样式表文件。
9、window的resize。
10、滚动滚动条,即scroll。
看看这个列表就知道触发reflow是多么的容易了,好消息是现代的浏览器(比如chrome)已经考虑尽量把reflow降低了,比如在一批layout之后触发一次reflow和repaint而不是每次都触发,再比如当节点设置了display为none时不触发reflow,再比如当在及其短的时间里向文档树中追加节点时根据时间点和节点数目来优化reflow。当然如上说的这些远古时代的IE系列不行。
除了浏览器自身的的改进外,如下是我们在工作中需要注意的(在工作中我都有使用,效果非常好,可以说立竿见影):
1、避免多次设置行内样式,避免分别单独设置样式。比如node.style.height = ''; node.style.width = '';这都会多次触发reflow(现代浏览器好一些,远古浏览器不行)。
2、使用className,并且在尽可能内层的节点上做修改。className可以满足多个样式一次修改,只进行一次reflow。
3、当要批量操作某一个DOM树时可以先将其在文档树中移除,待操作完成后在添加到文档树中,这样可避免多次reflow。
4、避免频繁的获取节点尺寸,可以使用标量缓存节点尺寸,这也是以空间换时间的思想。
5、当某一区域需要频繁的dom操作时可以将其外部包裹一个fixed或者absolute布局。
6、避免使用table布局,table布局会触发更多的reflow。
以上是我实际工作中遇到问题解决问题的收获,欢迎大家补充,不当之处欢迎指正。