css基础知识十三:怎么理解回流跟重绘?什么场景下会触发?

news2024/11/25 15:47:54

在这里插入图片描述
一、是什么

在HTML中,每个元素都可以理解成一个盒子,在浏览器解析过程中,会涉及到回流与重绘:

  • 回流:布局引擎会根据各种样式计算每个盒子在页面上的大小与位置
  • 重绘:当计算好盒模型的位置、大小及其他属性后,浏览器根据每个盒子特性进行绘制

具体的浏览器解析渲染机制如下所示:
在这里插入图片描述

  • 解析HTML,生成DOM树,解析CSS,生成CSSOM树
  • 将DOM树和CSSOM树结合,生成渲染树(Render Tree)
  • Layout(回流):根据生成的渲染树,进行回流(Layout),得到节点的几何信息(位置,大小)
  • Painting(重绘):根据渲染树以及回流得到的几何信息,得到节点的绝对像素
  • Display:将像素发送给GPU,展示在页面上

在页面初始渲染阶段,回流不可避免的触发,可以理解成页面一开始是空白的元素,后面添加了新的元素使页面布局发生改变

当我们对 DOM 的修改引发了 DOM几何尺寸的变化(比如修改元素的宽、高或隐藏元素等)时,浏览器需要重新计算元素的几何属性,然后再将计算的结果绘制出来

当我们对 DOM的修改导致了样式的变化(color或background-color),却并未影响其几何属性时,浏览器不需重新计算元素的几何属性、直接为该元素绘制新的样式,这里就仅仅触发了重绘

二、如何触发

要想减少回流和重绘的次数,首先要了解回流和重绘是如何触发的

回流触发时机

回流这一阶段主要是计算节点的位置和几何信息,那么当页面布局和几何信息发生变化的时候,就需要回流,如下面情况:

  • 添加或删除可见的DOM元素
  • 元素的位置发生变化
  • 元素的尺寸发生变化(包括外边距、内边框、边框大小、高度和宽度等)
  • 内容发生变化,比如文本变化或图片被另一个不同尺寸的图片所替代
  • 页面一开始渲染的时候(这避免不了)
  • 浏览器的窗口尺寸变化(因为回流是根据视口的大小来计算元素的位置和大小的)

还有一些容易被忽略的操作:获取一些特定属性的值

offsetTop、offsetLeft、 offsetWidth、offsetHeight、scrollTop、scrollLeft、scrollWidth、scrollHeight、clientTop、clientLeft、clientWidth、clientHeight

这些属性有一个共性,就是需要通过即时计算得到。因此浏览器为了获取这些值,也会进行回流

除此还包括getComputedStyle方法,原理是一样的

重绘触发时机

触发回流一定会触发重绘

可以把页面理解为一个黑板,黑板上有一朵画好的小花。现在我们要把这朵从左边移到了右边,那我们要先确定好右边的具体位置,画好形状(回流),再画上它原有的颜色(重绘)

除此之外还有一些其他引起重绘行为:

  • 颜色的修改
  • 文本方向的修改
  • 阴影的修改

浏览器优化机制

由于每次重排都会造成额外的计算消耗,因此大多数浏览器都会通过队列化修改并批量执行来优化重排过程。浏览器会将修改操作放入到队列里,直到过了一段时间或者操作达到了一个阈值,才清空队列

当你获取布局信息的操作的时候,会强制队列刷新,包括前面讲到的offsetTop等方法都会返回最新的数据

因此浏览器不得不清空队列,触发回流重绘来返回正确的值

三、如何减少

我们了解了如何触发回流和重绘的场景,下面给出避免回流的经验:

  • 如果想设定元素的样式,通过改变元素的 class 类名 (尽可能在 DOM 树的最里层)
  • 避免设置多项内联样式
  • 应用元素的动画,使用 position 属性的 fixed 值或 absolute 值(如前文示例所提)
  • 避免使用 table 布局,table 中每个元素的大小以及内容的改动,都会导致整个 table 的重新计算
  • 对于那些复杂的动画,对其设置 position: fixed/absolute,尽可能地使元素脱离文档流,从而减少对其他元素的影响
  • 使用css3硬件加速,可以让transform、opacity、filters这些动画不会引起回流重绘
  • 避免使用 CSS 的 JavaScript 表达式

在使用 JavaScript 动态插入多个节点时, 可以使用DocumentFragment. 创建后一次插入. 就能避免多次的渲染性能

但有时候,我们会无可避免地进行回流或者重绘,我们可以更好使用它们

例如,多次修改一个把元素布局的时候,我们很可能会如下操作

const el = document.getElementById('el')
for(let i=0;i<10;i++) {
    el.style.top  = el.offsetTop  + 10 + "px";
    el.style.left = el.offsetLeft + 10 + "px";
}

每次循环都需要获取多次offset属性,比较糟糕,可以使用变量的形式缓存起来,待计算完毕再提交给浏览器发出重计算请求

// 缓存offsetLeft与offsetTop的值
const el = document.getElementById('el') 
let offLeft = el.offsetLeft, offTop = el.offsetTop

// 在JS层面进行计算
for(let i=0;i<10;i++) {
  offLeft += 10
  offTop  += 10
}

// 一次性将计算结果应用到DOM上
el.style.left = offLeft + "px"
el.style.top = offTop  + "px"

我们还可避免改变样式,使用类名去合并样式

const container = document.getElementById('container')
container.style.width = '100px'
container.style.height = '200px'
container.style.border = '10px solid red'
container.style.color = 'red'

使用类名去合并样式

<style>
    .basic_style {
        width: 100px;
        height: 200px;
        border: 10px solid red;
        color: red;
    }
</style>
<script>
    const container = document.getElementById('container')
    container.classList.add('basic_style')
</script>

前者每次单独操作,都去触发一次渲染树更改(新浏览器不会),
都去触发一次渲染树更改,从而导致相应的回流与重绘过程
合并之后,等于我们将所有的更改一次性发出

我们还可以通过设置元素属性display: none,将其从页面上去掉,然后再进行后续操作,这些后续操作也不会触发回流与重绘,这个过程称为离线操作

onst container = document.getElementById('container')
container.style.width = '100px'
container.style.height = '200px'
container.style.border = '10px solid red'
container.style.color = 'red'

离线操作后

let container = document.getElementById('container')
container.style.display = 'none'
container.style.width = '100px'
container.style.height = '200px'
container.style.border = '10px solid red'
container.style.color = 'red'
...(省略了许多类似的后续操作)
container.style.display = 'block'

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/702672.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MySQL-SQL全部锁详解(下)

♥️作者&#xff1a;小刘在C站 ♥️个人主页&#xff1a; 小刘主页 ♥️努力不一定有回报&#xff0c;但一定会有收获加油&#xff01;一起努力&#xff0c;共赴美好人生&#xff01; ♥️学习两年总结出的运维经验&#xff0c;以及思科模拟器全套网络实验教程。专栏&#xf…

老PM,到底在牛什么?

早上好&#xff0c;我是老原。 “成长为一名优秀的项目经理。”相信这是每一个刚入行的项目经理都会立的flag。 对于项目经理来说&#xff0c;这是一个能力和经验并重的岗位&#xff0c;你应该也经常听人说&#xff0c;这项目经理是一个越老越吃香的职业。 就拿我自己来说&a…

【java】HashMap扩容机制详解

文章目录 JDK1.7下的扩容机制JDK1.8下的扩容机制 JDK1.7下的扩容机制 JDK1.7下的resize()方法是这样的&#xff1a; void resize(int newCapacity) { Entry[] oldTable table; int oldCapacity oldTable.length; if (oldCapacity MAXIMUM_CAPACITY) { threshold Integer.…

零样本视频生成无压力,基于飞桨框架实现Text2Video-Zero核心代码及依赖库

项目背景 继 AI 绘画之后&#xff0c;短视频行业正迎来 AI 智能创作的新浪潮。AI 智能创作正在各个方面为创作者和用户带来新的体验和价值。AI 动漫视频、AI 瞬息宇宙、AI 视频风格化等诸多创作功能不仅为视频内容创作提供了全新灵感&#xff0c;而且大大降低了用户创作的门槛…

SpringMVC原理分析 | 数据处理:ModelAndView

&#x1f497;wei_shuo的个人主页 &#x1f4ab;wei_shuo的学习社区 &#x1f310;Hello World &#xff01; 重定向和转发 ModelAndView 设置ModelAndView对象&#xff0c;根据view的名称、视图解析器跳转到指定的页面 页面&#xff1a;{视图解析器前缀} viewName {视图解析器…

git常用命令之命令集

15. 命令集 场景1. 构造1个文件的10个commit 命令作用for i in {1..10}; do date >> 66.txt && git add . && git commit -sm "update"; done自证 场景2. 构造10个文件 命令作用for i in {1..10}; do date >> "file_$i.log&quo…

Quiz 7: Files | Python for Everybody 配套练习_解题记录

文章目录 课程简介Quiz 7: Files 单选题&#xff08;1-10&#xff09;编程题Exercise 7.2 课程简介 Python for Everybody 零基础程序设计&#xff08;Python 入门&#xff09; This course aims to teach everyone the basics of programming computers using Python. 本课程…

项目——学生信息管理系统1

目录 创建项目 1. 修改Eclipse的编码为UTF-8,具体参考给的文档 1.1 设置代码自动保存 2. 创建Java项目 分包 添加数据库驱动jar包依赖 复制数据库驱动jar包到lib目录下 添加依赖 创建登陆页面 在 org.xingyun.view 包下创建 LoginFrm 选择 WindowBuilder下的 Swing D…

oracle 重复启动监听程序故障

又是一起 oracle 无法连接问题&#xff0c;检查配置都是正常的。 原来是碰到一个oralce的bugl了。 还真就是这个问题&#xff0c;子进程一kill掉&#xff0c;就恢复了。

微服务实战项目-学成在线-课程发布模块

学成在线-课程发布模块 1 模块需求分析 1.1 模块介绍 课程信息编辑完毕即可发布课程&#xff0c;发布课程相当于一个确认操作&#xff0c;课程发布后学习者在网站可以搜索到课程&#xff0c;然后查看课程的详细信息&#xff0c;进一步选课、支付、在线学习。 下边是课程编辑…

视频解说小程序看点小程序搭建上线,流量主对接实现广告收益

什么是视频解说小程序&#xff1f; 影视剪辑和解说&#xff0c;我们都知道有这类的抖音号&#xff0c;这时候就用到我们小程序了&#xff0c;流量主产生了收益。把视频解说上传到小程序&#xff0c;设置为广告观看&#xff0c;这样引导用户去小程序看&#xff0c;就产生一个广告…

(一)等值线 (Spatial Analyst)

等值线 (Spatial Analyst) 1.摘要 根据栅格表面创建等值线(等值线图)的线要素类。 等值线是用于连接各类等值点(如高程、温度、降雨量、污染或大气压力)的线。线的分布显示表面值的变化方式。值的变化量越小,线的间距就越大。值上升或下降得越快,线的间距就越小。 2.…

基于单片机的厨房安全监测

功能介绍 以STM32单片机作为主控系统&#xff1b; OLED液晶显示当前检测的气体浓度&#xff0c;温度&#xff0c;是否有火等信息&#xff1b; 按键可以设置温度上限、可燃气体浓度上限&#xff1b; 当温度超过我们设置自动开启风扇进行降温&#xff1b; 当检测到天然气泄露后蜂…

ROS学习篇之基础知识(一)-环境的安装

文章目录 一.ros的安装1.ros的一键安装&#xff1a;2.ros的验证是否安装成功3.安装导航必备的库&#xff1a; 二.VScode的配置1.安装ros插件2.安装cmake插件 三.VScode的简单使用1.快速注释&#xff1a; 四.pycharm的安装 一.ros的安装 1.ros的一键安装&#xff1a; wget htt…

shiro框架 shiro补充知识MD5加密

01.在用户登录时候&#xff0c;需要输入用户名和密码。 用户名用数据库的select来查询是否存在。 密码比较私密&#xff0c;就是后台的程序员也不可以查看&#xff0c;这需要加密。 一般的加密算法是MD5 02.例如&#xff1a; SecurityUtils 类是shiro框架的一个类。SecurityUt…

LabVIEW开发牵引控制动态仿真器

LabVIEW开发牵引控制动态仿真器 车辆牵引力控制包括轮胎在横向和纵向上的牵引力控制&#xff0c;以获得所需的车辆运动。决定轮胎牵引力的力来自路面和轮胎之间的相互作用&#xff0c;它们分解为两个组成部分&#xff1a;一个在横向&#xff0c;另一个在纵向。第一个组件取决于…

MSP432学习笔记11:定时器A的结构\基地址\函数汇总理解

今日得以继续我的电赛MSP432学习之路&#xff1a;所用开发板MSP432P401R 定时器是任何单片机开发板十分重要的模块&#xff0c;在几日的学习使用过程中&#xff0c;本人也对其使用原理等产生过许多疑问&#xff0c;他究竟是怎么存储计数值、捕获值的&#xff1f;一个定时器四个…

基于Java校园车辆管理系统设计实现(源码+lw+部署文档+讲解等)

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

Arduino Proteus仿真DHT11自动浇水浇花浇土装置-0048

Arduino Proteus仿真DHT11自动浇水浇花浇土装置-0048 Proteus仿真小实验&#xff1a; Arduino Proteus仿真DHT11自动浇水浇花浇土装置-0048 功能&#xff1a; 硬件组成&#xff1a;ARDUINO -UNO-R3开发板、 LCD1602 、DHT11温湿度传感器、LED灯、电机模拟水泵、2个按键 1.单…

【详解代码】vue element el-select选择器多选下拉框实现全选功能和取消全选

目录 解决方法 下拉项增加一个【全部】⭐️效果图如下&#xff1a;默认全选情况一&#xff1a;下拉选项全都勾选时&#xff0c;【全部】自动勾选&#xff1b;情况二&#xff1a;下拉选项全都未勾选时&#xff0c;点击【全部】后&#xff0c;所有下拉选项全部勾选&#xff1b;情…