【怎么理解回流与重绘?以及触发场景】

news2024/9/21 16:35:21

一、是什么

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

  • 回流:布局引擎会根据各种样式计算每个盒子在页面上的大小与位置

  • 重绘:当计算好盒模型的位置、大小及其他属性后,浏览器根据每个盒子特性进行绘制

具体的浏览器解析渲染机制如下所示:

  • 解析HTML,生成DOM树,解析CSS,生成CSSOM树

  • 将DOM树和CSSOM树结合,生成渲染树(Render Tree)

  • Layout(回流):根据生成的渲染树,进行回流(Layout),得到节点的几何信息(位置,大小)

  • Painting(重绘):根据渲染树以及回流得到的几何信息,得到节点的绝对像素

  • Display:将像素发送给GPU,展示在页面上

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

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

当对 DOM 的修改导致了样式的变化(colorbackground-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硬件加速,可以让transformopacityfilters这些动画不会引起回流重绘
  • 避免使用 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,将其从页面上去掉,然后再进行后续操作,这些后续操作也不会触发回流与重绘,这个过程称为离线操作

const 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/61698.html

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

相关文章

初学Nodejs(5):npm包管理器与包的发布

初学Nodejs 包 1、概念 什么是包 Nodejs中的第三方模块又叫做包。包的来源 不同于Nodejs中的内置模块与自定义模块&#xff0c;包是由第三方个人或团队开发出来的&#xff0c;免费供人使用。&#xff08;nodejs中的包都是免费且开源的&#xff0c;不需要付费即可免费下载使用…

2022年33个最佳WordPress健康与医疗主题

欢迎来到我们针对健康和保健相关网站和博客的最佳WordPress医疗主题的列表。这些涵盖了一切。您可以将它们用于医生、牙医、医院、健康诊所、内科医生、物理治疗师、外科医生以及健康领域的其他任何事物。大家有什么共同点&#xff1f;优质、100% 可定制的布局和 0 编码策略。 …

【论文精读8】MVSNet系列论文详解-UCS-Net

UCS-Net&#xff0c;论文名为&#xff1a;Deep Stereo using Adaptive Thin Volume Representation with Uncertainty Awareness&#xff0c;CVPR2020&#xff08;CCF A&#xff09; 本文是MVSNet系列的第8篇&#xff0c;建议看过【论文精读1】MVSNet系列论文详解-MVSNet之后再…

机器学习之过拟合和欠拟合

文章目录前言什麽是过拟合和欠拟合?过拟合和欠拟合产生的原因&#xff1a;欠拟合(underfitting)&#xff1a;过拟合(overfitting)&#xff1a;解决欠拟合(高偏差)的方法1、模型复杂化2、增加更多的特征&#xff0c;使输入数据具有更强的表达能力3、调整参数和超参数4、增加训练…

Java项目:SSM游戏点评网站

作者主页&#xff1a;源码空间站2022 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文末获取源码 项目介绍 本项目分为前后台&#xff0c;前台为普通用户登录&#xff0c;后台为管理员登录&#xff1b; 管理员角色包含以下功能&#xff1a; 管理员登录…

jenkins-pipeline语法总结(最全)

1、jenkins总结之pipeline语法 jenkins总结之pipeline语法1、jenkins总结之pipeline语法1.1必要的Groovy知识1.2pipeline的组成1.2.1pipeline最简结构1.3post部分1.4pipeline支持的指令• environment&#xff1a;• tools&#xff1a;• input&#xff1a;• options&#xff…

大学网课查题接口

大学网课查题接口 本平台优点&#xff1a; 多题库查题、独立后台、响应速度快、全网平台可查、功能最全&#xff01; 1.想要给自己的公众号获得查题接口&#xff0c;只需要两步&#xff01; 2.题库&#xff1a; 查题校园题库&#xff1a;查题校园题库后台&#xff08;点击跳…

项目管理逻辑:老板为什么赔钱的项目也做?为什么害怕你闲着?

目录 1.波士顿矩阵 2.为什么企业还要做没有市场占有率,也没有销售增长率的产品? 2.1项目层级划分 2.2项目集 2.3组合管理 2.4赔钱也做的项目案例 1.波士顿矩阵 项目经理没有资源, 公司不给足够的支持 在任何一个企业老板的脑子里,都会有这样一个矩阵, 纵向表示销售增长…

数据结构与算法,MySQL数据库面试专题及答案

文章目录数据结构面试题及答案数组问题字符串相关问题链表问题二叉树问题编程面试问题之杂项答案数据结构与算法时间复杂度 并不是计算程序具体运行的时间&#xff0c;而是算法执行语句的次数 O(2^n) 表示对 n 数据处理需要进行 2^n 次计算 多项式的时间复杂度 数据 n 在表达式…

Docker安装部署Redis集群

目录 概述 一、创建文件和目录 1.1 创建需要挂载的文件和目录 1.2 同步操作 二、随机从节点模式 2.1 创建master节点的redis容器 2.2 在同一台机器上创建另外2个节点 2.3 其他2台机器同步操作 2.4 配置主从集群 2.4.1 进入任意一个 Redis 实例 2.4.2 配置集群 2.4…

《未来简史:从智人到智神》笔记一——人类的新议题

目录 一、人类的旧议题演变 二、人类的新议题 1、长生不死 2、追求幸福快乐 3、努力把自己升级为神 三、研究历史的意义——不是为了重复过去&#xff0c;而是为了摆脱过去并从中获得解放 四、生命的意义 1、主观体验有两个基本特征 2、生命的意义&#xff1f; 一、人类…

C语言第十三课:初阶指针

目录 前言&#xff1a; 一、指针是什么&#xff1a; 1.那么指针到底是什么呢&#xff1f; 2.内存中的数据存储原理&#xff1a; 3.数据存储与指针使用实例&#xff1a; 4.存储编址原理&#xff1a; 二、指针和指针类型&#xff1a; 1.决定了指针的步长&#xff1a; 2.决定了…

【VSCode + Anaconda】VSCode [WinError 126]找不到指定模块

【VSCode Anaconda】VSCode [WinError 126]找不到指定模块问题解决一解决二问题 在 Anaconda Prompt 中的 python 环境测试&#xff0c;可以使用 import torch 命令 现在在 VSCode 中测试&#xff0c;发现相关异常 图中&#xff0c;已经选择了相应的 conda 环境的 python.exe…

分片集群中的分片集合

分片集群中的分片集合 MongoDB 中 分片集群有专门推荐的模式&#xff0c;例如 分片集合 它是一种基于分片键的逻辑对文档进行分组&#xff0c;分片键的选择对分片是非常重要的&#xff0c;分片键一旦确定&#xff0c;MongoDB 对数据的分片对应用是透明的 mongodb 分片中&#…

MySQL高级语句(三)

一、正则表达式&#xff08;REGEXP&#xff09; 1、正则表达式匹配符 字符解释举列^匹配文本的开始字符’ ^aa ’ 匹配以 aa 开头的字符串$匹配文本的结束字符’ aa$ ’ 匹配以aa结尾的字符串.匹配任何单个字符’ a.b 匹配任何a和b之间有一个字符的字符串*匹配零个或多个在它…

数据结构—树、有序二叉树

文章目录树的概述树的分类二叉树的遍历有序二叉树代码通过链表方式构建有序二叉树通过递归方式实现有序二叉树递归遍历有序二叉树中序遍历&#xff1a;先序遍历&#xff1a;后序遍历&#xff1a;删除节点1、删除叶子节点删除叶子节点总结图示2、删除只有一个子树的节点删除只有…

毕业设计-基于深度学习火灾烟雾检测识别系统-yolo

前言 &#x1f4c5;大四是整个大学期间最忙碌的时光,一边要忙着准备考研,考公,考教资或者实习为毕业后面临的就业升学做准备,一边要为毕业设计耗费大量精力。近几年各个学校要求的毕设项目越来越难,有不少课题是研究生级别难度的,对本科同学来说是充满挑战。为帮助大家顺利通过…

Spring循环依赖源码解析(深度理解)

文章目录前言本章目标一、什么是循环依赖&#xff1f;1、那么循环依赖是个问题吗&#xff1f;2、但是在Spring中循环依赖就是一个问题了&#xff0c;为什么&#xff1f;二、Bean的生命周期2.1、在Spring中&#xff0c;Bean是如何生成的&#xff1f;2.2、那么这个注入过程是怎样…

GitLab CI/CD系列教程(一)

来自&#xff1a;GitLab CI/CD系列教程&#xff08;一&#xff09;&#xff1a;Docker安装GitLab_哔哩哔哩_bilibili 1. 创建虚拟机并连接Xterm 创建一个4G内存的虚拟机&#xff0c;否则很容易启动不了&#xff0c;报502 虚拟机的创建看这篇&#xff1a; VMware16的安装及VM…

基于java+ssm+vue+mysql的网上书店

项目介绍 本网上系统是针对目前网上的实际需求&#xff0c;从实际工作出发&#xff0c;对过去的网上系统存在的问题进行分析&#xff0c;结合计算机系统的结构、概念、模型、原理、方法&#xff0c;在计算机各种优势的情况下&#xff0c;采用目前最流行的B/S结构和java中流行的…