首先,你应该了解的就是,浏览器是如何渲染一个页面的。javascript
先看一个大体的流程图css
它的整体流程是这样的:html
1)浏览器解析这三个东西:java
2)解析完成后,浏览器会根据DOM树和CSS Rule树来构造渲染树(Rendering Tree)。浏览器
3)最后经过调用操做系统Native GUI的API绘制(painting)。缓存
抛去其中的细节,再简单一点的说法就是:DOM树解析->css解析->渲染(也就是构建渲染树以及最终呈现到浏览器上的过程)性能优化
这里 主要针对第三步的渲染过程进行一下讲解:app
须要注意的是: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的成本高好多。那么,浏览器又是如何避免成本增长,从而优化渲染的呢?
浏览器如何优化渲染?
一、浏览器尽最大努力限制重排
的过程仅覆盖已更改的元素的区域。举个例子,一个 position 为 absolue 或 fixed 的元素的大小变化只影响它自身和子孙元素,而对一个 position 为 static 的元素作一样的操做就会引发全部它后面元素的重排。
二、当运行一段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。
固然,咱们能够经过改变书写习惯而作一些认为的性能优化:
实际优化建议
例如:
一、使用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);
二、先把DOM给display:none(有一次reflow),而后你想怎么改就怎么改。好比修改100次,而后再把他显示出来。
三、clone一个DOM结点到内存里,而后想怎么改就怎么改,改完后,和在线的那个的交换一下
:hover
动画是一个很好的主意(例如,给 body 标签加一个 no-hover 的 class