前言
DOM(Document Object Model)是一个与语言无关的、用来操作 XMl 和 HTML 文档的应用程序接口。在浏览器环境下,我们是通过 Javascript 来实现对 DOM 的操作。而针对 DOM 进行频繁操作会严重影响前端的体验和性能,下面我们就简单谈一下如何针对这种情况进行一些优化。
浏览器工作原理
先通过一张图来看一下浏览器是如何进行页面画渲染的:
首先通过HTML文件生成DOM Tree和CSS规则生成CSS Rule Tree,然后Javascript可以通过DOM API和CSSOM API对生成后的DOM Tree和CSS Rule Tree进行需要的操作。接下来CSS Rule Tree中的规则应用到DOM Tree上,构建出Rendering Tree。最后调用操作系统Native GUI的API绘制。
当Rendering Tree中的元素尺寸大小,布局位置,显示隐藏等改变时,需要对Rending Tree进行重新构建,这个过程就称为回流。回流完成后浏览器会重新绘制受影响的部分。回流必引起重绘,而重绘不一定引起回流。
天生就慢的DOM如何优化
实际上Javascipt的执行速度虽然比不上C、C++等预编译语言,但是和访问、操作DOM比起来那还是要快很多的。当你的Rendering Tree足够大时,每次使用Javascript调用DOM API去访问一个DOM元素,都会执行一个遍历过程,而当你又对获取的元素进行了操作,这就又会触发回流和重绘,这些都会大量消耗性能,很容易造成页面闪烁,卡顿等,影响用户体验。所以我们沿着这个思路就能找到要优化的点。
1. 使用尽可能少的DOM元素来构建页面
页面中过多的DOM元素,会使定位元素变得缓慢。当触发回流和重绘操作时会消耗更多的性能。还有就是在后期的维护上也会增加不必要的复杂性。如果页面真的很复杂,精简DOM不可行,可以用局部变量缓存获取过的DOM元素:
还可以使用一些高效的API,优先使用,而和在性能上和API相比较慢。
2. 减少回流和重绘
在操作DOM元素时,除了获取过程,还有对元素的外观,布局等进行的修改。触发回流的操作有以下类型:
添加或者删除可见的DOM元素
改变元素的位置
元素的内边距、外边距、边框、宽度、高度等尺寸的改变
元素文本内容和图片大小的改变导致高度和宽度的重新计算
浏览器窗口尺寸的改变
页面初始化渲染
通过上面的代码可以看出回流和重绘频繁发生。
而在实际的浏览器渲染过程中,会对上述过程做优化。连续的回流和重绘会被放入一个队列中,当任务积累到一定数量或者一定的时间间隔,就会刷新队列。减少了多次渲染的性能消耗。但是有些操作,浏览器为了返回正确的值,会忽略优化,立刻执行回流和重绘操作,比如:、、、等。
如何减少DOM的回流和重绘呢?我们可以从以下几个方面入手:
a. 在动态修改CSS样式时,不要逐条设置,可以使用类名
b. 对DOM的操作,留在Javascript内部,然后一次写回DOM,即实现离线处理
第一种,可以使用DocumentFragment的机制:DocumentFragment接口表示没有父级的最小文档对象,由于文档片段不是实际DOM结构的一部分,它是一个虚拟的dom节点,存在于内存中,所以对片段所做的更改不会影响文档,导致回流,影响性能。
第二种,使用cloneNode在外部更新节点然后再通过replace与原始节点互换,也只触发一次回流重绘。
还有一种方法,比较简单,将要操作的DOM设置为,操作完成后再设置为,这样只触发了两次回流。
c. 对于动画元素,尽可能使其脱离文档流,使用和定位。在进行动画制作时,如果能用opcity和transform实现,就不要使用其他属性。这两个属性只会触发composite,而不会触发回流。
总结
本文从浏览器的工作原理触发,简单说明了一下DOM慢的原因,然后给出了优化DOM操作的几个建议。主要目的就是为了精简DOM结构、减少DOM的回流和重绘。如果要对页面的性能瓶颈进行详细的分析,可以借助Chrome开发工具中的Performance面板。找到具体的原因,针对性的提出优化方案。
领取专属 10元无门槛券
私享最新 技术干货