页面渲染过程和面试题解析
1.浏览器的渲染是怎么进行的呢?这里用简单的语言描述一下
从浏览器获取到页面内容(html,css,js)开始
1.浏览器会将html代码解析成DOM树
2.浏览器会将css代码解析成CSSOM树
3.结合DOM树和CSSOM树,生成一棵render树
4.将render树的所有节点进行平面合成(重排/回流)
5.绘制页面到屏幕上(重绘)
1.1将html代码解析成DOM树
浏览器获取到一段html代码,是解析成什么样的DOM树?
例如从浏览器读到html开始,当然真实的DOM树是并没有下面那么简单,便于理解
html
<div class="wrapper">
<p>hello</p>
</div>
var dom = {
target:{
el:'div',
class:'wrapper',
children:[
{
el:'p',
class:'',
value:'hello'
}
]
}
}
1.2将css代码解析成CSSOM树
浏览器获取一段css代码,解析成CSSOM树,这个过程和解析DOM树是相似的。
1.3将DOM树和CSSOM树结合成render树
css
<style>
div{
width: 100;
height: 100;
}
</style>
html
<div class="wrapper">
<p>hello</p>
</div>
var dom = {
target:{
el:'div',
class:'wrapper',
style:{
width: 100;
height: 100;
}
children:[
{
el:'p',
class:'',
value:'hello'
}
]
}
}
1.4将render树的所有节点进行平面合成——重排(回流)
浏览器进行这一步时,页面布局完成,但是页面还没有被渲染到页面
1.5绘制页面到屏幕上 (render-UI)
在事件循环中,微任务队列执行完成,不会立即执行宏任务,之间如果有页面渲染则执行页面渲染(render-UI)
2.重排(回流)和重绘
2.1重排(回流)
重新生成新的布局,重新排列元素
什么情况会发生重排?
所有导致元素几何信息发生变化的操作,都会引起重排,例如:
- window大小被修改
- 增加删除dom结构
- 元素尺寸发生改变
- offsetWidth 和 offsetHeight, offset ... ,clientWidth,client...,scrollTop,screll ...
- 等等
2.2重绘:重新绘制页面
所有导致元素非几何信息发生变化的操作,都会引起重绘
重排一定会重绘,重绘不一定会重排
3.浏览器的优化策略
那么这个过程经历了几次重绘和重排?
let el = document.querySelector('div')
el.style.width = (el.offsetWidth + 1) + 'px'
el.style.width = 1 + 'px'
我们知道 offsetWidth 会触发重排, 元素尺寸发生改变会触发重排, 而发生重排又会发生重绘,所以回答3次重排,3次重绘。
面试官:恭喜你! 0 分
当改变元素的几何信息发生变化,导致重排发生,浏览器提供一个渲染队列用于临时存储该次重排,浏览器继续执行代码, 如果还有几何信息发生变化,继续入队,直至代码结束,然后浏览器会按照渲染队列来批量优化重排过程。如果途中出现了offsetWidth ... 等计算属性,则会立即对渲染队列进行刷新,重排一次
所以这题,在offsetWidth计算时,渲染队列是空的。所以offsetWidth进入渲染队列。最后,后续对几何信息的修改依次进入渲染队列。最后发生了1次重排,1次重绘
4.减少重排的操作
. 尽量避免style的使用,对于需要操作DOM元素节点,重新命名className,更改className名称。\
· 如果增加元素或者clone元素,可以先把元素通过documentFragment放入内存中,等操作完毕后,再appendChild到DOM元素中。\
· 不要经常获取同一个元素,可以第一次获取元素后,用变量保存下来,减少遍历时间。\
· 尽量少使用dispaly:none,可以使用visibility:hidden代替,dispaly:none会造成重排,visibility:hidden会造成重绘。\
· 不要使用Table布局,因为一个小小的操作,可能就会造成整个表格的重排或重绘。\
.使用resize事件时,做防抖和节流处理。\
.对动画元素使用absolute / fixed属性。\
.批量修改元素时,可以先让元素脱离文档流,等修改完毕后,再放入文档流。