blog icon indicating copy to clipboard operation
blog copied to clipboard

重绘和重排版

Open wuxianqiang opened this issue 5 years ago • 0 comments

当浏览器下载完所有页面HTML 标记,JavaScript,CSS,图片之后,它解析文件并创建两个内部数据结构:

  1. 一棵DOM 树:表示页面结构
  2. 一棵渲染树:表示DOM 节点如何显示

一旦DOM 树和渲染树构造完毕,浏览器就可以显示(绘制)页面上的元素了。

当DOM 改变影响到元素的几何属性(宽和高)——例如改变了边框宽度或在段落中添加文字,将发生 一系列后续动作——浏览器需要重新计算元素的几何属性,而且其他元素的几何属性和位置也会因此改变受到影响。浏览器使渲染树上受到影响的部分失效,然后重构渲染树。这个过程被称作重排版。重排版完成时,浏览器在一个重绘进程中重新绘制屏幕上受影响的部分。

不是所有的DOM 改变都会影响几何属性。例如,改变一个元素的背景颜色不会影响它的宽度或高度。在这种情况下,只需要重绘(不需要重排版),因为元素的布局没有改变。

重绘和重排版是负担很重的操作,可能导致网页应用的用户界面失去相应。所以,十分有必要尽可能减少这类事情的发生。

重排版时会发生什么?

正如前面所提到的,当布局和几何改变时需要重排版。在下述情况中会发生重排版:

  1. 添加或删除可见的DOM 元素
  2. 元素位置改变
  3. 元素尺寸改变(因为边距,填充,边框宽度,宽度,高度等属性改变)
  4. 内容改变,例如,文本改变或图片被另一个不同尺寸的所替代
  5. 最初的页面渲染
  6. 浏览器窗口改变尺寸

根据改变的性质,渲染树上或大或小的一部分需要重新计算。某些改变可导致重排版整个页面:例如,当一个滚动条出现时。

查询并刷新渲染树改变

因为计算量与每次重排版有关,大多数浏览器通过队列化修改和批量显示优化重排版过程。然而,你可能(经常不由自主地)强迫队列刷新并要求所有计划改变的部分立刻应用。获取布局信息的操作将导致刷新队列动作,这意味着使用了下面这些方法:

  1. offsetTop, offsetLeft, offsetWidth, offsetHeight
  2. scrollTop, scrollLeft, scrollWidth, scrollHeight
  3. clientTop, clientLeft, clientWidth, clientHeight
  4. getComputedStyle() (currentStyle in IE)(在IE 中此函数称为currentStyle)

在改变样式的过程中,最好不要使用前面列出的那些属性。任何一个访问都将刷新渲染队列,即使你正在获取那些最近未发生改变的或者与最新的改变无关的布局信息。

最小化重绘和重排版

重排版和重绘代价昂贵,所以,提高程序响应速度一个好策略是减少此类操作发生的机会。为减少发生次数,你应该将多个DOM 和风格改变合并到一个批次中一次性执行。

var el = document.getElementById('mydiv');
el.style.borderLeft = '1px';
el.style.borderRight = '2px';
el.style.padding = '5px';

这里改变了三个风格属性,每次改变都影响到元素的几何属性。在这个糟糕的例子中,它导致浏览器重排版了三次。大多数现代浏览器优化了这种情况只进行一次重排版。

一个达到同样效果而效率更高的方法是:将所有改变合并在一起执行,只修改DOM一次。可通过使用cssText 属性实现:

var el = document.getElementById('mydiv');
el.style.cssText = 'border-left: 1px; border-right: 2px; padding: 5px;';

另一个一次性改变风格的办法是修改CSS 的类名称,而不是修改内联风格代码。这种方法适用于那些风格不依赖于运行逻辑,不需要计算的情况。改变CSS 类名称更清晰,更易于维护;

批量修改DOM

推荐尽可能使用文档片断因为它涉及最少数量的DOM 操作和重排版。

缓冲布局信息

浏览器通过队列化修改和批量运行的方法,尽量减少重排版次数。当你查询布局信息如偏移量、滚动条位置,或风格属性时,浏览器刷队列并执行所有修改操作,以返回最新的数值。最好是尽量减少对布局信息的查询次数,查询时将它赋给局部变量,并用局部变量参与计算。

将元素脱离文档流

显示和隐藏部分页面构成展开/折叠动画是一种常见的交互模式。它通常包括区域扩大的几何动画,将页面其他部分推向下 方。

使用以下步骤可以避免对大部分页面进行重排版:

1、页面顶部可以“折叠/展开”的元素称作“动画元素”,用绝对坐标对它进行定位,当它的尺寸改变时,就不会推移页面中其他元素的位置,而只是覆盖其他元素。

2、展开动作只在“动画元素”上进行。这时其他元素的坐标并没有改变,换句话说,其他元素并没有因为“动画元素”的扩大而随之下移,而是任由动画元素覆盖。

3、“动画元素”的动画结束时,将其他元素的位置下移到动画元素下方,界面“跳”了一下。

事件托管

事件逐层冒泡总能被父元素捕获。采用事件委托技术之后,你只需要在一个包装元素上绑定事件,用于处理子元素发生的所有事件。

总结

  1. 最小化DOM访问
  2. 注意重绘和重排版
  3. 动画元素脱离文档流
  4. 事件委托批量处理子元素事件

wuxianqiang avatar Jan 08 '20 06:01 wuxianqiang