之前看到说操作DOM
很慢,要尽量少的去操作DOM
。当时没有细究,以致于有时候写js还是会纠结,是直接操作DOM
还是处理数据后refresh
好一点。今天看到一篇文章,虽然还没能完全消化但也是学到不少,顺带着还get了Dev tools
下的timeline
的正确使用方式,在这里稍微整理一下:
一张页面是如何呈现出来的
简单来说,就是:
- 解析
HTML
,生成一棵DOM tree
- 解析
css
样式,结合DOM tree
生成一棵Render tree
。render tree
与DOM tree
并不完全对应,一个简单的例子就是display:none
的元素虽然存在于DOM tree
中,但是因为不需要被绘制所以不会出现在render tree
上。 - 对
Render tree
的各个节点计算布局信息,比如box
的位置与尺寸。 - 利用浏览器的UI层对
Render tree
进行绘制。
在明确了页面的呈现过程后,有个问题就呼之欲出了,那就是:
什么因素会影响页面的渲染速度
事实上,paint
就是一个耗时的过程,而第三点中的layout
是一个更为耗时的过程。layout
发生在页面渲染前,用来计算文档中DOM
元素的位置和大小。第一次加载了html
文档后就会进行layout
操作,之后的js
执行和css
样式的改变也会触发浏览器去执行layout
。甚至一次layout
会牵涉到整个文档布局的重新计算。但是layout
又无法避免,所以我们需要尽量少的去让游览器做layout
。
一般情况下,浏览器的layout
是lazy
的,也就是说:在js
脚本执行时,是不会去更新DOM
的,任何对DOM
的修改都会被暂存在一个队列中,在当前js的执行上下文完成执行后,会根据这个队列中的修改进行一次layout
。然而有时候我们希望在js代码中立刻获取最新的DOM
节点信息,这种情况下浏览器就不得不提前执行layout
,这是导致DOM
性能问题的主因。
一般来说,如下的操作会触发浏览器执行layout
:
- 通过
js
获取需要计算的DOM
属性 - 添加或删除
DOM
元素 resize
浏览器窗口大小- 改变字体
css
伪类的激活,比如:hover
- 通过
js
修改DOM
元素样式且该样式涉及到尺寸的改变