我的编程空间,编程开发者的网络收藏夹
学习永远不晚

JavaScript中的DOM操作为什么很慢

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

JavaScript中的DOM操作为什么很慢

本篇内容主要讲解“JavaScript中的DOM操作为什么很慢”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“JavaScript中的DOM操作为什么很慢”吧!

浏览器是如何呈现一张页面的

一个浏览器有许多模块,其中负责呈现页面的是渲染引擎模块,比较熟悉的有WebKit和Gecko等,这里也只会涉及这个模块的内容。

先用文字大致阐述下这个过程:

  • 解析HTML,并生成一棵DOM tree

  • 解析各种样式并结合DOM tree生成一棵Render tree

  • 对Render tree的各个节点计算布局信息,比如box的位置与尺寸

  • 根据Render tree并利用浏览器的UI层进行绘制

其中DOM tree和Render tree上的节点并非一一对应,比如一个”display:none"的节点就只会存在于DOM tree上,而不会出现在Render tree上,因为这个节点不需要被绘制。

JavaScript中的DOM操作为什么很慢

上图是Webkit的基本流程,在术语上和Gecko可能会有不同,这里贴上Gecko的流程图,不过文章下面的内容都会统一使用Webkit的术语。

JavaScript中的DOM操作为什么很慢

影响页面呈现的因素有许多,比如link的位置会影响首屏呈现等。但这里主要集中讨论与layout相关的内容。

paint是一个耗时的过程,然而layout是一个更耗时的过程,我们无法确定layout一定是自上而下或是自下而上进行的,甚至一次layout会牵涉到整个文档布局的重新计算。

但是layout是肯定无法避免的,所以我们主要是要最小化layout的次数。

什么情况下浏览器会进行layout

在考虑如何最小化layout次数之前,要先了解什么时候浏览器会进行layout。

layout(reflow)一般被称为布局,这个操作是用来计算文档中元素的位置和大小,是渲染前重要的一步。在HTML***次被加载的时候,会有一次layout之外,js脚本的执行和样式的改变同样会导致浏览器执行layout,这也是本文的主要要讨论的内容。

一般情况下,浏览器的layout是lazy的,也就是说:在js脚本执行时,是不会去更新DOM的,任何对DOM的修改都会被暂存在一个队列中,在当前js的执行上下文完成执行后,会根据这个队列中的修改,进行一次layout。

然而有时希望在js代码中立刻获取***的DOM节点信息,浏览器就不得不提前执行layout,这是导致DOM性能问题的主因。

如下的操作会打破常规,并触发浏览器执行layout:

  • 通过js获取需要计算的DOM属性

  • 添加或删除DOM元素

  • resize浏览器窗口大小

  • 改变字体

  • css伪类的激活,比如:hover

  • 通过js修改DOM元素样式且该样式涉及到尺寸的改变

我们来通过一个例子直观的感受下:

// Read var h2 = element1.clientHeight;  // Write (invalidates layout) element1.style.height = (h2 * 2) + 'px';  // Read (triggers layout) var h3 = element2.clientHeight;  // Write (invalidates layout) element2.style.height = (h3 * 2) + 'px';  // Read (triggers layout) var h4 = element3.clientHeight;  // Write (invalidates layout) element3.style.height = (h4 * 2) + 'px';

clientHeight,这个属性是需要计算得到的,于是就会触发浏览器的一次layout。我们来利用chrome(v47.0)的开发者工具看下(截图中的timeline record已经经过筛选,仅显示layout):

JavaScript中的DOM操作为什么很慢

上面的例子中,代码首先修改了一个元素的样式,接下来读取另一个元素的clientHeight属性,由于之前的修改导致当前DOM被标记为脏,为了保证能准确的获取这个属性,浏览器会进行一次layout(我们发现chrome的开发者工具良心的提示了我们这个性能问题)。

优化这段代码很简单,预先读取所需要的属性,在一起修改即可。

// Read var h2 = element1.clientHeight;  var h3 = element2.clientHeight;  var h4 = element3.clientHeight;  // Write (invalidates layout) element1.style.height = (h2 * 2) + 'px';  element2.style.height = (h3 * 2) + 'px';  element3.style.height = (h4 * 2) + 'px';

看下这次的情况:

JavaScript中的DOM操作为什么很慢

下面再介绍一些其他的优化方案。

最小化layout的方案

上面提到的一个批量读写是一个,主要是因为获取一个需要计算的属性值导致的,那么哪些值是需要计算的呢?

这个链接里有介绍大部分需要计算的属性:http://gent.ilcore.com/2011/03/how-not-to-trigger-layout-in-webkit.html

再来看看别的情况:

面对一系列DOM操作

针对一系列DOM操作(DOM元素的增删改),可以有如下方案:

  • documentFragment

  • display: none

  • cloneNode

比如(仅以documentFragment为例):

var fragment = document.createDocumentFragment();  for (var i=0; i < items.length; i++){    var item = document.createElement("li");   item.appendChild(document.createTextNode("Option " + i);   fragment.appendChild(item); } list.appendChild(fragment);

这类优化方案的核心思想都是相同的,就是先对一个不在Render tree上的节点进行一系列操作,再把这个节点添加回Render tree,这样无论多么复杂的DOM操作,最终都只会触发一次layout。

面对样式的修改

针对样式的改变,我们首先需要知道并不是所有样式的修改都会触发layout,因为我们知道layout的工作是计算RenderObject的尺寸和大小信息,那么我如果只是改变一个颜色,是不会触发layout的。

这里有一个网站CSS triggers,详细列出了各个CSS属性对浏览器执行layout和paint的影响。

像下面这种情况,和上面讲优化的部分是一样的,注意下读写即可。

elem.style.height = "100px"; // mark invalidated  elem.style.width = "100px";  elem.style.marginRight = "10px"; elem.clientHeight // force layout here

但是要提一下动画,这边讲的是js动画,比如:

function animate (from, to) {    if (from === to) return    requestAnimationFrame(function () {     from += 5     element1.style.height = from + "px"     animate(from, to)   }) }  animate(100, 500)

动画的每一帧都会导致layout,这是无法避免的,但是为了减少动画带来的layout的性能损失,可以将动画元素绝对定位,这样动画元素脱离文本流,layout的计算量会减少很多。

使用requestAnimationFrame

任何可能导致重绘的操作都应该放入requestAnimationFrame

在现实项目中,代码按模块划分,很难像上例那样组织批量读写。那么这时可以把写操作放在requestAnimationFrame的callback中,统一让写操作在下一次paint之前执行。

// Read var h2 = element1.clientHeight;  // Write requestAnimationFrame(function() {    element1.style.height = (h2 * 2) + 'px'; });  // Read var h3 = element2.clientHeight;  // Write requestAnimationFrame(function() {    element2.style.height = (h3 * 2) + 'px'; });

JavaScript中的DOM操作为什么很慢

可以很清楚的观察到Animation Frame触发的时机,MDN上说是在paint之前触发,不过我估计是在js脚本交出控制权给浏览器进行DOM的invalidated check之前执行。

其他注意点

除了由于触发了layout而导致性能问题外,这边再列出一些其他细节:

缓存选择器的结果,减少DOM查询。这里要特别提下HTMLCollection。HTMLCollection是通过document.getElementByTagName得到的对象类型,和数组类型很类似但是每次获取这个对象的一个属性,都相当于进行一次DOM查询:

var divs = document.getElementsByTagName("div");  for (var i = 0; i < divs.length; i++){  //infinite loop  document.body.appendChild(document.createElement("div")); }

比如上面的这段代码会导致***循环,所以处理HTMLCollection对象的时候要做些缓存。

另外,减少DOM元素的嵌套深度并优化css,去除无用的样式对减少layout的计算量有一定帮助。

在DOM查询时,querySelectorquerySelectorAll应该是***的选择,它们功能***大,但执行效率很差,如果可以的话,尽量用其他方法替代。

自己对View层的想法

上面的内容理论方面的东西偏多,从实践的角度来看,上面讨论的内容,正好是View层需要处理的事情。已经有一个库FastDOM来做这个事情,不过它的代码是这样的:

fastdom.read(function() {    console.log('read'); });  fastdom.write(function() {    console.log('write'); });

问题很明显,会导致callback hell,并且也可以预见到像FastDOM这样的imperative的代码缺乏扩展性,关键在于用了requestAnimationFrame后就变成了异步编程的问题了。要让读写状态同步,那必然需要在DOM的基础上写个Wrapper来内部控制异步读写,不过都到了这份上,感觉可以考虑直接上React了。

到此,相信大家对“JavaScript中的DOM操作为什么很慢”有了更深的了解,不妨来实际操作一番吧!这里是编程网网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

JavaScript中的DOM操作为什么很慢

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

怎么在JavaScript中操作DOM

本篇文章给大家分享的是有关怎么在JavaScript中操作DOM,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。javascript是一种什么语言javascript是一种动态类
2023-06-14

javascript中dom的作用是什么

javascript中dom的作用是什么?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。JavaScript是什么JS是JavaScript的简称,它是一种直译式
2023-06-14

JavaScript中dom节点的作用是什么

本篇文章给大家分享的是有关JavaScript中dom节点的作用是什么,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。javascript是一种什么语言javascript是一种
2023-06-14

为什么 Go 中读写文件比 Perl 慢很多?

为什么 Go 中读写文件比 Perl 慢很多?这是很多开发者在使用这两种编程语言时经常遇到的问题。在这篇文章中,php小编草莓将为您解答这个问题。在比较 Go 和 Perl 读写文件的速度时,我们需要考虑到两个关键因素:语言特性和底层实现。
为什么 Go 中读写文件比 Perl 慢很多?
2024-02-09

javascript中dom指的是什么

这篇文章将为大家详细讲解有关javascript中dom指的是什么,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。JavaScript的作用是什么1、能够嵌入动态文本于HTML页面。2、对浏览器事件做出响应
2023-06-14

react中的DOM操作怎么实现

这篇“react中的DOM操作怎么实现”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“react中的DOM操作怎么实现”文章吧
2023-06-05

JavaScript中new操作符的作用是什么

这篇文章给大家介绍JavaScript中new操作符的作用是什么,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。JavaScript的特点1.JavaScript主要用来向HTML页面添加交互行为。2.JavaScrip
2023-06-06

为什么我的电脑关机很慢都要3.4分钟(win7系统)

很多人一关机基本上不会去管自己电脑的关机时间,事实上小编感觉关机时间的长短有时候也代表着你电脑即将发生的问题。像很多人要关电脑没有把所有东西都关掉的习惯就关电脑了,这样关机要2.3分钟我们还好理解。但是有点人明明没开php什么东西,关机都要
2023-06-01

揭秘 JavaScript 原型链:它是如何工作的以及为什么它很重要?

JavaScript 原型链是一个强大的特性,允许您从一个对象继承另一个对象的属性和方法。它被广泛用于面向对象编程和代码复用。本文将深入探讨 JavaScript 原型链,包括它是如何工作的、为什么它很重要以及如何使用它来编写更有效、更可维护的代码。
揭秘 JavaScript 原型链:它是如何工作的以及为什么它很重要?
2024-02-06

windows操作系统启动慢的原因是什么

本篇文章为大家展示了windows操作系统启动慢的原因是什么,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。都希望电脑一开机就可以立即进入Windows系统而不用等待,但由于种种原因常常未能如愿,甚至
2023-06-14

编程热搜

目录