
继续上篇《浏览器地址栏里输入URL后的全过程》
前言
为什么要了解浏览器的渲染原理?了解浏览器的渲染原理有什么好处?我们做前端开发为什么非要了解浏览器的原理?直接把网页做出来,什么需求,直接一把梭,撸完收工不好吗。
但是经常会有人会问,什么是重排和重绘?
重排也叫回流(Reflow),重绘(Repaint),会影响到浏览器的性能,给用户的感觉就是网页访问慢,或者网页会卡顿,不流畅,从而使网页访问量下降。
所以,想要尽可能的避免重排和重绘,就需要了解浏览器的渲染原理。
浏览器工作流程

上图我们可以看出,浏览器会解析三个模块:
HTML,SVG,XHTML,解析生成DOM树。CSS解析生成CSS规则树。JavaScript用来操作DOM API和CSSOM API,生成DOM Tree和CSSOM API。
解析完成后,浏览器会通过已经解析好的DOM Tree 和 CSS规则树来构造 Rendering Tree。
-
Rendering Tree渲染树并不等同于DOM树,因为一些像Header或display:none的东西就没必要放在渲染树中了。 -
CSS的Rule Tree主要是为了完成匹配并把CSS Rule附加上Rendering。 -
Tree上的每个Element。也就是DOM结点,即Frame。然后,计算每个Frame(也就是每个Element)的位置,这又叫layout和reflow过程。 -
最后通过调用操作系统
Native GUI的API绘制。
不同内核的浏览器渲染

webkit内核的渲染流程,和总体渲染流程差不多,要构建HTML的DOM Tree,和CSS规则树,然后合并生成Render Tree,最后渲染。

Mozilla的Gecko渲染引擎。总体看来渲染流程差不多,只不过在生成渲染树或者
Frame树时,两者叫法不一致,webkit称之为Layout,Gecko叫做Reflow。
渲染顺序

- 当浏览器拿到一个网页后,首先浏览器会先解析
HTML,如果遇到了外链的css,会一下载css,一边解析HTML。 - 当
css下载完成后,会继续解析css,生成css Rules tree,不会影响到HTML的解析。 - 当遇到
<script>标签时,一旦发现有对javascript的引用,就会立即下载脚本,同时阻断文档的解析,等脚本执行完成后,再开始文档的解析。

- 当
DOM树和CSS规则树已经生成完毕后,构造Rendering Tree。 - 调用系统渲染页面。
什么情况会造成重排和重绘。
重排意味着元件的几何尺寸变了,我们需要重新验证并计算Render Tree。是Render Tree的一部分或全部发生了变化。这就是Reflow,或是Layout。
重排因为要重新计算Render Tree,而且每一个DOM Tree都有一个reflow方法,一旦某个节点发生重排,就有可能导致子元素和父元素甚至是同级其他元素的reflow,浪费大量的时间重新验证Render Tree。
因此,重排的成本要比重绘高很多。
以下操作会导致重排或重绘。
- 删除,增加,或者修改
DOM元素节点。 - 移动
DOM的位置,开启动画的时候。 - 修改
CSS样式,改变元素的大小,位置时,或者将使用display:none时,会造成重排;修改CSS颜色或者visibility:hidden等等,会造成重绘。 - 修改网页的默认字体时。
- Resize窗口的时候(移动端没有这个问题),或是滚动的时候。
- 内容的改变,(用户在输入框中写入内容也会)。
- 激活伪类,如:hover。
- 计算
offsetWidth和offsetHeight。
如果当前网页含有一些动画,或者固定不动元素的网页时,由于滚动也会发生重排,一旦发生滚动,当前浏览器所承受的压力很大,就会造成网页的卡顿,掉帧等情况。
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!'));
以上逻辑,几乎每一步都会造成重排或重绘,如果浏览器像这样处理的话,可能现代的浏览器没有我们使用的那么流畅了。
因此浏览器有一个机制,会把需要重排或重绘的先积累着,然后一次性进行重排和重绘。
当然,不是所有的情况浏览器都是这样处理的,比如resize或者修改默认字体,对于这些操作,浏览器会立马进行重排。
所以我们在监听resize事件时,一般我们都会做防抖和节流。
如何减少重排和重绘
- 尽量避免
style的使用,对于需要操作DOM元素节点,重新命名className,更改className名称。 - 如果增加元素或者
clone元素,可以先把元素通过documentFragment放入内存中,等操作完毕后,再appendChild到DOM元素中。 - 不要经常获取同一个元素,可以第一次获取元素后,用变量保存下来,减少遍历时间。
- 尽量少使用
dispaly:none,可以使用visibility:hidden代替,dispaly:none会造成重排,visibility:hidden会造成重绘。 - 不要使用
Table布局,因为一个小小的操作,可能就会造成整个表格的重排或重绘。 - 使用
resize事件时,做防抖和节流处理。 - 对动画元素使用
absolute / fixed属性。 - 批量修改元素时,可以先让元素脱离文档流,等修改完毕后,再放入文档流。
最后
参考文章: