网页重排(reflow)是阻碍流畅度的重要原因之一。结合Whatforceslayout/reflow一文和参考资料,梳理一下reflow产生的原因和优化思路。借用这张经典图:网页渲染会经过DOM->CSSOM->Layout(重排或回流)->Paint(重绘)->Composite(合成),其中Composite在精读中已经详细介绍了《深入了解现代浏览器四》,在GPU中进行光栅化。所以排除掉JS、DOM、CSSOM、Composite可能带来的性能问题,剩下的就是我们这次关注的重点,reflow。从顺序可以看出,重排之后一定要重绘,重绘不一定会触发重排。overview什么时候触发Layout(reflow)?一般来说,当元素的位置发生变化时。但也不一定,因为浏览器会自动合并变化,达到一定数量或时间后,会合并成回流,而回流是渲染页面的重要步骤,打开浏览器肯定至少会回流一次,所以我们无法避免回流。那么为什么要关注回流带来的性能问题呢?这是因为有些代码可能会导致浏览器优化失败,即reflow可以合并的时候不合并。这通常发生在我们使用jsAPI访问某个元素大小时。为了保证获取的值是准确的,我们要提前触发。回流,即使是写在for循环中。当然,并不是每次访问元素位置都会触发回流。浏览器触发回流后,所有存在的元素位置都会记录快照。只要不触发location等变化,第二次访问location时就不会触发reflow。关于这一点,后面会详细展开。我现在要说明的是,什么是“触发位置和其他变化”?根据Whatforceslayout/reflow文档的总结,有几类:getboxmodelinformationelem.offsetLeft,elem.offsetTop,elem.offsetWidth,elem.offsetHeight,elem.offsetParentelem.clientLeft,elem.clientTop,elem.clientWidth,elem.clientHeightelem.getClientRects(),elem.getBoundingClientRect()获取元素位置宽高的一些方法会导致回流,没有绕过,因为只要获取到这些信息,就必须使用回流来给出准确的值。滚动elem.scrollBy(),elem.scrollTo()elem.scrollIntoView(),elem.scrollIntoViewIfNeeded()elem.scrollWidth,elem.scrollHeightelem.scrollLeft,elem.scrollTop访问和赋值赋值给scrollLeft相当于触发scrollTo,都是resulting滚动产生的行为会触发回流。笔者查阅了一些资料。目前主要猜测是滚动条的出现会导致可见区域变窄,所以需要回流。focus()elem.focus()(sourcecode)大家可以根据源码看看注释,主要是这一段://Ensurewehavecleanstyle(includingforceddisplaylocks).GetDocument().UpdateStyleAndLayoutTreeForNode(this)聚焦元素时,虽然没有元素位置信息的需求,但是可能会隐藏或移除要聚焦的元素。这时必须调用UpdateStyleAndLayoutTreeForNode重排重绘函数,保证元素状态更新后才能继续操作。还有一些其他的元素API:elem.computedRole、elem.computedNameelem.innerText(源代码)innerText也需要重新排列才能得到正确的内容。获取窗口信息window.scrollX、window.scrollYwindow.innerHeight、window.innerWidthwindow.visualViewport.height/width/offsetTop/offsetLeft(源码)同elementlevel,为了获取正确的宽高和位置信息,它必须重新排列。document相关的document.scrollingElement只会重绘document.elementFromPointelementFromPoint,因为要获取元素的精确位置,必须重新排列。表单相关的inputElem.focus()inputElem.select(),textareaElem.select()focus,select触发重排的原因和elem.focus类似。mouseEvt.layerX,mouseEvt.layerY,mouseEvt.offsetX,mouseEvt.offsetY相关的鼠标事件(sourcecode)鼠标相关位置的计算必须依赖正确的排列,所以必须触发回流。getComputedStylegetComputedStyle通常会引起重排和重绘,是否触发重排取决于是否访问到位置相关键等因素。Range相关range.getClientRects(),range.getBoundingClientRect()获取选中区域的大小,必须回流保证精度。SVG大量的SVG方法会造成重排,就不一一列举了。总之,在使用SVG操作时,应该像操作dom一样谨慎。contenteditable设置为contenteditable元素,包括复制图片到剪贴板,大量操作会造成回流。(源码)精读Whatforceslayout/reflow下面引用几篇关于reflow的相关文章,笔者挑选几篇比较重要的进行总结。repaint-reflow-restylerepaint-reflow-restyle提到现代浏览器会合并多个dom操作,但是其他核心浏览器如IE不保证这样的实现,所以给出一个安全的写法://badvarleft=10,top=10;el.style.left=left+"px";el.style.top=top+"px";//更好的el.className+="theclassname";//或者当top和left被动态计算时...//betterel.style.cssText+=";left:"+left+"px;top:"+top+"px;";比如使用className修饰,或者cssText修饰,确保浏览器必须触发回流。但是这样会大大降低可维护性,所以不推荐。avoidlargecomplexlayoutsavoidlargecomplexlayouts强调读写分离,先看下面的坏情况:functionresizeAllParagraphsToMatchBlockWidth(){//使浏览器进入读写-读写循环。对于(vari=0;i
