一直听说DOM很慢,要尽量少操作DOM,所以想进一步探究为什么大家这么说.在网上了解到一些资料,这里整理一下。首先,DOM对象本身也是一个js对象,所以严格来说并不是操作这个对象慢,而是操作这个对象之后,会触发一些浏览器的行为,比如布局(layout)和绘图(画)。下面主要介绍这些浏览器行为,解释一个页面最终是如何呈现的,同时也从代码的角度解释一些不好的做法和一些优化方案。浏览器如何渲染页面浏览器有很多模块,其中渲染引擎模块负责渲染页面。比较熟悉的有WebKit和Gecko等,这里只涉及本模块的内容。先用文字大概解释一下这个过程:解析HTML,生成DOM树解析各种样式,结合DOM树,生成Render树计算Render树每个节点的布局信息,比如根据框的位置和大小到Render树并使用浏览器的UI层进行绘制。DOM树和Render树上的节点并不是一一对应的。例如,“display:none”节点只会存在于DOM树上,不会出现在Render树上。因为这个节点不需要绘制。上图是Webkit的基本流程,可能在术语上和Gecko有所不同。这里是Gecko的流程图,但是文章后面的内容统一使用Webkit的术语。影响页面渲染的因素有很多,比如链接的位置会影响首屏的渲染。但是这里我们主要关注布局相关的内容。绘画是一个耗时的过程,但布局是一个更耗时的过程。我们无法确定布局必须从上到下还是从下到上,甚至一次布局都会涉及到整个文档布局的重新计算。但是布局肯定是不可避免的,所以我们主要是想尽量减少布局的次数。什么情况下浏览器会进行布局在考虑如何尽量减少布局次数之前,首先要了解浏览器什么时候会进行布局。版图(reflow)一般称为版图。该操作用于计算元素在文档中的位置和大小,是渲染前的重要步骤。当HTML第一次加载时,会有一个布局,js脚本的执行和样式的改变也会导致浏览器执行布局,这就是本文的主要内容。一般情况下,浏览器的布局是惰性的,也就是说:js脚本执行的时候,DOM是不会更新的。对DOM的任何修改都会被临时存储在一个队列中,并在当前的js执行上下文中完成。执行后会根据这个队列中的修改进行一次布局。但是,有时希望在js代码中立即获取最新的DOM节点信息,而浏览器不得不提前执行布局,这是造成DOM性能问题的主要原因。下面的操作会打破套路,触发浏览器执行布局:通过js获取需要计算的DOM属性添加或删除DOM元素这个样式涉及到大小的变化。举个例子直观感受一下://Readvarh1=element1.clientHeight;//Write(invalidateslayout)element1.style.height=(h1*2)+'px';//Read(triggerslayout)varh2=element2。clientHeight;//Write(invalidateslayout)element2.style.height=(h2*2)+'px';//Read(triggerslayout)varh3=element3.clientHeight;//Write(invalidateslayout)element3.style.height=(h3*2)+'px';clientHeight,这个属性需要计算,所以会触发浏览器布局。我们看一下chrome(v47.0)的开发者工具(截图中的timeline记录已经过过滤,只显示布局):在上面的例子中,代码先修改了一个元素的样式,然后读取另一个元素的clientHeight属性,由于之前的修改,当前DOM被标记为脏的。为了保证这个属性能够准确获取,浏览器会进行一次布局(我们发现chrome开发者工具良心提醒我们这个性能问题)。优化这段代码很简单,提前读取需要的属性,一起修改。//Readvarh1=element1.clientHeight;varh2=element2.clientHeight;varh3=element3.clientHeight;//写入(invalidateslayout)element1.style.height=(h1*2)+'px';element2.style.height=(h2*2)+'px';element3.style.height=(h3*2)+'px';看看这次的情况:下面会介绍一些其他的优化方案。最小化布局的方案上面说的批量读写是一个,主要是获取一个属性值需要计算,那么需要计算哪些值呢?这个链接介绍了大部分需要计算的属性:http://gent.ilcore.com/2011/03/how-not-to-trigger-layout-in-webkit.html我们再看看其他情况:面A一系列DOM操作对于一系列DOM操作(DOM元素的增删改查),可以实现如下方案:documentFragment显示:无cloneNode例如(仅以documentFragment为例):);for(vari=0;i
