前言,最近在碎片化的时间里又看了Nicholas《高性能JavaScript》的另一部巨作。今天的文章从“老土”的页面重绘和重排开始,探讨这两个概念在提升页面性能方面的作用。1.Rearrangement&Redrawing有经验的高手一定很熟悉这个概念,“浏览器输入URL时会发生什么”。估计大家已经很熟悉了,从计算机网络到JS引擎,一直到浏览器渲染引擎。经历越多,理解越深。有兴趣的同学可以看看这篇文章。深度和广度都很棒。从输入URL到页面加载的过程?如何从一道题提升你的前端知识体系!回到正题,我们继续讨论什么是重排。浏览器下载完页面的所有资源后,就会开始构建DOM树,同时也会构建RenderTree。(其实在渲染树构建之前,StyleTree会和DOM树同时构建,DOM树和StyleTree合并成一个渲染树)DOM树代表了页面的结构。渲染树表示页面的节点如何显示。一旦渲染树构建完成,它就会开始绘制(paint)页面元素。当DOM的变化导致元素的几何属性发生变化时,比如改变元素的宽高、元素的位置等,浏览器不得不重新计算元素的几何属性,重建渲染树。这个过程称为“重排”。重排完成后,应该将重构的渲染树渲染到屏幕上。这个过程称为“重绘”。简单来说,重排负责更新元素的几何属性,重绘负责更新元素的样式。而且,重排必然带来重绘,但重绘不一定带来重排。例如,更改元素的背景不涉及元素的几何属性,因此只发生重绘。2.重排触发机制上面已经说了。重新排列的基本原理是元素的几何属性发生了变化。那么我们可以从改变元素的几何属性的角度来增加或删除可见的DOM元素。改变元素本身页面大小改变,内容改变,页面渲染器初始化,浏览器窗口大小改变。计算页面需要很多开销。这样的话,用户在使用我们的页面时,就会出现明显的卡顿。现在的浏览器其实已经对重排进行了优化,比如下面的代码:高度='300px';对于旧版浏览器,这段代码会触发两次页面重排。分别设置宽高时,会触发两次。现代浏览器对此进行了优化。这个想法类似于流行的MVVM框架使用的虚拟DOM。它收集对更改的DOM节点的依赖项,确认没有更改的节点,然后执行更新。不过,虽然浏览器对重排的优化与虚拟DOM类似,但还是有本质的区别。大多数浏览器通过排队更改并分批执行它们来优化回流过程。也就是说,上面的代码实际上只是构成了当前浏览器优化下的重排。但是,仍然有一些特殊的元素几何属性会导致此优化失败。例如:offsetTop,offsetLeft,...scrollTop,scrollLeft,...clientTop,clientLeft,...getComputedStyle()(IE中为currentStyle)为什么会优化失败?仔细看这些属性,它们都需要实时反馈给用户Geometry属性或者布局属性,当然不能再依赖浏览器优化了,所以浏览器不得不立即在渲染队列中执行“pendingchanges”,并且然后触发回流以返回正确的值。接下来,我将深入介绍几个性能优化的小TIPS。3.1尽量减少重绘和重绘由于重绘和重绘会影响页面的性能,尤其是糟糕的JS代码会导致重排序放大带来的性能问题。既然如此,我们首先想到的就是减少重排和重绘。3.1.1.更改样式请考虑以下示例://javascriptvarel=document.querySelector('.el');el.style.borderLeft='1px';el.style.borderRight='2px';el.style.padding='5px';这个例子其实和上面的例子是一样的。最坏的情况下,会触发浏览器回流3次。然而,一种更有效的方法是将所有更改合并到一个进程中。这只会修改DOM节点一次,例如改用cssText属性:varel=document.querySelector('.el');el.style.cssText='border-left:1px;border-right:2px;padding:5像素';按照这个思路,聪明的老铁肯定说了,你直接改类名不合适。没错,另一个减少reflow的方法就是切换类名,而不是使用内联样式的cssText方法。使用切换类名称变为://css.active{padding:5px;border-left:1px;border-right:2px;}//javascriptvarel=document.querySelector('.el');el.className='积极的';3.1.2批量修改DOM如果我们需要多次修改DOM元素,如何减少重新排列和重绘的次数?有同学要说了,用上面修改样式的方法还不行呢。回顾一下导致页面重排的要点,可以清楚地看到,当元素的几何属性发生变化时,就会触发重排。现在我们需要增加10个节点,这必然涉及到DOM的修改。这时候,我们就需要采用批量修改DOM的优化方式。这里也可以看出,改变样式最小化重绘重排的优化方法适用于单个已有节点。批量修改DOM元素的核心思想是:让元素脱离文档流,对其进行多次修改,将元素带回文档中,测试有问题,修复后再安装。如果直接在主板上用螺丝刀,估计主板很快就坏了……这个过程会造成两次重排,第一步和第三步。如果没有这两个步骤,你可以想象在第一步和第二步中,对DOM的每次添加或删除都会引起重新排列。那么,了解了批量修改DOM的核心思想之后,我们就来了解一下三种可以让元素脱离文档流的方法。请注意,这里没有使用CSS中的浮动和绝对定位,这是一个无关紧要的概念。隐藏元素,修改,再显示元素使用文档片段创建子树,然后复制到文档中将原来的元素复制到一个单独的节点,操作这个节点,然后覆盖原来的元素看看在下面的代码示例中://htmlxiaomiui//javascript现在需要添加一个li节点,其中包含以下信息letdata=[{name:'tom',url:'https://www.baidu.com',},{name:'ann',url:'https://www.techFE.com'}]首先我们写一个通用的更新新数据到指定节点的方法://javascriptfunctionappendNode($node,data){vara,li;for(leti=0,max=data.length;i
