Z
Toggle Nav

获取元素尺寸/位置对性能的影响

实际开发中我们经常需要获取某个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。