标签:
首先,你应该了解的就是,浏览器是如何渲染一个页面的。
先看一个大致的流程图
它的总体流程是这样的:
1)浏览器解析这三个东西:
2)解析完成后,浏览器会根据DOM树和CSS Rule树来构造渲染树(Rendering Tree)。
3)最后通过调用操作系统Native GUI的API绘制(painting)。
抛去其中的细节,再简单一点的说法就是:DOM树解析->css解析->渲染(也就是构建渲染树以及最终呈现到浏览器上的过程)
这里 主要针对第三步的渲染过程进行一下讲解:
需要注意的是:Javascript如果动态修改了DOM属性或是CSS属会导致重新Layout(Reflow),当然有些属性改变不会。
这里,这里重要要说两个概念,一个是Reflow,另一个是Repaint
Repaint(重绘)
当在页面上修改了一些不需要改变定位的样式的时候(比如background-color
,border-color
,visibility
),浏览器只会将新的样式重新绘制给元素(这就叫一次“重绘”或者“重新定 义样式”)。这时只需要屏幕的一部分要重画。
Reflow(重排)
当页面上的改变影响了文档内容、结构或者元素定位时,就会发生重排(或称“重新布局”)。重排通常由以下改变触发:
这时,我们需要重新验证并计算Render Tree。是Render Tree的一部分或全部发生了变化。这就是Reflow,或是Layout。(HTML使用的是flow based layout,也就是流式 布局,所以,如果某元件的几何尺寸发生了变化,需要重新布局,也就叫reflow)reflow 会从<html>这个root frame开始递归往下,依次计算所有的结点几何尺寸和位置,在 reflow过程中,可能会增加一些frame,比如一个文本字符串必需被包装起来。
可以看出,这两个动作对于浏览器的性能都有较大的影响,当然reflow的成本比repaint的成本高好多。那么,浏览器又是如何避免成本增加,从而优化渲染的呢?
浏览器如何优化渲染?
1、浏览器尽最大努力限制重排
的过程仅覆盖已更改的元素的区域。举个例子,一个 position 为 absolue 或 fixed 的元素的大小变化只影响它自身和子孙元素,而对一个 position 为 static 的元素做同样的操作就会引起所有它后面元素的重排。
2、当运行一段Jjavascript 代码的时候,浏览器会将一些修改缓存起来,然后当代码执行的时候,一次性的将这些修改执行。举例来说,这段代码会触发一次重绘和一次重排:
var bstyle = document.body.style; // cache bstyle.padding = "20px"; // reflow, repaint bstyle.border = "10px solid red"; // 再一次的 reflow 和 repaint bstyle.color = "blue"; // repaint bstyle.backgroundColor = "#fad"; // repaint bstyle.fontSize = "2em"; // reflow, repaint // new DOM element - reflow, repaint document.body.appendChild(document.createTextNode(‘dude!‘));
浏览器不会像上面那样,你每改一次样式,它就reflow或repaint一次。一般来说,浏览器会把这样的(都是设置style属性,而不涉及其他类似读取属性的操作)操作积攒一批,然后做一次reflow,这又叫异步reflow或增量异步reflow。但是有些情况浏览器是不会这么做的,比如:resize窗口,改变了页面默认的字体,等。对于这些操作,浏览器会马上进行reflow。
但是有些时候,我们的脚本会阻止浏览器这么干,比如:如果我们请求下面的一些DOM值:(比如我们在上面的例子中若加一个读取属性的操作则会引起又一次的重排)
因为,如果我们的程序需要这些值,那么浏览器需要返回最新的值,而这样一样会flush出去一些样式的改变,从而造成频繁的reflow/repaint。
当然,我们可以通过改变书写习惯而做一些认为的性能优化:
实际优化建议
例如:
1、使用documentFragment 对象在内存里操作DOM,类似以下的代码示例:
// Create the fragment var fragment = document.createDocumentFragment(); //add DOM to fragment for(var i = 0; i < 10; i++) { var spanNode = document.createElement("span"); spanNode.innerHTML = "number:" + i; fragment.appendChild(spanNode); } //add this DOM to body document.body.appendChild(spanNode);
2、先把DOM给display:none(有一次reflow),然后你想怎么改就怎么改。比如修改100次,然后再把他显示出来。
3、clone一个DOM结点到内存里,然后想怎么改就怎么改,改完后,和在线的那个的交换一下
:hover
动画是一个很好的主意(例如,给 body 标签加一个 no-hover 的 class标签:
原文地址:http://www.cnblogs.com/qqqiangqiang/p/5757656.html