天天微动态丨JavaScript DOM的性能优化详解_驱动网
天天微动态丨JavaScript DOM的性能优化详解
时间:2022-12-16 06:06:36 来源:博客园
本身JS操作DOM就比较消耗性能,你可以理解为JS和dom是独立的小岛,用桥实现两者的联系,但桥很窄,要过路费,所以我们要尽最大可能减少过桥的次数。
再加上每次操作DOM都会触发布局的改变、DOM树的修改和渲染。也就是说操作DOM会引发重排(回流)与重绘,这个过程也是非常消耗资源的。所以要尽量避免重复操作DOM元素。
(相关资料图)
DOM性能优化的本质: 就是减少对DOM查询及减少DOM操作(增、删、改)引起的重排(回流)和重绘的次数
DOM性能优化方法: 合并多次对css样式的修改,改为一次处理 对DOM查询做缓存 将频繁DOM操作改为一次性操作 操作DOM前,先把DOM节点删除或隐藏 采用事件代理处理事件 在讲解5种优化方法之前,我们需要先了解什么是重排(回流)和重绘,要了解重排(回流)和重绘,就需要先了浏览器的渲染机制,所以我们先从浏览器的渲染机制开始讲起。
一、浏览器的渲染机制 浏览器的整个渲染过程(下图)
解析 HTML,构建 DOM 树 解析 CSS,生成 CSS 规则树 合并 DOM 树和 CSS 规则树,生成 render(渲染)树。 布局 render 树(回流 / 重排),负责各元素尺寸、位置的计算。 绘制 render 树(painting 重绘),绘制页面像素信息 浏览器会将各层的信息发送给 GPU,GPU 会将各层合成(composite),显示在屏幕上。 构建渲染树时,浏览器主要完成以下工作
从 DOM 树的根节点开始遍历每个可见节点 对每个可见节点,找到 CSS 规则树中对应的规则,并应用它们 根据每个可见节点以及其对应的样式,组合生成渲染树 不可见节点(也就是不会出现在渲染树中的节点)
一些不会渲染输出的节点(如:script、meta、link 等) 一些通过 css 进行隐藏的节点(如:display: none) 注意点:
样式为display:none;的节点会在DOM树中而不在渲染树中 visiblity 和 opacity 隐藏的节点在DOM和渲染树中同时存在。 浏览器绘制之后便开始解析js文件,根据js对DOM的操作来确定是否会再次发生重排(回流)和重绘。 二、什么是重排(回流)和 重绘 重排(回流)
当渲染树(render tree)中的一部分或全部因为元素的规模尺寸、大小等改变时,浏览器需要重新计算元素在设备视口(viewport)内的确切位置和大小,需要重新布局render树,这个过程为回流(重排)。
重绘
当页面元素样式改变(如 color、background-color、visibility),但不影响元素在文档流中的的位置时,浏览器只需将新样式赋予元素并进行重新绘制render树操作,这个过程为重绘。
回流必将引起重绘,但重绘不一定会引起回流
什么情况会发生重排(回流)
添加或删除可见的 DOM 元素 元素的位置发生变化 元素的尺寸发生变化(外边距、内边距、边框大小、高度和宽度等) 内容发生变化,(比如文本变化或图片被尺寸大小发生变化) 页面渲染初始化(必然要首次重排) 浏览器的窗口 resize 尺寸变化(因为回流是根据视口的大小来计算元素的位置和大小的) 获取元素位置和大小相关的属性和方法,因为都需要返回最新的布局信息,因此浏览器不得不触发回流重绘来返回正确的值 offset(Top / Left / Width / Height) scroll(Top / Left / Width / Height) client(Top / Left / Width / Height) width、height 调用了 getComputeStyle() 或者 IE 的 currentStyle 三、DOM性能优化的4种方法 ① 合并多次对css样式的修改,改为一次处理 优化前样式代码
oLi.style.width="100px";oLi.style.height="20px";oLi.style.background="pink";
方法一:用cssText一次性处理
oLi.style.cssText = "width:100px;height:20px;background:pink";
方法二:用className一次性处理
.item{width:100px;height:20px; padding: 0px; border: 0px; color: rgb(197, 134, 192); --tt-darkmode-color: #C586C0;">pink;}/*定义类样式*/oLi.className="item"//js添加类样式
三种情况下,消耗时间对比
方法一:优化前方法二:cssText处理后方法三:className处理后370.9970703125 ms243.667236328125 ms147.678955078125 ms
<script>console.time("优化前");//测试执行时间代码varoUl=document.getElementById("list");for(vari=0;i<50000;i++) {varoLi=document.createElement("li");//方法一oLi.style.width="100px";oLi.style.height="20px";oLi.style.background="pink";//方法二:// oLi.style.cssText = "width:100px;height:20px;background:pink";//方法三:/* oLi.className = "item";oUl.appendChild(oLi);*/}console.timeEnd("优化前");//测试执行时间代码</script> ② 对DOM查询做缓存 不要频繁的去查询DOM,把查询到的内容保存在变量中,后面直接通过变量来操作就好。
未优化前,要频繁的查询DOM
for(vari=0;i优化后,只需要查询一次DOM
varn=document.querySelectorAll("li").length;for(vari=0;i
优化后与优化前耗时对比未优化前耗时优化后耗时206.722900390625 ms154.436767578125 ms<script>window.onload=function() {//优化前代码console.time("时间记录");//测试执行时间代码for(vari=0;i
③、将频繁DOM操作改为一次性操作 这里面要理解一个概念DOM文档片段(虚拟节点对象)文档片段的作用是充当其它要被添加到文档的节点的仓库 。他自己永远不会被添加到文档树中,但是他能包含和操作节点。可以通过document.createDocumentFragment()方法来创建文档片段。
具体的代码实现
<script> console.time("优化后"); const oUl = document.getElementById("list"); //创建一个文档片段,些时还没有插入到DOM中,存在内存中 const frag = document.createDocumentFragment(); //执行行入操作 for (let x = 0; x < 10000; x++) { const li = document.createComment("li"); //将DOM先放到文档片段中,这样不会频繁操作DOM frag.appendChild(li); } //最后一次性将10000个li插入到DOM树中 oUl.appendChild(frag); console.timeEnd("优化后"); </script>
这个优化消耗的时间需要在正常的网页上去测试才能看到效果,因为如果页面中没有其它元素,也就不会造成重排和重绘,那对于时间上的消耗也是看不到的。大家可以自己行测试。
④ 操作DOM前,先把DOM节点删除或隐藏 如果要对一个元素进行多次DOM操作,可以先将其隐藏,操作完成后再显示。这样只在隐藏和显示时触发2次重排,而不会是在每次进行操作时都出发一次重排。因为display:none时的元素不在渲染树中,因此对隐藏的元素操作不会引发其他元素的重排。
<script> var oUl = document.getElementById("list"); oUl.style.display = "none";/*先隐藏元素*/ for (var i = 0; i < 15000; i++) { var li = document.createElement("li"); oUl.appendChild(li); } oUl.style.display = "block";/*DOM操作完再显示*/ </script>
⑤ 事件代理 事件委托,利用浏览器事件冒泡捕获减少页面事件绑定,我们可以指定一个事件处理程序就可以管理某一类型的所有事件。事件函数过多会占用大量内存,而且绑定事件的DOM元素越多会增加访问dom的次数,对页面的交互就绪时间也会有延迟。
<script> //事件委托 var oUl = document.getElementById("list"); oUl.onclick = function (e) { console.log(e.target.innerHTML); }; /* 未用事件委托 var li = document.querySelectorAll("#list li"); var n = li.length; for (let i = 0; i < n; i++) { li[i].onclick = function () { console.log(this.innerHTML); }; } */ </script>
DOM是JS阶段重要的教学内容,在星辰班已经讲了40个小节的课程,更多完整JavaScript课程体系在我们的系统班里有完整的呈现,包含了JavaScript基础篇、重点、算法、原理、面试题、实战案例讲解!同时也为你提供了前端高级工程师成长体系!(详细看下图内容)
如果需要深度学习的同学可以联系助理老师了解详细的课程以及课程的报名方式!(不定期会推出活动,有大额优惠券推出,活动详情联系助理老师了解即可!)
如果你才开始学习前端,那么可以先学习我们的三十天计划(零基础的同学报名系统班同学可以和老师沟通制定学习计划,可以得到更快的成长!)
为帮助到一部分同学不走弯路,真正达到一线互联网大厂前端项目研发要求,首次实力宠粉,打造了《30天挑战学习计划》,内容如下:
HTML/HTML5,CSS/CSS3,JavaScript,真实企业项目开发,云服务器部署上线,从入门到精通
PC端项目开发(1个) 移动WebApp开发(2个) 多端响应式开发(1个) 共4大完整的项目开发 !一行一行代码带领实践开发,实际企业开发怎么做我们就是怎么做。从学习一开始就进入工作状态,省得浪费时间。
从学习一开始就同步使用 Git 进行项目代码的版本的管理,Markdown 记录学习笔记,包括真实大厂项目的开发标准和设计规范,命名规范,项目代码规范,SEO优化规范
从蓝湖UI设计稿 到 PC端,移动端,多端响应式开发项目开发
真机调试,云服务部署上线; Linux环境下 的 Nginx 部署,Nginx 性能优化; Gzip 压缩,HTTPS 加密协议,域名服务器备案,解析; 企业项目域名跳转的终极解决方案,多网站、多系统部署; 使用 使用 Git 在线项目部署; 这些内容在《30天挑战学习计划》中每一个细节都有讲到,包含视频+图文教程+项目资料素材等。只为实力宠粉,真正一次掌握企业项目开发必备技能,不走弯路 !
过程中【不涉及】任何费用和利益,非诚勿扰 。
如果你没有添加助理老师微信,可以添加下方微信,说明要参加30天挑战学习计划,来自头条号!老师会邀请你进入学习,并给你发放相关资料。
30 天挑战学习计划 Web 前端从入门到实战 | arry老师的博客-艾编程
推荐内容
天天微动态丨JavaScript DOM的性能优化详解
2022-12-16
每日资讯:VUE的实例的生命周期
2022-12-16
焦点热讯:替代if esle 的高级方法
2022-12-15
make学习
2022-12-15
天天热门:高手必备10大难题:Mysql如何实现RR级隔离时,不会幻读?
2022-12-15
FreeSWITCH学习笔记:应用程序(APP)
2022-12-15
全球观察:7-2 案例 字符串关键字的散列映射
2022-12-15
环球微头条丨初始Docker
2022-12-15
热点评!分智慧果 - 2021算法与数据结构实验题
2022-12-15
天天微动态丨自研分布式高性能RPC框架及服务注册中心ApiRegistry实践笔记【原创】【开源】
2022-12-15
【独家】图形用户界面(GUI)编程可以学习C++ Builder,多图、实例、书籍
2022-12-15
17 Java内存模型与线程_Java与线程
2022-12-15
环球要闻:AIRIOT答疑第2期|如何使用物联网平台的数据采集与控制引擎?
2022-12-15
世界速读:BaseDet: 走过开发的弯路
2022-12-15
今日热门!引迈信息荣登「2022低代码企业50强」:JNPF,你的不二选择!
2022-12-15
环球微速讯:SQL的使用总结
2022-12-15
天天快资讯:WPF深入简出(一)入门必看
2022-12-15
观热点:【机器学习】李宏毅——浅谈机器学习原理+鱼与熊掌兼得的深度学习简述
2022-12-15
当前动态:前后端AES加密解密,CryptoJS和Java实现
2022-12-15
热讯:记录--手把手带你开发一个uni-app日历插件(并发布)
2022-12-15
【世界速看料】LeetCode HOT 100:旋转图像
2022-12-15
【天天速看料】Shell 标准输入和输出
2022-12-15
信息:公司新来一个同事,把网关系统设计的炉火纯青!(万能通用,稳的一批。。)
2022-12-15
当前视讯!易基因|NSUN2介导RNA m5C修饰促进食管鳞状细胞癌进展的表观调控机制 | 肿瘤研究
2022-12-15
ThingsBoard前端项目的安装与启动
2022-12-15
【时快讯】RELIC库学习
2022-12-15
全球看热讯:喜讯+1!袋鼠云数栈技术团队获“2022年度优秀开源技术团队”
2022-12-15
投影仪的工作原理是什么?投影仪排名前十的品牌
2022-12-15
Centos安装Nginx
2022-12-15
针孔无线摄像机怎么连接?针孔无线摄像机怎么用?
2022-12-15
本地连接受限制或无连接是怎么回事?本地连接受限制或无连接怎么解决?
2022-12-15
svchost.exe占用cpu过是什么原因?svchost.exe占用cpu过高解决方法
2022-12-15
pdf文件打开是乱码怎么回事?pdf文件打开是乱码时该怎么办?
2022-12-15
全球讯息:阻止Windows未知应用弹窗的一种思路方法
2022-12-15
Zabbix6.0使用教程 (四)—zabbix6.0从源代码安装
2022-12-15
Tomcat工作原理
2022-12-15
天天快讯:针对某钓鱼网站的渗透测试
2022-12-15
【脚本项目源码】Python制作桌面宠物,这么可爱的萌宠你不想拥有吗?
2022-12-15
快消息!Vue核心概念与其指令
2022-12-15
Wireshark使用笔记
2022-12-15
焦点!Zabbix监控系统
2022-12-15
世界快资讯丨Kubernetes Volumes 笔记
2022-12-15
环球滚动:[WPF] MediaElement播放HDR视频泛黄、颜色显示不正确应该如何解决?
2022-12-14
R数据分析:冲击流图与热图的做法以及多图布局
2022-12-14
简讯:VUE简介
2022-12-14
Python3.7.3环境搭建
2022-12-14
全球观点:哈希表总结
2022-12-14
当前报道:2022 ICPC 杭州站 K - Master of Both // Trie
2022-12-14
精选!浅析JWT Attack
2022-12-14
记录--uniapp 应用APP跳转微信小程序
2022-12-14