上次在面试中,面试官问了我一个关于页面重绘和回流的问题,我解释的不怎么好。今天把它整理了一下,又参考了一些其他的文章。
参考文章:http://www.css88.com/archives/4996
页面加载过程
1、浏览器将获取到的HTML代码解析成一个DOM树,HTML中的每一个标签都是DOM树中的一个节点,根节点就是document。DOM树中包含了所有的HTML标签,同时也包含了display:none 隐藏和JS动态添加的元素等
2、浏览器将所有的样式(CSS)解析成样式结构体(CSS Model),在解析过程中会去掉浏览器不识别的样式。
3、DOM Tree 和样式结构体组合后构建render tree。 render tree类似于DOM tree。但是有较大差别。render树中的每个节点都有自己的样式,但是render tree中不包含display:none的元素。但是visiblity:hidden的隐藏元素会包含在render Tree中。因为visibility:hidden会影响布局(layout),且在文档流中占据一定的空间。
4、一旦render Tree 构建完毕,浏览器就可以根据render tree绘制页面了
回流与重绘
1、什么是回流?
当render tree 中的一部分(或者全部)因为元素的规模尺寸、布局隐藏等改变需要重新构建。这就是回流。
2、什么是重绘?
在回流的时候,浏览器会使渲染树中受影响的部分失效,并重新绘制这部分的渲染树,完成回流后,浏览器会重新绘制受影响的部分到屏幕中,该过程称为重绘
3、什么情况下浏览器会发生重绘?
重绘不一定引起回流,但是回流一定会影响重绘。只是改变某些元素的属性并且这些属性会影响到元素的外观、风格,不会影响页面的布局,比如backgorund-color;color等;visability:hidden只重绘不回流
4、什么情况下,浏览器会发生回流?(重要)
当页面布局或者几何大小发生变化时就会发生回流。
①添加或删除可见的DOM元素;
②元素的位置变化
③元素的尺寸变化--内容区域、边框、内外边距、宽高
④内容改变--文本内容改变或者图片大小改变而引起的计算值宽度和高度改变
⑤页面渲染的初始化
⑥浏览器可视窗口变化---resize时间发生时、或者浏览器的字体大小改变时(用户操作)
聪明的浏览器 (见文章 http://www.css88.com/archives/4996;写的很好)
如何优化页面?
减少回流和重绘就是减少对render tree的操作(合并多次DOM修改和样式修改),减少对一些元素style的请求。具体方法如下:
1、同时添加子元素和父元素时,应先在内存中将子元素拼接到父元素中,最后在整体一次性将父元素添加到文档流中---这样只会发生一次layout
2、如果多次同时添加多个平级元素时,应该使用文档片段
什么是文档片段:内存中临时存储的多个平级子元素的虚拟父元素
何时使用: 只要是添加多个平级的子元素时,都要先将子元素加入到文档片段中,再一次性将文档片段添加到DOM树
实例如下:
使用文档片段
<ul id="container"> </ul> <script type="text/javascript"> var container = document.getElementById('container'); console.time(); var fragment = document.createDocumentFragment(); for(var i = 0;i<50000;i++){ fragment.appendChild(document.createElement('li')); } container.appendChild(fragment); console.timeEnd(); </script>
//总耗时default: 76.31201171875ms
不使用文档片段
<script type="text/javascript">
var container = document.getElementById('container');
console.time();
for(var i = 0;i<50000;i++){
container.appendChild( document.createElement('li'))
}
console.timeEnd();
</script>
//default: 88.941162109375ms
3、直接改变className,如果动态改变样式,可以使用cssText
4、由于display属性为none的元素不在渲染树中,对隐藏的元素操作不会引发其他元素的重排。如果要对一个元素进行复杂的操作时,可以先隐藏它,操作完成后再显示。这样只在隐藏和显示时触发2次重排。
5、使用cloneNode(true/false)和replaceChild 技术,引发一次回流和重绘
6、不要经常访问会引起浏览器flush队列的属性,如果确实要访问的话,可以利用缓存;也就是说需要经常去那些引起浏览器重排的属性时,需要缓存到变量中,再调用。
7、让元素脱离动画流,减少回流的Render Tree的规模