重排(回流)和重绘

article/2025/4/30 19:15:25

什么是重排和重绘

浏览器下载完页面所有的资源后,就要开始构建DOM树,与此同时还会构建渲染树(Render Tree)。(其实在构建渲染树之前,和DOM树同期会构建Style Tree。DOM树与Style Tree合并为渲染树)

浏览器下载完成所有的资源后,开始构建DOM树和Style树,DOM树和Style树合并为渲染树。

DOM树:表示页面的结构

渲染树:表示页面节点如何显示

一旦渲染树构建完成,就要开始绘制(paint)页面元素了。

当DOM的变化引发了元素几何属性的变化,比如改变元素的宽高,元素的位置,导致浏览器不得不重新计算元素的几何属性,并重新构建渲染树,这个过程称为“重排”。完成重排后,要将重新构建的渲染树渲染到屏幕上,这个过程就是“重绘”。

简单的说,重排负责元素的几何属性更新,重绘负责元素的样式更新,而且,重排必然带来重绘,但是重绘未必带来重排。比如,改变某个元素的背景,这个就不涉及元素的几何属性,所以只发生重绘。

重排触发机制

上面已经提到了,重排发生的根本原理就是元素的几何属性发生了改变,那么我们就从能够改变元素几何属性的角度入手

  1. 添加或删除可见的DOM元素
  2. 元素位置改变(外边距、position)
  3. 元素本身的尺寸发生改变(内边距、边框厚度、宽高等几何属性)
  4. 内容改变
  5. 页面渲染器初始化
  6. 浏览器窗口大小发生改变

如何进行性能优化,最小化重排和重绘

1.改变样式

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

这个例子其实和上面那个例子是一回事儿,在最糟糕的情况下,会触发浏览器三次重排。更高效的方式就是合并所有的改变一次处理。这样就只会修改DOM节点一次,比如改为使用cssText属性实现:

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

但cssText也有个缺点,会覆盖之前的样式。如

<div style="color:red;">TEST</div>// 想给该div在添加个css属性width
div.style.cssText = "width:200px;";
// 这时虽然width应用上了,但之前的color被覆盖丢失了。因此使用cssText时应该采用叠加的方式以保留原有的样式。
function setStyle(el, strCss){el.style.cssText = el.style.cssText + strCss;
}

沿着这个思路,直接切换类名。没错,还有一种减少重排的方法就是切换类名,而不是使用内联样式的cssText方法。使用切换类名就变成了这样:

// css 
.active {padding: 5px;border-left: 1px;border-right: 2px;
}
// javascript
var el = document.querySelector('.el');
el.className = 'active';

2.批量修改DOM

批量修改DOM元素的核心思想是:

  1. 让该元素脱离文档流
  2. 对其进行多重改变
  3. 将元素带回文档中

这个过程引发俩次重排,第一步和第三步,如果没有这两步,可以想象一下,第二步每次对DOM的增删都会引发一次重排。


<ul class="ul_box"><li><a href="https://www.baidu.com/">百度</a></li><li><a href="https://map.baidu.com/">百度地图</a></li>
</ul>// 把以下数据插入到上面的节点中
const data = [{name: '百度翻译',url: 'https://fanyi.baidu.com/'},{name: '百度知道',url: 'https://zhidao.baidu.com/'}
]// 封装的插入节点方法
appendNode(node,data){data.forEach(item => {const li = document.createElement('li');const a = document.createElement('a');a.href = item.urlconst textNode = document.createTextNode(item.name);//createTextNode() 方法创建文本节点,该方法返回 Text 对象。a.appendChild(textNode)li.appendChild(a)node.appendChild(li)});
},// 不考虑优化,每次插入新的节点都会造成一次重排
const ul_box = document.getElementsByClassName('ul_box')[0]
this.appendNode(ul_box,data)

优化1:隐藏元素,进行修改后,然后再显示该元素

const ul_box = document.getElementsByClassName('ul_box')[0]
ul_box.style.display = 'none';// 隐藏元素
this.appendNode(ul_box,data); // 进行修改
ul_box.style.display = 'block';// 显示元素

这种方法造成俩次重排,分别是控制元素的显示与隐藏。对于复杂的,数量巨大的节点段落可以考虑这种方法。为啥使用display属性呢,因为display为none的时候,元素就不在文档流

优化2:使用文档片段创建一个子树,然后再拷贝到文档中

const ul_box = document.getElementsByClassName('ul_box')[0]
let fragment = document.createDocumentFragment();
this.appendNode(fragment, data);
ul_box.appendChild(fragment);// 一次重排

文档片段是一个轻量级的document对象,它设计的目的就是用于更新,移动节点之类的任务,而且文档片段还有一个好处就是,当向一个节点添加文档片段时,添加的是文档片段的子节点群,自身不会被添加进去。不同于第一种方法,这个方法并不会使元素短暂消失造成逻辑问题。上面这个例子,只在添加文档片段的时候涉及到了一次重排。

优化3:将原始元素拷贝到一个独立的节点中,操作这个节点,然后覆盖原始元素

const old = document.getElementsByClassName('ul_box')[0]
const clone = old.cloneNode(true);//clone节点,true为clone后代节点,否则只是clone当前节点只有<div class='ui_box'></div>
this.appendNode(clone, data);// 操作clone的节点
old.parentNode.replaceChild(clone,old);// 获取老节点的父节点,替换原有子节点,只有一次重排,parentNode 属性以 Node对象的形式返回指定节点的父节点,如果指定节点没有父节点,则返回 null。replaceChild 实现子节点(对象)的替换。返回被替换对象的引用。replaceChild(new,old) 

cloneNode

cloneNode(true);true为clone后代节点,否则只是clone当前节点。

// 内联事件处理函数,将会被绑定到克隆所得的新节点上
<div style='width: 100px;height: 200px;background:pink' onclick="divClick()">aaa</div><script>
// 内联事件处理函数,将会被绑定到克隆所得的新节点上
function divClick(){console.log('aaaa');
}const div = document.getElementsByTagName('div')[0]
// 元素属性: 事件将不会被绑定到被克隆的节点上
div.onclick = function (){console.log('bbb');
}
//addEventListener函数: 事件将不会被绑定到克隆所得的新节点上
div.addEventListener('click',function(){console.log('cccc');
})
console.log('div',div);const cloneDiv = div.cloneNode(true);
console.log('cloneDiv',cloneDiv);
document.body.appendChild(cloneDiv);
</script>

Vue中通过v-on或其语法糖@指令来给元素绑定事件并且提供了事件修饰符,基本流程是进行模板编译生成AST,生成render函数后并执行得到VNode,VNode生成真实DOM节点或者组件时候使用addEventListener方法进行事件绑定。

所以在vue中cloneNode(true),不会clone事件

缓存布局信息

缓存布局信息这个概念,在《高性能JavaScript》DOM性能优化中,多次提到类似的思想.比如我现在要得到页面ul节点下面的100个li节点,最好的办法就是第一次获取后就保存起来,减少DOM的访问以提升性能,缓存布局信息也是同样的概念。

前面有讲到,当访问诸如offsetLeft,clientTop这种属性时,会冲破浏览器自有的优化————通过队列化修改和批量运行的方法,减少重排/重绘版次。所以我们应该尽量减少对布局信息的查询次数,查询时,将其赋值给局部变量,使用局部变量参与计算。

看以下样例:
将元素div向右下方平移,每次移动1px,起始位置100px, 100px。性能糟糕的代码:

div.style.left = 1 + div.offsetLeft + 'px';
div.style.top = 1 + div.offsetTop + 'px';

这样造成的问题就是,每次都会访问div的offsetLeft,造成浏览器强制刷新渲染队列以获取最新的offsetLeft值。更好的办法就是,将这个值保存下来,避免重复取值

current = div.offsetLeft;
div.style.left = 1 + ++current + 'px';
div.style.top = 1 + ++current + 'px';

http://chatgpt.dhexx.cn/article/eTE2cBwM.shtml

相关文章

什么是重排和重绘

简答 1.重排&#xff08;重新排列&#xff09;> 指元素的位置发生改变的时候浏览器会进行重排 2.重绘&#xff08;重新绘制&#xff09;>指元素的基本样式发生改变是发生重绘&#xff0c;比如颜色等。。。 细答 前提概要&#xff08;浏览器使用两个引擎进行工作一是渲…

Docker之发布自己的镜像

首先应当是先登入 其次是将镜像重新打包 在此处使用命令 docker commit fb6af4e48c14 zlx/centos7_vim:1.0 将一个容器生成一个新的镜像 然后把这个镜像改成dockerhub的仓库名&#xff0c;push一下就行了&#xff0c;dockerhub毕竟是国外的网站&#xff0c;换成阿里云就好了

Matlab调用excel数据绘制折线图

如题&#xff0c;matlab之前没接触过&#xff0c;但是电脑上一直有安装&#xff0c;有些老师需要做几张图放论文里&#xff0c;所以尝试了一下&#xff08;excel其实效果也行&#xff0c;但matlab感觉更专业&#xff09; x2:2:778;%x轴上的数据&#xff0c;第一个值代表数据开…

COGS 2075. [ZLXOI2015][异次元圣战III]ZLX的陨落

★★☆ 输入文件&#xff1a;ThefallingofZLX.in 输出文件&#xff1a;ThefallingofZLX.out 简单对比时间限制&#xff1a;1 s 内存限制&#xff1a;256 MB 【题目描述】 正当革命如火如荼&#xff0c;情侣教会日薄西山之时&#xff0c;SOX和FFF的分歧却越来越大&#…

Codechef GRAPHCNT 支配树学习及tarjan算法求解

[Codechef GRAPHCNT]新年的有向图 【题目描述】 zlx同学在学习数论时,被虐了一脸,丧心病狂的他决定去报复社会。 zlx在公园里埋下N颗地雷,用来炸飞在春节期间秀恩爱的情侣。这N颗地雷由M条有向边连接成为一个有向图,zlx则在1号地雷处引爆1号地雷可以到达的地雷。现在,为了…

大数据体系建设经验分享

分享嘉宾&#xff1a;吴荣彬 分贝通 大数据部负责人 编辑整理&#xff1a;zlx 出品平台&#xff1a;DataFunTalk 导读&#xff1a;本文将介绍分贝通在大数据领域的一些建设经验。分贝通在ToB领域是一个年轻的公司&#xff0c;成立六年多&#xff0c;大数据体系刚刚建立一年多&a…

将二维数组转为稀疏数组进行压缩,提高效率

** 稀疏数组 ** ##基本介绍&#xff1a; 当一个数组中大部分元素为&#xff10;&#xff0c;或者为同一个值的数组时&#xff0c;可以使用稀疏数组来保存该数组。 稀疏数组的处理方法是: 1) 记录数组一共有几行几列&#xff0c;有多少个不同的值 2) 把具有不同值的元素的行列…

A数字三角 怨种

问题描述 有一天&#xff0c;无聊的 zlx 从 1 开始数数&#xff0c;同时在纸上写下每个数的个位数字。因为她非常热爱直角三角形&#xff0c;所以在纸上写下的数字按照直角三角形排列。现在告 诉你写她了 N 行数字&#xff0c;要求你打出这些数字。 输入格式 一行一个数 N&a…

聊聊Spring的IOC,AOP、DI、MVC

1 聊聊Ioc,Di,Mvc,Aop 不想看下面内容的&#xff0c;直接上代码连接&#xff0c;以下代码都有注释 传送门 1.1 看启动项 我们先来看看启动项 package com.tian;import com.tian.springframework.annotation.SpringBootApplication; import com.tian.springframework.config…

ZYNQ DDS产生载波FFT变换

环境&#xff1a;vivado2017.4&#xff0c;快速傅里叶变换V9.0 IP核 一&#xff0c;介绍&#xff1a;Xilinx FFT IP核是一种计算DFT的有效方式。 特点&#xff1a;前向变换&#xff08;FFT&#xff09;和反向变换&#xff08;IFFT&#xff09;在复数空间&#xff0c;并且可…

并发队列中迭代器弱一致性原理探究

一、前言 并发队列里面的Iterators是弱一致性的&#xff0c;next返回的是队列某一个时间点或者创建迭代器时候的状态的反映。当创建迭代器后&#xff0c;其他线程删除了该元素时候并不会抛出java.util.ConcurrentModificationException异常&#xff0c;能够保持创建迭代器后的元…

浅谈同构类问题的骗分算法

ZLX算法-----同构类问题的有力骗分算法前言&#xff1a;ZLX算法是一种解决判定性同构问题的蒙特卡罗式骗分算法&#xff1a;总能在确定的运行时间内出解&#xff0c;但是得到的解不能保证正确。尽管由于具有拓扑序&#xff0c;树同构和仙人掌同构存在多项式算法&#xff0c;但是…

win10下修改C盘用户文件夹名

之前安装一个程序出错&#xff0c;上网百度后是用户文件夹名为中文&#xff0c;也在网上找了好多方法&#xff0c;有同步的&#xff0c;有修改注册表的&#xff0c;最后我找到一个比较简单而且数据保留完整的方法。这种方法也会自动修改用户的环境变量&#xff0c;不过修改完后…

【Windows】Win10-更改c盘下的用户文件夹名

转载ooooohugh的文章&#xff0c;原文地址&#xff1a;https://blog.csdn.net/qq_33530388/article/details/71739845 当初 不小心用自己名字 作为计算机用户名&#xff0c;后来 许多软件因为 不支持 路径中有中文&#xff0c;导致吃了不少的亏&#xff0c;心疼。。。。 下面说…

Windows10更改c盘中用户名对应的文件名字

目录 前言一、修改步骤1.开启管理员账号并登陆2.重启电脑3.登录管理员账号4.重命名用户名对应的文件夹5.修改Path6.重启电脑并切换回你的账号7.修改环境变量8.重启电脑 二、修改导致的问题1.桌面大部分软件快捷方式失效2.部分软件无故消失 三、提醒Last 前言 强烈建议看完此文…

win10 修改c盘用户文件夹名称

c盘用户文件夹如果是中文名 可能会导致需要没必要的麻烦&#xff0c;记录一下修改方法 第一步&#xff1a;如果你电脑不是本地用户administrator&#xff0c;注销当前用户使用administrator登录 按winx 点击关机或注销 在点击注销 注销后如果没有Administrator登录方式&#…

有关windows10修改C盘用户中文名文件夹相关问题的具体解决方案

win10修改用户文件夹名 今天在下载安卓sdk开发工具的时候&#xff0c;安装出现了一个问题如图&#xff0c;左下角提示我们的sdk路径含有非ascll的字符&#xff0c;无法继续安装&#xff0c;其实不只是中文字符&#xff0c;英文字符中间若有空格也不能继续安装。 对于互联网学…

更改计算机用户文件夹,win10系统怎么自定义C盘用户文件夹名称

许多用户在安装win10系统之后&#xff0c;想要让电脑显得更加个性化&#xff0c;就想要给C盘中的用户文件夹名称进行自定义修改&#xff0c;那么win10系统怎么自定义C盘用户文件夹名称呢&#xff1f;接下来给大家分享一下具体的操作步骤。 1、在键盘上按下Windows键X 组合键&am…

Windows 11 的C盘User(用户)文件夹下的用户文件夹名称的修改

背景介绍&#xff1a;由于系统重装导致Windows 11的系统用户名与C盘User&#xff08;用户&#xff09;文件夹下的用户名文件夹&#xff08;公用文件夹旁边的文件夹&#xff09;出现名称不一致&#xff0c;事例中系统用户名命名为“寂萧”&#xff0c;User&#xff08;用户&…

win10怎么更改c盘用户计算机名,详解win10系统更改c盘用户名文件夹名称的设置技巧...

电脑一旦开机就会不停的运行&#xff0c;不可避免会出现软硬件问题&#xff0c;win10系统更改c盘用户名文件夹名称就是比较常见的状况&#xff0c;尤其是姑娘们遇到win10系统更改c盘用户名文件夹名称是要难过哭鼻子的&#xff0c;其实小编的经验是碰到win10系统更改c盘用户名文…