页面生成的过程:

  1. HTML -> 被 HTML 解析器解析成 DOM 树
  2. CSS -> 被 CSS 解析器解析成 CSSOM 树
  3. 结合 DOM 树和 CSSOM 树 生成一颗渲染树(Render Tree) 这一个过程称之为 Attachment
  4. 生成布局(flow),浏览器在屏幕上“画”出渲染树中的所有节点
  5. 将布局绘制(paint)在屏幕上,显示出整个页面

4和5 结合起来也就是我们所说的渲染,同时也是最耗时的部分

渲染:

在网页生成时,至少会渲染一次。在用户访问的过程中,还会不断触发重绘和重排,从而影响性能。所以在开发过程中应该避免页面的重排重绘。

什么是重绘、重排?

什么是重绘、重排?从字面意思就可以理解

  • 重绘:重新绘制,一般发生在布局不变,视觉上变化的时候,比如阴影颜色
  • 重排:重新排列,一般发生在布局变化或元素大小发生变化时

如上概念可以得出:

  1. 单单改变元素的外观,肯定不会引起网页的重新生成布局。
  2. 改变元素大小和布局变化时,那元素乃至周边的 DOM 可能都需要重新绘制。

也就是说:重绘不一定导致重排,但重排一定会导致重绘

重排(reflow):

概念:

当 DOM 的变化影响了元素的几何信息(如位置、大小等),浏览器都需要重新计算元素的几何属性。这个过程叫做重排。
重排也叫回流,简单的说就是重新生成布局,重新排列元素

下面的情况会发生重排:

+ 页面初始渲染,这是开销最大的一次重排
+ 添加/删除可见的 DOM 元素
+ 改变元素位置
+ 改变元素尺寸,比如 边距、填充、边框、宽度高度等
+ 改变元素内容,比如文字数量、图片大小等
+ 改变元素字体大小
+ 改变浏览器窗口尺寸,比如 resize 事件发生时
+ 激活 css 伪类 比如 :hover
+ 设置 style 属性的值,
+ 查询某些属性或调用某些计算方法:offsetWidth、offsetHeight等
常见引起重排的属性和方法 - - -
width height margin padding
display border-width border position
overflow font-size vertical-align min-height
clientWidth clientHeight offsetTop offsetLeft
scrollWidth scrollHeight scrollTop scrollLeft
getBoundingClientRect() scrollIntoViewNeeded()

重排的影响范围:

由于浏览器渲染界面是基于流式布局模型的,所以触发重排时会对周围 DOM 重新排列,影响范围有两种:
+ 全局范围:从根节点 html 开始对整个渲染树进行重新渲染
+ 局部范围:对渲染树的某部分或某一个渲染对象进行重新布局

全局范围重排

一般情况下当根节点 或 子节点发生重大布局变化时,往往会发生全局范围的重排

局部范围重排

只在某个 DOM 内部触发重排,一个元素形成 BFC 后,这个元素内部发生变化不会影响到外部的其他元素,只会影响该元素内部的其他元素

重排优化建议

重排的代价是高昂的,会破坏用户体验,并且让 UI 展示非常迟缓。通过减少重排的负面影响来提供用户体验的最简单方式就是尽可能的减少重排次数

减少重排范围

尽量以局部布局的形式组织 html 结构,尽可能小的影响重排范围
+ 不要使用 table 布局,可能一个小改动会造成整个 table 的重新布局。不得已的情况下,可以设置 table-layout: auto; 或者是 table-layout: fixed 这样可以让 table 一行一行的渲染。这样的做法是为了限制 reflow 的影响范围。
+ 减少 DOM 嵌套

减少重排次数

  • 样式集中改变, 不频繁的操作样式。
  • 分离 DOM 读写操作,DOM 的多个读写操作,应该放在一起,不要两个读操作之间,加入一个写操作。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // bad 强制刷新 触发四次重排+重绘
    div.style.left = div.offsetLeft + 1 + 'px';
    div.style.top = div.offsetTop + 1 + 'px';
    div.style.right = div.offsetRight + 1 + 'px';
    div.style.bottom = div.offsetBottom + 1 + 'px';


    // good 缓存布局信息 相当于读写分离 触发一次重排+重绘
    var curLeft = div.offsetLeft;
    var curTop = div.offsetTop;
    var curRight = div.offsetRight;
    var curBottom = div.offsetBottom;

    div.style.left = curLeft + 1 + 'px';
    div.style.top = curTop + 1 + 'px';
    div.style.right = curRight + 1 + 'px';
    div.style.bottom = curBottom + 1 + 'px';

原来的操作会触发四次重排,修改后只会触发一次,这是因为我们的浏览器的渲染机制。
当我们修改元素的几何属性,导致浏览器发生重排或重绘时。它会把该操作放进渲染队列,等到队列中的操作到了一定数量或到了一定的时间间隔,浏览器就会批量操作。

  • 将 元素 形成 BFC

  • 优化动画
    将动画效果应用在 BFC 元素上,减少重排范围。动画效果可以牺牲一些平滑,来换取速度,这中间的度自己衡量。比如实现一个动画 以1个像素单位移动这样最平滑,但是 layout 会过于频繁,大量消耗 CPU 资源,如果以3个像素为单位移动则会好很多

    • 启用 GPU 加速,GPU 硬件加速是指应用 GPU 的图形性能对浏览器中的一些图像操作交给 GPU 来完成。因为 GPU 是专门为处理图形而设计,所以它在速度和能耗上更有效率。

      GPU 加速通常包括以下几个部分: Canvas2D,布局合成, CSS3 转换(transitions),CSS3 3D 变换(transform),WebGl 视频

重绘(repaints)

当一个元素的外观发生改变,但没有改变布局,重新把元素外观绘制出来的过程,就叫重绘

常见引起重绘的属性

常见引起重绘的属性和方法 - - -
color border-style visibility background
text-decoration background-image background-position background-repeat
outline-color outline outline-style border-radius
outline-width box-shadow background-size

小结

  • 渲染的三个阶段 Layout,Paint,Composite Layers。Layout: 重排,又叫回流。Paint:重绘,重绘重排这些步骤都是在 CPU 中发生的。 Composite Layers:CPU 把生成的 BitMap 传输到 GPU ,渲染到屏幕上
  • CSS3 就是在 GPU 发生的: Transform Opacity。在 GPU 发生的属性比较高效。所以 CSS3 性能比较高。