渲染页面的过程:
1.解析html源码,创建dom树。每个标签都是一个节点。
2.解析css
3.构建dom树,并且计算出样式(padding、margin等),然后构建 渲染树。对于渲染树和dom树的不同是:dom树的每一个标签都是一个节点,但是渲染树会忽略掉不需要渲染的元素,例如head、display:none的元素。渲染树中的每一个节点都存储有对应的css属性。
4.渲染树创建好后,浏览器就可以根据渲染树直接把页面绘制到屏幕上。
重排和重绘:
重排:就是渲染树的一部分必须要更新 并且节点的尺寸发生了变化。这就会触发重排操作(相当于渲染树需要重新计算.....)。
重绘:部分节点需要更新,但是没有改变他的集合形状,比如改变了背景颜色,这就会触发重绘。
产生重排或重绘的操作:
(1)增加或删除DOM节点
(2)设置 display: none;(重排并重绘) 或者 visibility: hidden(只有重排)
(3)移动页面中的元素
(4)增删或者修改样式
(5)计算offsetWidth和offsetHeight
(6)用户 改变窗口大小,滚动页面等
浏览器的处理方案:
因为渲染树的改变导致的重绘或重排操作都可能代价很高,浏览器会对这个改动做很多优化。
一个策略就是不要立即做操作,而是批量进行。比如把你的脚本对DOM的修改放入一个队列,在队列所有操作结束后只需要进行一次绘制即可。
但是有的时候脚本可能会导致浏览器的批量优化无法进行,可能在清空队列之前就需要重新绘制(绘制意思是重绘或者重排)页面。比如你通过脚本获取这些样式:
offsetTop, offsetLeft, offsetWidth, offsetHeight
scrollTop/Left/Width/Height
clientTop/Left/Width/Height
getComputedStyle(), or currentStyle in IE
因为浏览器必须给你最新的值,所以当你进行这些取值操作的时候会立刻触发一次页面的绘制。这样本来可以批量修改样式然后一次性绘制的方法就无法使用了。
最后来说以下解决办法:
1.不要一个一个地单独修改属性,最好通过一个classname来定义这些修改
2.把对节点的大量修改操作放在页面之外,用 documentFragment来做修改,clone 节点,在clone之后的节点中做修改,然后直接替换掉以前的节点。
通过 display: none 来隐藏节点(直接导致一次重排和重绘),做大量的修改,然后显示节点(又一次重排和重绘),总共只会有两次重排。
3.不要频繁获取计算后的样式。如果你需要使用计算后的样式,最好暂存起来而不是直接从DOM上读取。
4.减少table布局(话说,很少有用table布局的了吧 现在,都过时了....)
5.避免css表达式,(因为每当文档重新加载或者部分文档重新加载的时候,CSS表达式都会重新计算一次,因此其性能会受到非常大的影响。)
6.总的来说,总是考虑到渲染树得存在,考虑到你的一次修改会导致多大的绘制操作。(比如绝对定位元素的动画就不会影响其他大部分元素)。