likes
comments
collection
share

(一)层层突破之回流重绘

作者站长头像
站长
· 阅读数 12

层层突破

回流重绘、跨域、浏览器相关考点是成为一个前端切图仔必不可少的基础知识了吧,从这篇文章开始一一搞定它们!

一、从输入url页面渲染发生了什么?

在聊这个回流重绘之前,我们首先要知道浏览器从输入url页面渲染发生了什么呢? 它主要分为大步:

1. 第一大步

(1)DNS解析

DNS是用来解析域名的分布式数据库系统,将域名解析为IP地址。DNS分为两种查询方式,迭代查询(靠自己)和递归查询(靠别人),这里介绍DNS的迭代查询。 它的步骤是:客户端->本地域名服务器->根域名服务器->顶级域名服务器->目标服务器。 解析过程如下:

(一)层层突破之回流重绘

(2)HTTP建立连接

连接传输的过程分为连接建立(TCP三次握手),数据传送,连接释放(TCP四次挥手)。

1) 连接建立(TCP三次握手)

整个过程就像这两位的对话:

(一)层层突破之回流重绘

假设运行在一台主机(客户)上的一个进程想与另一台主机(服务器)上的一个进程建立一条连接,客户应用进程首先通知客户TCP,他想建立一个与服务器上某个进程之间的连接,客户中的TCP会用以下步骤与服务器中的TCP建立一条TCP连接: · ROUND 1: 客户端发送连接请求报文段。 SYN=1,seq=x(随机) · ROUND 2: 服务器端为该TCP连接分配缓存和变量,并向客户端返回确认报文段允许连接。 SYN=1,ACK=1,seq=y(随机),ack=x+1 · ROUND3: 客户端为该TCP连接分配缓存和变量,并向服务器端返回确认的确认,可以携带数据。 SYN=0,ACK=1,seq=x+1,ack=y+1

(一)层层突破之回流重绘

2) 连接释放(TCP四次挥手)

整个过程就像这两位的对话:

(一)层层突破之回流重绘

参与一条TCP连接的两个进程中的任何一个都能终止该连接,连接结束后,主机中的"资源"(缓存和变量)将被释放。步骤如下: · ROUND 1: 客户端发送连接释放报文段,停止发送数据,主动关闭TCP连接。 FIN=1,seq=u · ROUND 2: 服务器端回送一个确认报文段客户到服务器这个方向的连接就释放了(半关闭状态)。 ACK=1,seq=v,ack=u+1 · ROUND 3: 服务器端发完数据,就发出连接释放报文段,主动关闭TCP连接。 FIN=1,ACK=1,seq=w,ack=u+1 · ROUND 4: 客户端回送一个确认报文段,再等到时间等待计时器设置的2MSL (最长报文段寿命)后,连接彻底关闭。 ACK=1,seq=u+1,ack=w+1

(一)层层突破之回流重绘

2. 第二大步

(1) 浏览器加载HTML代码,将其解析成DOM树 (2) 加载css代码,将其解析成CSSOM树 (3) DOM Tree + CSSOM Tree =render Tree (4) 计算布局,render树中的每一个节点都计算一遍(回流) (5) GPU 绘制 一个又一个的图层(重绘)

二、回流重绘

好的,咱们回归主题,回流与重绘的概念我们已经知道了,那么什么时候会触发回流,什么时候会触发重绘呢?

1. 触发回流的操作

(1) 视窗大小变化 (2) DOM集合属性发生变化 (3) 添加或者删除DOM (4)offsetWidth,offsetHeight,offset...,clientWidth,clientHeight... ,client...,scrollTop,scroll...

回流需要更新渲染树,性能开销大,所以我们要减少回流主要有以下几种方式:

  • 元素脱离文档流

  • 改变样式

  • 回归文档流

2. 触发重绘的操作

元素的非几何属性变化时会发生重绘,不会刷新页面 (1) color (2) background (3) border-radius (4) box-shadow (5) 背景图

回流一定重绘,重绘不一定回流

3. 浏览器的渲染队列

  • 浏览器维护了一个渲染队列,当我们修改了元素的几何属性,导致浏览器发生回流和重绘时,浏览器会将该操作存进队列,等待队列中的内存达到一定的阈值,或者到了一定的时间时,浏览器会一次性批量操作

4. 强制刷新队列

offsetWidth,offsetHeight,clientWidth,clientHeight,scrollTop,..... 有关的操作会强制刷新渲染队列,即会触发回流。

5.优化

  1. 当一个dom节点display:none时,它便不参加render树的构建
  2. 页面添加结构时,使用虚拟文档片段
  3. 使用克隆元素

例:我们要在ul中添加5个li,三种优化方式的代码如下:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <div id="app">
    <ul id="ul"></ul>
  </div>
  <script>
    //1.
    //for循环中一次回流,render树构建一次
    // let ul = document.getElementById('ul')
    // ul.style.display = 'none'
    // for (let i = 1; i < 5; i++) {
    //   let li = document.createElement('li')
    //   li.innerText = i
    //   ul.appendChild(li)
    // }
    // ul.style.display = 'block'

    //2.
    //for循环中一次回流,render树构建一次
    // let ul=document.getElementById('ul')
    // let frg=document.createDocumentFragment() //虚拟文档片段
    // for (let i = 1; i < 5; i++) {
    //   let li = document.createElement('li')
    //   li.innerText = i
    //   frg.appendChild(li)
    // }
    // ul.appendChild(frg)

   //3.
    let ul = document.getElementById('ul')
    let clone=ul.cloneNode(true)
    for (let i = 1; i < 5; i++) {
      let li = document.createElement('li')
      li.innerText = i
      clone.appendChild(li)
    }
    ul.parentNode.replaceChild(clone,ul)
  </script>
</body>

</html>