浏览器渲染页面的流程
解析HTML文档,构建DOM树:
过程:
读取html文档 -> 将字节转成字符串 -> 确定标签 -> 将标签转成节点
注:(js会对DOM节点未进行操作,浏览器无法预测未来,DOM节点的具体内容为防止无效操作,节省资源,只能阻塞DOM树构建)
构建渲染(render)树:
解析对立的css样式文件信息,(包括JS生成的样式和外部css文件)
注:(由DOM树,CSSOM树合并而成,并不是等DOM树,CSSOM树加载完成后才开始合并构建渲染树,三者构建 无先后条件,一边加载,一边解析,一边渲染)
布局渲染树:
从根节点递归调用,计算每个元素的大小位置,给出每个节点所应该放在屏幕上出现的精确坐标
绘制渲染树:
遍历渲染树,使用UI后端层来绘制每个节点
(UI后端层:用来绘制类似组合选择框及对话框等基本组件,具有不特定于莫格平台的通用接口,底层使用操作系统的用户接口)
影响页面渲染速度主要有:重排(重构/回流/reflow)和重绘(repaint或redraw)
重排(重构/回流/reflow):当渲染树中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建, 这就称为回流(reflow)。每个页面至少需要一次回流,就是在页面第一次加载的时候。
重绘(repaint或redraw):当盒子的位置、大小以及其他属性,例如颜色、字体大小等都确定下来之后,浏览器便把这些原色都按照各自的特性绘制一遍,将内容呈现在页面上。一个元素外观的改变所触发的浏览器行为,浏览器会根据元素的新属性重新绘制,使元素呈现新的外观。触发重绘的条件:改变元素外观属性。
重绘和重排的关系:在回流的时候,浏览器会使渲染树中受到影响的部分失效,并重新构造这部分渲染树,完成回流后,浏览器会重新绘制受影响的部分到屏幕中。
所以,重排必定会引发重绘,但重绘不一定会引发重排。
发重排的条件:任何页面布局和几何属性的改变都会触发重排
1、页面渲染初始化;(无法避免)
2、添加或删除可见的DOM元素;
3、元素位置的改变,或者使用动画;
4、元素尺寸的改变:大小,外边距,边框;
5、浏览器窗口尺寸的变化(resize事件发生时);
6、填充内容的改变,比如文本的改变或图片大小改变而引起的计算值宽度和高度的改变;
7、读取某些元素属性:(offsetLeft/Top/Height/Width, clientTop/Left/Width/Height, scrollTop/Left/Width/Height, width/height, getComputedStyle(), currentStyle(IE) )
重绘重排的代价:耗时,导致浏览器卡慢。
优化:
1、浏览器自己的优化:浏览器会维护1个队列,把所有会引起回流、重绘的操作放入这个队列,等队列中的操作到了一定的数量或者到了一定的时间间隔,浏览器就会flush队列,进行一个批处理。这样就会让多次的回流、重绘变成一次回流重绘。
2、我们要注意的优化:我们要减少重绘和重排就是要减少对渲染树的操作,则我们可以合并多次的DOM和样式的修改。并减少对style样式的请求。
- 直接改变元素的className
- display:none;先设置元素为display:none;然后进行页面布局等操作;设置完成后将元素设置为display:block;这样的话就只引发两次重绘和重排;
- 不要经常访问浏览器的flush队列属性;如果一定要访问,可以利用缓存。将访问的值存储起来,接下来使用就不会再引发回流;
- 使用cloneNode(true or false) 和 replaceChild 技术,引发一次回流和重绘;
- 将需要多次重排的元素,position属性设为absolute或fixed,元素脱离了文档流,它的变化不会影响到其他元素;
- 如果需要创建多个DOM节点,可以使用DocumentFragment创建完后一次性的加入document;