JavaScript 浏览器的重排和重绘

news2025/1/12 20:48:20

文章目录

  • JavaScript 浏览器的重排和重绘
    • 概述
    • 浏览器解析过程
    • 重排
    • 重绘
    • 优化
      • 将多次改变样式的属性操作合并为一次
      • 需要多次重排的元素设置为绝对定位
      • 减少DOM操作
      • 复杂元素处理先设置display为none处理完后再显示
      • 缓存频繁操作的属性
      • 减少使用table布局
      • 使用事件委托绑定事件处理程序
      • 利用DocumentFragment操作DOM节点

JavaScript 浏览器的重排和重绘

概述

虽然可以通过JavaScript操作DOM元素,但是代价却是高昂的。我们可以将DOM和JavaScript想象成两个岛,它们之间的连接需要通过一座桥,而JavaScript对DOM的访问就需要通过这座桥,并收取“过桥费”,随着对DOM访问次数的增加,费用也就越高,因此我们需要尽量减少“过桥”的次数,也就是减少对DOM的访问和修改,而这也是优化DOM性能的手段之一。

对DOM的修改相比于对DOM的访问,在性能上的影响会更大,这是因为它会带来浏览器的重排或重绘。

浏览器解析过程

浏览器渲染HTML的过程大致可以分为4步:

  1. HTML文件被HTML解析器解析成对应的DOM树,CSS样式文件被CSS解析器解析生成对应的样式规则集。
  2. DOM树与CSS样式集解析完成后,附加在一起形成一个渲染树。
  3. 节点信息的计算,即根据渲染树计算每个节点的几何信息。
  4. 渲染绘制,即根据计算完成的节点信息绘制整个页面。

在这里插入图片描述

重排

览器渲染页面默认采用的是流式布局模型。

因为浏览器渲染页面是基于流式布局的,对某一个DOM节点信息进行修改时,就需要对该DOM结构进行重新计算。该DOM结构的修改会决定周边DOM结构的更改范围,主要分为全局范围和局部范围。

全局范围就是从页面的根节点html标签开始,对整个渲染树进行重新计算。例如,当我们改变窗口的尺寸或者修改了根元素的字体大小时。

局部范围只会对渲染树的某部分进行重新计算。例如要改变页面中某个div的宽度,只需要重新计算渲染树中与该div相关的部分即可。

重排是一种明显的改变页面布局的操作,常见操作:

  • 页面首次渲染。在页面首次渲染时,HTML页面的各个元素位置、尺寸、大小等信息均是未知的,需要通过与CSS样式规则集才能确定各个元素的几何信息。这个过程中会产生很多元素几何信息计算的过程,所以会产生重排操作。
  • 浏览器窗口大小发生改变。页面渲染完成后,就会得到一个固定的渲染树。如果此时对浏览器窗口进行缩放或者拉伸操作,渲染树中从根元素html标签开始的所有元素,都会重新计算其几何信息,从而产生重排操作。
  • 元素尺寸或位置发生改变。
  • 元素内容发生变化。
  • 元素字体发生变化。
  • 添加或删除可见的DOM元素。
  • 获取某些特定的属性。

频繁的重排操作会对浏览器引擎产生很大的消耗,因此浏览器不会针对每个JS操作都进行一次重排,而是维护一个会引起重排操作的队列,等队列中的操作达到了一定的数量或者到了一定的时间间隔时,浏览器才会去flush一次队列,进行真正的重排操作。

虽然浏览器会有这个优化,但是浏览器为了返回最精准的信息,我们写的一些代码可能会强制浏览器提前flush队列,例如我们获取以下这些样式信息的时候:

·  offsetTop, offsetLeft, offsetWidth, offsetHeight
·  scrollTop/Left/Width/Height
·  clientTop/Left/Width/Height
·  width,height
·  调用getComputedStyle()函数

在获取以下一些常见的属性和函数时,会引发重排的操作:

· width:宽度。
· height:高度。
· margin:外边距。
· padding:内边距。
· display:元素显示方式。
· border:边框。
· position:元素定位方式。
· overflow:元素溢出处理方式。
· clientWidth:元素可视区宽度。
· clientHeight:元素可视区高度。
· clientLeft:元素边框宽度。
· clientTop:元素边框高度。
· offsetWidth:元素水平方向占据的宽度。
· offsetHeight:元素水平方向占据的高度。
· offsetLeft:元素左外边框至父元素左内边框的距离。
· offsetTop:元素上外边框至父元素上内边框的距离。
· scrollWidth:元素内容占据的宽度。
· scrollHeight:元素内容占据的高度。
· scrollLeft:元素横向滚动的距离。
· scrollTop:元素纵向滚动的距离。
· scrollIntoView():元素滚动至可视区的函数。
· scrollTo():元素滚动至指定坐标的函数。
· getComputedStyle():获取元素的CSS样式的函数。
· getBoundingClientRect():获取元素相对于视窗的位置集合的函数。
· scrollIntoViewIfNeeded():元素滚动至浏览器窗口可视区的函数。(非标准特性,谨慎使用)

重绘

相比于重排,重绘简单很多。重绘只是改变元素在页面中的展现样式,而不会引起元素在文档流中位置的改变。例如更改了元素的字体颜色、背景色、透明度等,浏览器均会将这些新样式赋予元素并重新绘制。

简单来说,重排一定会引起重绘的操作,而重绘却不一定会引起重排的操作。

因为在元素重排的过程中,元素的位置等几何信息会重新计算,并会引起元素的重新渲染,这就会产生重绘的操作。而在重绘时,只是改变了元素的展现样式,而不会引起元素在文档流中位置的改变,所以并不会引起重排的操作。

在修改某些常见的属性时,会引发重绘的操作,例如:

· color:颜色。
· border-style:边框样式。
· visibility:元素是否可见。
· background:元素背景样式,包括背景色、背景图、背景图尺寸、背景图位置等。
· text-decoration:文本装饰,包括文本加下画线、上划线、贯穿线等。
· outline:元素的外轮廓的样式,在边框外的位置。
· border-radius:边框圆角。
· box-shadow:元素的阴影。

优化

将多次改变样式的属性操作合并为一次

不推荐

var changeDiv = document.querySelector('#changeDiv');
changeDiv.style.width = '100px';
changeDiv.style.background = '#e3e3e3';
changeDiv.style.height = '100px';
changeDiv.style.marginTop = '10px';

推荐

<!doctype html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<style type="text/css">
			div.changeDiv {
				width: "100px";
				background: #e3e3e3;
				height: 100px;
				margin-top: 10px;
			}
		</style>
	</head>
	<body>
		<div class="changeDiv"></div>
		<script type="text/javascript">
			document.getElementById('changeDiv').className = "changeDiv";
		</script>
	</body>
</html>

需要多次重排的元素设置为绝对定位

需要进行重排的元素都是处于正常的文档流中的,如果这个元素不处于文档流中,那么它的变化就不会影响到其他元素的变化,这样就不会引起重排的操作。常见的操作就是设置其position为absolute或者fixed。

假如一个页面有动画元素,如果它会频繁地改变位置、宽高等信息,那么最好将其设置为绝对定位。

减少DOM操作

不推荐

// 将数据渲染至table
function renderTable(list) {
    // 目标table元素
    var table = $('#table');
    var rowHTML = '';
    // 遍历数据集
    list.forEach(function(item) {
        rowHTML += '<tr>';
        rowHTML += '<td>' + item.name + '</td>';
        rowHTML += '<td>' + item.address + '</td>';
        rowHTML += '<td>' + item.email + '</td>';
        rowHTML += '</tr>';
        // 每次添加一行数据
        table.append($(rowHTML));
        // 添加完后清空
        rowHTML = '';
    });
}

推荐

// 将数据渲染至table
function renderTable(list) {
    // 目标table元素
    var table = $('#table');
    var allHTML = '';
    // 遍历数据集
    list.forEach(function(item) {
        allHTML += '<tr>';
        allHTML += '<td>' + item.name + '</td>';
        allHTML += '<td>' + item.address + '</td>';
        allHTML += '<td>' + item.email + '</td>';
        allHTML += '</tr>';
    });
    // 获取完整片段后,一次性渲染
    table.append($(allHTML));
}

复杂元素处理先设置display为none处理完后再显示

因为display属性为none的元素不会出现在渲染树中,所以对该元素处理时不会引起其他元素的重排。当我们需要对一个元素做复杂处理时,可以将其display属性设置为none,操作完成后,再将其显示出来,这样就只会在隐藏和显示的时候引发两次重排操作。

缓存频繁操作的属性

不推荐

var ele = document.querySelector('#ele');
// 判断条件1
if(true) {
    ele.style.width = '200px';
}
// 判断条件2
if(true) {
    ele.style.width = '300px';
}
// 判断条件3
if(true) {
    ele.style.width = '400px';
}

推荐

var ele = document.querySelector('#ele');
// 先获取width属性
var width = ele.style.width;
// 判断条件1
if(true) {
    width = '200px';
}
// 判断条件2
if(true) {
    width = '300px';
}
// 判断条件3
if(true) {
    width = '400px';
}
// 最后执行一次width属性赋值
ele.style.width = width;

减少使用table布局

如果table中任何一个元素触发了重排的操作,那么整个table都会触发重排的操作,尤其是当一个table内容比较庞大时,更加不推荐使用table布局。

如果不得已使用了table,可以设置table-layout:auto或者是table-layout:fixed。这样可以让table一行一行地渲染,这种做法也是为了限制重排的影响范围。

使用事件委托绑定事件处理程序

在对多个同级元素做事件绑定时,推荐使用事件委托机制进行处理。使用事件委托可以在很大程度上减少事件处理程序的数量,从而提高性能。

<!doctype html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<ul>
			<li>文本1</li>
			<li>文本2</li>
			<li>文本3</li>
		</ul>
		<script type="text/javascript">
			var EventUtil = {
				// 获取事件对象
				getEvent: function(event) {
					return event || window.event;
				},
				getTarget: function(event) {
					return event.target || event.srcElement;
				}
			};

			var ul = document.querySelector("ul");
			ul.addEventListener("click", function(event) {
				var event = EventUtil.getEvent(event);
				var target = EventUtil.getTarget(event);
				if (target.nodeName.toLowerCase() === "li") {
					console.log(target.innerText);
				}
			});
		</script>
	</body>
</html>

利用DocumentFragment操作DOM节点

DocumentFragment是一个没有父级节点的最小文档对象,它可以用于存储已经排好版或者尚未确定格式的HTML片段。DocumentFragment最核心的知识点在于它不是真实DOM树的一部分,它的变化不会引起DOM树重新渲染的操作,也就不会引起浏览器重排和重绘的操作,从而带来性能上的提升。

因为DocumentFragment具有的特性,在需要频繁进行DOM新增或者删除的操作中,它将变得非常有用。

一般的操作方法分为以下两步:

  1. 将需要变更的DOM元素放置在一个新建的DocumentFragment中,因为DocumentFragment不存在于真实的DOM树中,所以这一步操作不会带来任何性能影响。
  2. 将DocumentFragment添加至真正的文档树中,这一步操作处理的不是DocumentFragment自身,而是DocumentFragment的全部子节点。对DocumentFragment的操作来说,只会产生一次浏览器重排和重绘的操作,相比于频繁操作真实DOM元素的方法,会有很大的性能提升。

不推荐

<ul id="list"></ul>
<script>
    var list = document.querySelector('#list');
    for (var i = 0; i < 100; i++) {
        var li = document.createElement('li');
        var text = document.createTextNode('节点' + i);
        li.append(text);
        list.append(li);
    }
</script>

推荐

<script>
    var list = document.querySelector('#list2');
    // 1.创建新的DocumentFragment对象
    var fragment = document.createDocumentFragment();
    for (var i = 0; i < 100; i++) {
        var li = document.createElement('li');
        var text = document.createTextNode('节点' + i);
        li.append(text);
        // 2.将新增的元素添加至DocumentFragment对象中
        fragment.append(li);
    }
    // 3.处理DocumentFragment对象
    list.append(fragment);
</script>

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

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

相关文章

上海亚商投顾:沪指重返3200点 牛市旗手回归!

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。市场情绪三大指数今日继续走强&#xff0c;沪指重返3200点上方&#xff0c;创业板指午后一度涨近3%&#xff0c;随后涨幅有所…

2023.1. Stimulsoft 报告和仪表板的新版本:Crack

2023.1. Stimulsoft 报告和仪表板的新版本。 发布时间&#xff1a;2022 年 12 月 9 日 我们很高兴地宣布发布 Stimulsoft Reports and Dashboards 2023.1 版&#xff01;我们为 .NET Core 组件添加了对Razor Pages的支持&#xff0c;为PHP和Blazor平台更新了组件。此外&#x…

【Linux】基础:进程间通信

【Linux】基础&#xff1a;进程间通信 摘要&#xff1a;本文主要介绍进程间通信的基础知识&#xff0c;首先将会对进程间通信进行简单概述&#xff0c;其中包括本质目的和方法分类。再介绍对于方法的实现过程&#xff0c;其中有三大类方法&#xff08;管道、System V、POSIX&am…

Kotlin 中变量,类型,表达式,函数详解

博主前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住也分享一下给大家 &#x1f449;点击跳转到教程 一、变量&#xff0c;编译时变量 1、要声明可修改变量&#xff0c;使用var关键字。 2、要声明只读变量&#xff0c;使用…

35岁高龄程序员的 4 条出路,提早布局,避免出局!

目录 一、40岁回首往事&#xff1a;自己竟没有任何核心优势二、公司遇到危机时40岁大龄程序员会怎么样三、适合大龄程序员的几条职业发展路线四、最后的寄语 这篇文章&#xff0c;给大家聊聊Java工程师的职业发展规划的一些思考&#xff0c;同时也给不少20多岁、30多岁&#…

Spark 运行架构

文章目录Spark 运行架构一、运行架构二、核心组件1、Driver2、Executor3、Master & Worker4、ApplicationMaster三、核心概念1、Exuecutor 和 Core2、并行度&#xff08;Parallelism&#xff09;3、有向无环图&#xff08;DAG&#xff09;4、提交流程Yarn Client 模式Spark…

Spring Cloud Gateway(黑马springcloud笔记)

Gateway 目录Gateway一、为什么需要网关二、gateway入门三、断言工厂四、过滤器工厂五、全局过滤1. 实现2. 过滤器执行顺序六、跨域问题一、为什么需要网关 不能让外部能够直接访问微服务&#xff0c;而是需要通过网关访问&#xff1a; 网关的作用&#xff1a; 身份认证和权限…

数据结构与算法基础(王卓)(8):线性表的应用(并集和有序表合并)

PPT&#xff1a;第二章P173&#xff1b; 并集集合&#xff1a;线性表的合并&#xff08;无需有序&#xff0c;不能重复&#xff09; 线性表&#xff1a; Status Union(Sqlist& A, Sqlist& B)//并集 {int len_A A.length;int len_B B.length;for (int i 1; i < …

研究生如何能(较快)找出某领域(去噪)已有算法的创新点或者引入其他领域的新算法?

广义上说&#xff0c;滤波就是给不同的信号分量分配不同的权重&#xff0c;较为复杂的维纳滤波, 是根据信号的统计量设计权重。狭义上说&#xff0c;降噪/去噪&#xff0c;可以看成滤波的一种。降噪的目的在于突出信号本身而抑制噪声影响。从这个角度&#xff0c;降噪就是给信号…

C/C++ 调用规则

平栈&#xff1a;清理参数对调用栈的操作步骤&#xff1a;参数传递三种调用约定&#xff1a;cdecl &#xff08;C调用约定&#xff09;:从右往左传参&#xff0c;参数通过栈传递&#xff0c;调用方(caller)负责平参&#xff08;支持类似printf的不定参&#xff09;stdcall (标准…

hadoop简介

文章目录1&#xff1a;hadoop简介2&#xff1a;Hadoop系统2.1&#xff1a;hadoop架构1&#xff1a;MapReduce2&#xff1a;YARN架构3&#xff1a;HDFS2.2&#xff1a;HDFS、YARN、MapReduce三者关系1&#xff1a;hadoop简介 Hadoop是一个由Apache基金会所开发的分布式系统基础…

如何快速删除CSV、Excel、Markdown表格的重复行?

如果你正在使用 CSV、Excel 或 Markdown 表格&#xff0c;你可能会遇到重复行的问题。这可能是因为你手动输入了重复的数据&#xff0c;或者是因为你从其他源导入了重复的数据。无论原因是什么&#xff0c;删除重复行是一项重要的数据清理任务。本文将向你展示如何使用几种不同…

RESTful的风格提倡 URL 地址使用统一的风格设计

RESTful概念实现REST&#xff1a;Representational State Transfer&#xff0c;表现层资源状态转移。资源&#xff1a;资源是一种看待服务器的方式&#xff0c;即&#xff0c;将服务器看作是由很多离散的资源组成。每个资源是服务器上一个可命名的抽象概念。资源的表述资源的表…

nnUNet 训练 AMOS22数据集 Task216(抽丝剥茧指令+原理篇)

环境准备篇 安装hiddenlayer&#xff08;用来生成什么网络拓扑图&#xff1f;管他呢&#xff0c;装吧&#xff09; pip install --upgrade githttps://github.com/nanohanno/hiddenlayer.gitbugfix/get_trace_graph#egghiddenlayer 安装环境&#xff0c;由于服务器已经装好py…

网络安全日益严峻下计算机主机加固的意义

​ 近年来&#xff0c;计算机以及互联网应用在中国得到普及和发展&#xff0c;已经深入到社会每个角落&#xff0c;政府&#xff0c;经济&#xff0c;军事&#xff0c;社会&#xff0c;文化和人们生活等各方面都越来越依赖于计算机和网络&#xff0c;电子政务&#xff0c;无纸办…

【计算机体系结构】指令集体系结构、微体系结构简介

1. “虚拟” to “现实” 首先可以看这张图片&#xff0c;下面的 Physics 所指的是我们的物理世界中看得见摸得到或者是客观存在的事物&#xff0c;而人类希望将自己的工作内容或者需求以某种方式映射到物理层面上&#xff0c;用物理变化带来的影响来完成人类工作内容。例如早期…

《Linux Shell脚本攻略》学习笔记-第二章

2.1 简介 本章将为你介绍一些最值得关注同时也是最实用的命令。 2.2 用cat进行拼接 cat命令能够显示或者拼接文件内容。cat能够将标准输入数据与文件数据组合在一起。 通常的做法是将stdin重定向到一个文件&#xff0c;然后再合并两个文件。而cat命令一次就能搞定这些操作。 1&…

【Linux】基础常见指令

目录​​​​​​​ 前言 一、Linux的环境搭建与远程控制 Linux 环境的搭建方式主要有三种 使用 XShell 远程登陆到 Linux 二、常见指令 1. ls 指令 2. pwd命令 3. cd 指令 4. touch指令 5. mkdir指令 6. rmdir指令 && rm 指令 7. man指令 8. cp指令 9. mv指令 10.…

【微信小程序】实用教程02-添加全局页面配置、页面、底部导航

开始前&#xff0c;请先完成项目创建&#xff0c;详见 【微信小程序】实用教程01-注册登录账号&#xff0c;获取 AppID、下载安装开发工具、创建项目、上传体验 前期准备 因我们的项目是根据模板创建的&#xff0c;需先清理掉无效的页面代码&#xff0c;具体操作方式如下&…

LeetCode 64. 最小路径和

&#x1f308;&#x1f308;&#x1f604;&#x1f604; 欢迎来到茶色岛独家岛屿&#xff0c;本期将为大家揭晓LeetCode 64. 最小路径和&#xff0c;做好准备了么&#xff0c;那么开始吧。 &#x1f332;&#x1f332;&#x1f434;&#x1f434; 一、题目名称 LeetCode 64…