浏览器的渲染过程
1.解析HTML,生成DOM树
2.解析CSS,生成CSS规则树(CSSOM)
3.合并DOM和CSSOM,生成渲染树(Render-Tree)
4.计算渲染树的布局(Layout)
5.将布局渲染到屏幕上(paint)
什么是重排和重绘
重排:当DOM的变化引发了元素几何属性的变化,比如改变元素的宽高,元素的位置,导致浏览器不得不重新计算元素的几何属性,并重新构建渲染树,这个过程称为“重排”。
重绘:当各种盒子的位置、大小以及其他属性,例如颜色、字体大小等属性都确定下来后,浏览器根据他们的特性重新绘制一遍时,就叫重绘
简单来说,涉及元素的几何更新时,叫重排。而只涉及样式更新而不涉及几何更新时,叫重绘。对于两者来说,重排必定引起重绘,但是重绘并不一定引起重排。所以,当涉及重排时,浏览器会将上述的步骤再次执行一遍。
什么操作会引起重排和重绘
显然,触发重排的一般都是几何因素,这是比较好理解的:
- 页面第一次渲染 在页面发生首次渲染的时候,所有组件都要进行首次布局,这是开销最大的一次重排
- 浏览器窗口尺寸改变
- 元素位置和尺寸发生改变的时候
- 新增和删除可见元素
- 内容发生改变(文字数量或图片大小等等)
- 元素字体大小变化
- 还有其他一些操作也可能引发重排
查询某些属性或调用某些方法
- offset(Top|Left|Width|Height)
- scroll(Top|Left|Width|Height)
- client(Top|Left|Width|Height)
- getComputedStyle()
我们可能不太理解为什么这些操作也能引起重排,这里我先简单解释一下。因为现在的浏览器已经非常完善了,会自动帮我们做一些优化。当我们用js操作DOM的时候,浏览器并不是立马执行的,而是将操作存储在一个队列中。当达到一定数量或者经过一定时间以后浏览器再统一的去执行队列中的操作。那么回到我们刚才的问题,为什么查询这些属性也会导致重排?因为当你查询这些属性时,浏览器就会强制刷新队列,因为如果不立马执行队列中的操作,有可能得到的结果就是错误的。所以相当于你强制打断了浏览器的优化流程,引发了重排。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> #test { width: 100px; height: 100px; background-color: red; position: relative; } </style> </head> <body> <div id="test"> </div> <button onclick="reflow()">click</button> <script> function reflow() { var div = document.querySelector("#test"); console.log(div.offsetLeft); } </script> </body> </html>
上述代码,当我们点击按钮后,浏览器立马重新计算CSSOM,但是并没有触发重排
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> #test { width: 100px; height: 100px; background-color: red; position: relative; } </style> </head> <body> <div id="test"> </div> <button onclick="reflow()">click</button> <script> function reflow() { var div = document.querySelector("#test"); div.style.left = '200px'; console.log(div.offsetLeft); div.style.left = '100px'; console.log(div.offsetLeft); div.style.left = '200px'; console.log(div.offsetLeft); div.style.left = '100px'; console.log(div.offsetLeft); } </script> </body> </html>
上述代码触发了4次重排
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> #test { width: 100px; height: 100px; background-color: red; position: relative; } </style> </head> <body> <div id="test"> </div> <button onclick="reflow()">click</button> <script> function reflow() { var div = document.querySelector("#test"); div.style.left = '200px'; div.style.left = '100px'; div.style.left = '200px'; div.style.left = '100px'; console.log(div.offsetLeft); console.log(div.offsetLeft); console.log(div.offsetLeft); console.log(div.offsetLeft); } </script> </body> </html>
上述代码只触发了一次重排