回流和重绘

news/2024/11/17 16:22:30/

系列文章目录

前端系列文章——传送门
JavaScript系列文章——传送门


文章目录

    • 系列文章目录
      • 1、浏览器渲染过程
      • 2、回流
      • 3、重绘
      • 4、优化
        • 4.1、合并样式修改
        • 4.2、批量操作DOM
        • 4.3、避免多次触发布局
        • 4.4、修改批量设置样式函数


我们在做案例的时候,通常一个标签要设置很多样式。为了方便我们批量设置样式,可以封装一个批量设置样式的函数:

function setStyle(ele, styleObj) {for(var key in styleObj) {ele.style[key] = styleObj[key]}
}

这个函数在批量设置样式的时候,每遍历一次,设置一次样式,每次设置样式都设置在了行内,这样会造成多次回流,影响页面性能。

1、浏览器渲染过程

浏览器在渲染页面的时候,大致是以下几个步骤:

  1. 解析html生成DOM树,解析css,生成CSSOM树,将DOM树和CSSOM树结合,生成渲染树;
  2. 根据渲染树,浏览器可以计算出网页中有哪些节点,各节点的CSS以及从属关系 - 回流
  3. 根据渲染树以及回流得到的节点信息,计算出每个节点在屏幕中的位置 - 重绘
  4. 最后将得到的节点位置信息交给浏览器的图形处理程序,让浏览器中显示页面

2、回流

回流:英文叫reflow,指的是当渲染树中的节点信息发生了大小、边距等问题,需要重新计算各节点和css具体的大小和位置。

例:在css中对一个div修饰的样式中,修饰了宽度、高度等样式,浏览器需要重新计算标签大小,这个计算的过程,就是回流的过程。

容易造成回流的操作:

  • 布局流相关操作

    • 盒模型的相关操作会触发重新布局
    • 定位相关操作会触发重新布局
    • 浮动相关操作会触发重新布局
  • 节点操作

    改变节点的结构或其中的文本结构会触发重新布局。

    对标签进行下面这些属性或方法操作的时候,会强行回流:

    • offsetTop
    • offsetLeft
    • offsetWidth
    • offsetHeight
    • scrollTop
    • scrollLeft
    • scrollWidth
    • scrollHeight
    • clientTop
    • clientLeft
    • clientWidth
    • clientHeight
    • getComputedStyle
  • css

    • width
    • height
    • padding
    • border
    • margin
    • position
    • top
    • left
    • bottom
    • right
    • float
    • clear
    • text-align
    • vertical-align
    • line-height
    • font-weight
    • font-size
    • font-family
    • overflow
    • white-space

3、重绘

重绘:英文叫repaint,当节点的部分属性发生变化,但不影响布局,只需要重新计算节点在屏幕中的绝对位置并渲染的过程,就叫重绘。比如:改变元素的背景颜色、字体颜色等操作会造成重绘。

回流的过程在重绘的过程前面,所以回流一定会重绘,但重绘不一定会引起回流。

容易造成重绘操作的css:

  • color
  • border-style
  • border-radius
  • text-decoration
  • box-shadow
  • outline
  • background

4、优化

不管是回流还是重绘,都会对浏览器的渲染造成影响,所以我们在项目中,尽量避免回流。

4.1、合并样式修改

减少造成回流的次数,如果要给一个节点操作多个css属性,而每一个都会造成回流的话,尽量将多次操作合并成一个,例:

var oDiv = document.querySelector('.box');
oDiv.style.padding = '5px';
oDiv.style.border = '1px solid #000';
oDiv.style.margin = '5px';

操作div的3个css属性,分别是padding、border、margin,此时就可以考虑将多次操作合并为一次。

方法1:使用style的cssText

oDiv.style.cssText = 'padding:5px; border:1px solid #000; margin:5px;';

方法二:将这几个样式定义给一个类名,然后给标签添加类名:

<style>.pbm{padding:5px; border:1px solid #000; margin:5px;}
</style>
<script>var oDiv = document.querySelector('.box');oDiv.classList.add('pbm');
</script>

4.2、批量操作DOM

当对DOM有多次操作的时候,需要使用一些特殊处理减少触发回流,其实就是对DOM的多次操作,在脱离标准流后,对元素进行的多次操作,不会触发回流,等操作完成后,再将元素放回标准流。

例:

var data = [{id:1,name:"商品1",},{id:2,name:"商品1",},{id:3,name:"商品1",},{id:4,name:"商品1",},// 假设后面还有很多
];
var oUl = document.querySelector("ul");
for(var i=0;i<data.length;i++){var oLi = document.createElement("li");oLi.innerText = data[i].name;oUl.appendChild(oLi);
}

这样每次给ul中新增一个li的操作,每次都会触发回流。

方法1:方法一:隐藏ul后,给ul添加节点,添加完成后再将ul显示

oUl.style.display = 'none';
for(var i=0;i<data.length;i++){var oLi = document.createElement("li");oLi.innerText = data[i].name;oUl.appendChild(oLi);
}
oUl.style.display = 'block';

此时,在隐藏ul和显示ul的时候,触发了两次回流,给ul添加每个li的时候没有触发回流。

方法二:创建文档碎片,将所有li先放在文档碎片中,等都放进去以后,再将文档碎片放在ul中

var fragment = document.createDocumentFragment();
for(var i=0;i<data.length;i++){var oLi = document.createElement("li");oLi.innerText = data[i].name;fragment.appendChild(oLi);
}
oUl.appendChild(fragment);

文档碎片就是一个虚拟的DOM节点。对文档碎片操作不会造成回流。

方法三:将ul拷贝一份,将所有li放在拷贝中,等都放进去以后,使用拷贝替换掉ul

var newUL = oUl.cloneNode(true);
for(var i=0;i<data.length;i++){var oLi = document.createElement("li");oLi.innerText = data[i].name;newUL.appendChild(oLi);
}
oUl.parentElement.replaceChild(newUl, oUl);

4.3、避免多次触发布局

如下回到顶部的操作:

goBack.onclick = function(){setInterval(function(){var t = document.documentElement.scrollTop || document.body.scrollTop;t += 10;document.documentElement.scrollTop = document.body.scrollTop = t;},20)
}

每隔20毫秒都会重新获取滚动过的距离,每次都会触发回流,代码优化如下:

goBack.onclick = function(){var t = document.documentElement.scrollTop || document.body.scrollTop;setInterval(function(){t += 10;document.documentElement.scrollTop = document.body.scrollTop = t;},20)
}

只获取一次,每次都让数字递增,避免每次都获取滚动过的距离。

对于页面中比较复杂的动画,尽量将元素设置为绝对定位,操作元素的定位属性,这样只有这一个元素会回流,如果不是定位的话,容易引起其父元素以及子元素的回流。

4.4、修改批量设置样式函数

function setStyle(ele, styleObj) {var cssText = ''for(var key in styleObj) {var cssProp = keyfor(var a = 0; a < cssProp.length; a++) {var charCode = cssProp.charCodeAt(a)if(charCode >= 65 && charCode <= 90) {cssProp = cssProp.slice(0, a) + '-' + cssProp[a].toLowerCase() + cssProp.slice(a+1)}}cssText += cssProp + ':' + styleObj[key] + ';'}ele.style.cssText = cssText
}

http://www.ppmy.cn/news/35171.html

相关文章

【嵌入式烧录/刷写文件】-1-详解Motorola S-record(S19/SREC/mot/SX)格式文件

目录 1 什么是Motorola S-record 2 Motorola S-record的格式 2.1 Motorola S-record的结构 2.1.1 “Record type记录类型”的说明 2.1.2 “Record length记录长度”的说明 2.1.3 如何计算“Checksum校验和” 2.2 Record order记录顺序 2.3 Text line terminator文本行终…

Python深度学习实战:人脸关键点(15点)检测pytorch实现

引言 人脸关键点检测即对人类面部若干个点位置进行检测&#xff0c;可以通过这些点的变化来实现许多功能&#xff0c;该技术可以应用到很多领域&#xff0c;例如捕捉人脸的关键点&#xff0c;然后驱动动画人物做相同的面部表情&#xff1b;识别人脸的面部表情&#xff0c;让机…

动态规划---线性dp和区间dp

动态规划(三) 目录动态规划(三)一&#xff1a;线性DP1.数字三角形1.1数字三角形题目1.2代码思路1.3代码实现(正序and倒序)2.最长上升子序列2.1最长上升子序列题目2.2代码思路2.3代码实现3.最长公共子序列3.1最长公共子序列题目3.2代码思路3.3代码实现4.石子合并4.1题目如下4.2代…

【Java】注解与反射

学习视频&#xff1a;【狂神说Java】注解和反射_哔哩哔哩_bilibili Java内存分析 #mermaid-svg-5DVSYhOqC0pHFfwe {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-5DVSYhOqC0pHFfwe .error-icon{fill:#552222;}#merm…

现代浏览器四大进程

现代浏览器四大进程 一、进程简介及分类 现代浏览器通常使用多进程架构&#xff0c;其中包括以下四种常见的进程&#xff1a; 浏览器进程&#xff08;Browser Process&#xff09;&#xff1a;浏览器的主进程&#xff08;负责协调、主控&#xff09;&#xff0c;只有一个 该进…

Spring6 - (03) Spring 入门程序

文章目录Spring6 -&#xff08;03&#xff09;Spring 入门程序1. Spring 框架下载&#xff08;了解即可&#xff09;2. Spring 框架目录3. Spring 框架jar包4. 第一个 Spring 程序4.1 环境准备4.2 添加 Spring 依赖4.3 添加 junit 依赖4.4 定义 Bean 类4.5 编写 Spring 配置文件…

大数据技术之Hive

第1章Hive基本概念1.1 Hive1.1.1 Hive的产生背景在那一年的大数据开源社区&#xff0c;我们有了HDFS来存储海量数据、MapReduce来对海量数据进行分布式并行计算、Yarn来实现资源管理和作业调度。但是面对海量数据和负责的业务逻辑&#xff0c;开发人员要编写MR来对数据进行统计…

【python实操】年轻人,别用记事本保存数据了,试试数据库吧

为什么用数据库&#xff1f; 数据库比记事本强在哪&#xff1f; 答案很明显&#xff0c;你的文件很多时候都只能被一个人打开&#xff0c;不能被重复打开。当有几百万数据的时候&#xff0c;你如何去查询操作数据&#xff0c;速度上要快&#xff0c;看起来要清晰直接 数据库比我…