获取元素尺寸/位置对性能的影响
实际开发中我们经常需要获取某个DOM节点的尺寸或位置来做一些逻辑上的计算,但获取它的成本在大部分场景中是高昂的,因为blink要确保我们获取到的值是正确的,这就需要blink在获取前强制重新计算布局,之后再返回结果,这就造成了潜在的性能问题。
通常我们使用getBoundingClientRect
来获取元素尺寸及其相对于视口的位置,来看看调用这个API后在blink层面做了什么事情。
首先构造个简单页面:
<button id="btn">get dom size</button>
<div id="a" style="height: 100px;"></div>
document.getElementById("btn").addEventListener("click", () => {
const rect = document.getElementById("a").getBoundingClientRect();
console.log(rect);
});
在 element.cc 中可以找到 Blink 对于getBoundingClientRect
的实现:
DOMRect* Element::getBoundingClientRect() {
GetDocument().EnsurePaintLocationDataValidForNode(
this, DocumentUpdateReason::kJavaScript);
return DOMRect::FromFloatRect(GetBoundingClientRectNoLifecycleUpdate());
}
API被调用后首先要确保当前节点绘制位置的数据是正确的,进入到EnsurePaintLocationDataValidForNode
中可以看到它是这么实现的:
void Document::EnsurePaintLocationDataValidForNode(
const Node* node,
DocumentUpdateReason reason) {
DCHECK(node);
if (!node->InActiveDocument())
return;
DisplayLockUtilities::ScopedForcedUpdate scoped_update_forced(node);
// For all nodes we must have up-to-date style and have performed layout to do
// any location-based calculation.
UpdateStyleAndLayout(reason);
}
经过简单的内部状态检查,就到了UpdateStyleAndLayout,这是Document Class上的一个方法,说明一旦调用getBoundingClientRect,不论对哪个层级的dom调用,recalculate style的scope始终是整个Document。