当前位置: 首页 > Web前端 > HTML5

关于重绘和重排你应该知道的

时间:2023-04-05 12:43:27 HTML5

前言大多数现代Web框架都是数据驱动的,例如React、Vue,因此开发者不需要直接接触DOM,可以通过修改数据来驱动界面更新。但是作为一个前端工程师,了解浏览器重绘重排还是很有必要的,这可以帮助我们写出性能更好的web应用。浏览器渲染CSSTree:浏览器将CSS解析成CSSOM的树状结构DOMTree:浏览器将HTML解析成树状的数据结构RenderTree:将DOM和CSSOM合并成渲染树有了渲染树(RenderTree),浏览器知道网页中有哪些节点,以及每个节点与CSS的关系,从而知道每个节点的位置和几何属性,然后绘制页面。重绘和重排当DOM的变化影响到元素的几何属性(如width和height)时,会导致浏览器重新计算元素的几何属性,受该元素影响的其他元素也会重新计算。此时,浏览器使渲染树受影响的部分失效并重建渲染树。这个过程叫做回流(也叫“回流”)(reflow)。回流完成后,浏览器会将受影响的部分重新绘制到页面中。这个过程称为重绘。所以重排一定会引起重绘,重绘不一定会引起重排。例如,元素的变化不会影响布局的变化(背景颜色的变化)。在这种情况下,只会发生一次重绘(不需要重新排列)。引起重排的因素可以归纳为当元素的几何属性或页面布局发生变化时,就会引起重排,例如:对可见DOM元素的操作(增删改序)元素位置改变元素的几何属性发生变化(例如:外边距、内边距、边框宽度以及内容变化引起的宽高变化)页面优先渲染伪类样式激活(悬停等)浏览器视口大小变化(滚动或缩放)如何优化重绘Reflow和reflow是昂贵的操作(因为每次reflow都会产生计算成本),并且它们会导致Web应用程序的UI无响应,因此开发人员在编写应用程序时应尽量减少此类过程的发生。渲染树队列可能会因为过多的重绘和重排导致应用卡顿,所以浏览器会对此有一个优化过程。大多数浏览器会通过排队的方式进行批量执行(比如将脚本对DOM的修改放入一个队列中,待队列中的所有操作完成后再进行绘制)。但是,开发人员有时可能会在不知不觉中强制刷新渲染队列以立即重新排序和重绘。例如,获取页面布局信息会导致渲染队列的强制刷新。以下属性或方法会立即触发页面绘制:offsetTop,offsetLeft,offsetWidth,offsetHeightscrollTop,scrollLeft,scrollWidth,scrollHeightclientTop,clientLeft,clientWidth,clientHeightgetComputedStyle()以上属性和方法都是为了浏览器返回最新的布局信息,所以浏览器会立即执行渲染队列中的“pendingchanges”并触发重绘并返回最新值。因此,在修改样式的过程中,应尽量避免使用上述属性和方法。减少重绘和重排为了减少重绘和重排的次数,开发者应该将对DOM的多次修改和对样式的修改合并起来,然后一次处理。合并样式操作如:varel=document.querySelector('div');el.style.borderLeft='1px';el.style.borderRight='2px';el.style.padding='5px';可以合并为:varel=document.querySelector('div');el.style.cssText='border-left:1px;右边框:1px;填充:5px;'批量修改DOM,让元素脱离文档流,然后修改操作,再将元素带回文档,这种方式可以有效减少重绘和重排的次数。有三种基本方法可以将元素从文档流中取出:隐藏元素、应用更改、重新显示varul=document.querySelector('ul');ul.style.display='none';//code...forulDOM操作ul.style.display='block';使用文档片段(documentfragment),构造一个空白文档进行DOM操作,然后放回原文档中querySelector('ul');ul.appendChild(fragment)将要修改的元素复制到文档流外的一个节点,修改副本,然后替换原来的元素varul=document.querySelector('ul');varcloneUl=ul.cloneNode(true);//代码...对clone节点进行DOM操作ul.parentNode.replaceChild(cloneUl,ul)之前已经知道缓存布局信息,并且获取到页面布局信息,会导致浏览器强行刷新渲染队列。因此,减少这些操作是非常必要的。开发者可以将第一次获取的页面信息缓存到局部变量中,然后对局部变量进行操作,例如下面的伪代码示例://Inefficientelement.style。left=1+element.offsetLeft+'px';element.style.top=1+element.offsetTop+'px';if(element.offsetTop>500){stopAnimation();}//高效varoffsetLeft=element.offsetLeft;varoffsetTop=element.offsetTop;offsetLeft++;offsetTop++;element.style.left=offsetLeft+'px';element.style.top=offsetTop+'px';if(offsetTop>500){stopAnimation();}总结为了减少重绘和重排带来的性能消耗,Web应用可以通过以下几点进行改进:批量修改DOM和样式,“离线”操作DOM树,与文档流分离并缓存在局部变量中,以及减少访问次数页面布局信息参考高性能JavaScript