• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

重排和重绘——理解

武飞扬头像
我先润了
帮助1

学习文章:

关键渲染路径(Critical Rendering Path)

要理解重排和重绘,首先要理解浏览器渲染过程中发生了什么。

浏览器将HTML,CSS,JavaScript转换为屏幕上所呈现的实际像素,这期间所经历的一系列步骤,叫做关键渲染路径

关键渲染路径的具体步骤如下:

  1. 浏览器获取HTML文件,然后对文件进行解析,形成DOM Tree
  2. 与此同时,进行CSS解析,生成CSSOM树
  3. 接着将DOM树与CSSOM树结合,去除不可见元素,生成渲染树(Render Tree)
  4. 进入布局(Layout)阶段,即为每个节点分配一个应出现在屏幕上的确切坐标
  5. 调用GPU进行绘制(Paint),遍历Render Tree的节点,并将元素呈现出来

学新通

生成DOM

文档对象模型 - Document Object Model

  1. 转码: 浏览器将从磁盘或网络接收到的二进制数据按照指定编码格式转化为HTML字符串
  2. 生成Tokens: 之后开始parse,浏览器将HTML字符串解析成Tokens
  3. 构建Nodes: 一边生成Token一边消耗Token来生成节点对象,节点对象包含了这个节点的所有属性。通过指针确定 Node 的父、子、兄弟关系和所属 treeScope
  4. 生成DOM Tree: 通过node包含的指针确定的关系构建出DOM Tree

节点间的关系如何维护

节点根据Token 层次结构连接到 DOM 树中,单个 DOM 节点以 startTag token 开始,以 endTag token 结束。Token中会标识出当前Token是“开始标签(startTag)”或是“结束标签(endTag)”亦或是“文本”等信息。如果另一组 startTag 和 endTag token 位于一组 startTag 和 endTag 之间,则在节点内有一个节点。
学新通

生成CSSOM

DOM会捕获页面的内容,但浏览器还需要知道页面如何展示,所以需要构建CSSOM(CSS对象模型 - CSS Object Model)

构建CSSOM的过程与构建DOM的过程非常相似,都要经过Bytes→characters→tokens→nodes→objectmodel这个过程,不同点在于:

  • DOM 构造是增量的,CSSOM 却不是
  • CSS 是渲染阻塞的——浏览器会阻塞页面渲染直到它接收和执行了所有的 CSS,只有当CSSOM构建完毕后才会进入下一个阶段构建渲染树

HTML可以逐步解析,它不需要等待所有DOM都构建完毕后再去构建CSSOM,而是在解析HTML构建DOM时,若遇见CSS会立刻构建CSSOM,它们可以同时进行。但CSS不行,不完整的CSS是无法使用的,因为CSS的每个属性都可以改变CSSOM,所以会存在这样一个问题:假设前面几个字节的CSS将字体大小设置为16px,后面又将字体大小设置为14px,那么如果不把整个CSSOM构建完整,最终得到的CSSOM其实是不准确的。所以必须等CSSOM构建完毕才能进入到下一个阶段,哪怕DOM已经构建完,它也得等CSSOM,然后才能进入下一个阶段。

所以,CSS的加载速度与构建CSSOM的速度将直接影响首屏渲染速度,因此在默认情况下CSS被视为阻塞渲染的资源。
学新通

生成渲染树(Render Tree)

DOM(内容)和CSSOM(样式)树结合为渲染树

学新通

为了构建渲染树,浏览器主要完成了以下工作:

  1. 从DOM树的根节点开始遍历每个可见节点。
  2. 对于每个可见的节点,找到CSSOM树中对应的规则,并应用它们。
  3. 根据每个可见节点以及其对应的样式,组合生成渲染树。

不可见的节点包括:

  • 一些不会渲染输出的节点,比如script、meta、link等。
  • 一些通过css进行隐藏的节点。比如display:none。注意,利用visibility和opacity隐藏的节点,还是会显示在渲染树上的。只有display:none的节点才不会显示在渲染树上。

从上面的例子来讲,我们可以看到span标签的样式有一个display:none,因此,它最终并没有在渲染树上。

Layout

布局取决于屏幕的尺寸,一个页面渲染在不同尺寸的屏幕上,比如渲染在移动端和 PC 端上,展示有差异,在前面的步骤都是不变的,只有在布局的时候才会根据屏幕尺寸进行差异化处理。布局这个步骤决定了在哪里和如何在页面上放置元素,决定了每个元素的宽和高,以及他们之间的相关性。

Paint

最后一步是将像素绘制在屏幕上,栅格化所有元素,将元素转换为实际像素。

一旦渲染树创建并且布局完成,像素就可以被绘制在屏幕上。加载时,整个屏幕被绘制出来。之后,只有受影响的屏幕区域会被重绘,浏览器被优化为只重绘需要绘制的最小区域。

JavaScript 与关键路径渲染

学新通

JavaScript 的执行在生成渲染树之前,JS可以修改网页的内容,更改 DOM。为了保证最终得到的DOM是正确的,JS的加载、解析与执行会阻塞DOM的构建阻塞页面的渲染。为了尽量保证DOM 树生成完毕再去加载JS,需要把js放在页面底部。

重排与重绘

重排/回流(Relayout/Reflow)

当渲染树中部分或者全部元素的尺寸、结构或者属性发生变化时,浏览器会重新渲染部分或者全部文档的过程就称为重排。表现为重新生成布局,重新排列元素。此时在 Layout 阶段,计算每一个元素在设备视口内的确切位置和大小。当一个元素位置发生变化时,其父元素及其后边的元素位置都可能发生变化,代价极高

导致重排的操作

  • 页面的首次渲染
  • 浏览器的窗口大小发生变化
  • 元素的内容发生变化,比如文本变化或图片被另一个不同尺寸的图片所替代
  • 元素的尺寸或者位置发生变化(包括外边距、内边框、边框大小、高度和宽度等)
  • 元素的字体大小发生变化
  • 激活CSS伪类
  • 查询某些属性或者调用某些方法
  • 添加或者删除可见的DOM元素

在触发回流(重排)的时候,由于浏览器渲染页面是基于流式布局的,所以当触发回流时,会导致周围的DOM元素重新排列,它的影响范围有两种:

  • 全局范围:从根节点开始,对整个渲染树进行重新布局
  • 局部范围:对渲染树的某部分或者一个渲染对象进行重新布局
    • 把一个dom的宽高之类的几何信息定死,然后在dom内部触发重排,就只会重新渲染该dom内部的元素,而不会影响到外界

重绘(Repaint)

当页面中某些元素的样式发生变化,但是不会影响其在文档流中的位置时,浏览器对元素进行重新绘制的过程,叫做重绘。表现为某些元素的外观被改变。此时在关键渲染路径中的 Paint 阶段,将渲染树中的每个节点转换成屏幕上的实际像素,这一步通常称为绘制或栅格化。

导致重绘的操作

  • color、background 相关属性:background-color、background-image 等
  • outline 相关属性:outline-color、outline-width 、text-decoration
  • border-radius、visibility、box-shadow

联系

触发重绘不一定触发重排,触发重排一定触发重绘

避免重绘或者重排

  1. 集中改变样式,不要一条一条地修改 DOM 的样式
  2. 不要把 DOM 结点的属性值放在循环里当成循环里的变量
  3. 为动画的 HTML 元件使用 fixedabsoultposition,将动画脱离文档流,那么修改他们的 CSS 是不会引起回流的
  4. 不使用 table 布局。因为可能很小的一个小改动会造成整个 table 的重新布局
  5. 使用absolute或者fixed,使元素脱离文档流,这样他们发生变化就不会影响其他元素
  6. 触发硬件加速(GPU加速),可以让transform、opacity、filters这些动画不会引起回流重绘
  7. 提升为合成层
    • 优点
      • 合成层的位图,会交由 GPU 合成,比 CPU 处理要快
      • 当需要 repaint 时,只需要 repaint 本身,不会影响到其他的层
      • 对于 transform 和 opacity 效果,不会触发 layout 和 paint
    • 提升合成层的最好方式是使用 CSS 的 will-change 属性
  8. 避免频繁操作DOM,可以创建一个文档片段documentFragment,在它上面应用所有DOM操作,最后再把它添加到文档中
    • DocumentFragment,文档片段接口,一个没有父对象的最小文档对象。它被作为一个轻量版的 Document使用,就像标准的document一样,存储由节点(nodes)组成的文档结构。与document相比,最大的区别是DocumentFragment不是真实 DOM 树的一部分,它的变化不会触发 DOM 树的重新渲染,且不会导致性能等问题。当我们把一个 DocumentFragment 节点插入文档树时,插入的不是 DocumentFragment 自身,而是它的所有子孙节点。在频繁的DOM操作时,我们就可以将DOM元素插入DocumentFragment,之后一次性的将所有的子孙节点插入文档中。和直接操作DOM相比,将DocumentFragment 节点插入DOM树时,不会触发页面的重绘,这样就大大提高了页面的性能
  9. 将元素先设置display: none,操作结束后再把它显示出来。因为在display属性为none的元素上进行的DOM操作不会引发回流和重绘
  10. 将DOM的多个读操作(或者写操作)放在一起,而不是读写操作穿插着写
    • 这得益于浏览器的渲染队列机制,浏览器会将所有的回流、重绘的操作放在一个队列中,当队列中的操作到了一定的数量或者到了一定的时间间隔,浏览器就会对队列进行批处理,这样就会让多次的回流、重绘变成一次回流重绘。将多个读操作(或者写操作)放在一起,就会等所有的读操作进入队列之后执行,这样,原本应该是触发多次回流,变成了只触发一次回流
  11. 避免使用offsetTop,scrollTop,clientTop,getComputedStyle(),getBoundingClientRect等属性或方法,因为获取布局信息的操作的时候,浏览器不得不清空队列,强制队列刷新,触发回流重绘来返回正确的值,如果要使用它们,最好将值缓存起来

重排和重绘是浏览器关键渲染路径上的两个节点,浏览器的关键渲染路径就是 DOM 和 CSSOM 生成渲染树,然后根据渲染树通过一个布局(也叫 layout)步骤来确定页面上所有内容的大小和位置,确定布局后,将像素绘制(也叫 Paint)到屏幕上。

其中重排就是当元素的位置发生变动的时候,浏览器重新执行布局这个步骤,来重新确定页面上内容的大小和位置,确定完之后就会进行重新绘制到屏幕上,所以重排一定会导致重绘。

如果元素位置没有发生变动,仅仅只是样式发生变动,这个时候浏览器重新渲染的时候会跳过布局步骤,直接进入绘制步骤,这就是重绘,所以重绘不一定会导致重排。

性能优化

  1. 减少 DOM 树渲染时间(譬如降低 HTML 层级、标签尽量语义化等等)
  2. 减少 CSSOM 树渲染时间(降低选择器层级等等)
  3. 减少 HTTP 请求次数及请求大小
  4. 将 css 放在页面开始位置
  5. 将 js 放在页面底部位置,并尽可能用 defer 或者 async 避免阻塞的 js 加载,确保 DOM 树生成完才会去加载 JS
  6. 用 link 替代@import
  7. 如果页面 css 较少,尽量使用内嵌式
  8. 为了减少白屏时间,页面加载时先快速生成一个 DOM 树

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgghahi
系列文章
更多 icon
同类精品
更多 icon
继续加载