Z

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

实际开发中我们经常需要获取某个DOM节点的尺寸或位置来做一些逻辑上的计算,但获取它的成本在大部分场景中是高昂的,因为blink要确保我们获取到的值是正确的,这就需要blink在获取前强制重新计算布局,之后再返回结果,这就造成了潜在的性能问题。

通常我们使用getBoundingClientRect来获取元素尺寸及其相对于视口的位置,来看看调用这个API后在blink层面做了什么事情。

首先构造个简单页面:

1<button id="btn">get dom size</button>
2<div id="a" style="height: 100px;"></div>
1document.getElementById("btn").addEventListener("click", () => {
2  const rect = document.getElementById("a").getBoundingClientRect();
3  console.log(rect);
4});

在 element.cc 中可以找到 Blink 对于getBoundingClientRect的实现:

1DOMRect* Element::getBoundingClientRect() {
2  GetDocument().EnsurePaintLocationDataValidForNode(
3      this, DocumentUpdateReason::kJavaScript);
4  return DOMRect::FromFloatRect(GetBoundingClientRectNoLifecycleUpdate());
5}

API被调用后首先要确保当前节点绘制位置的数据是正确的,进入到EnsurePaintLocationDataValidForNode中可以看到它是这么实现的:

 1void Document::EnsurePaintLocationDataValidForNode(
 2    const Node* node,
 3    DocumentUpdateReason reason) {
 4  DCHECK(node);
 5  if (!node->InActiveDocument())
 6    return;
 7
 8  DisplayLockUtilities::ScopedForcedUpdate scoped_update_forced(node);
 9
10  // For all nodes we must have up-to-date style and have performed layout to do
11  // any location-based calculation.
12  UpdateStyleAndLayout(reason);
13}

经过简单的内部状态检查,就到了UpdateStyleAndLayout,这是Document Class上的一个方法,说明一旦调用getBoundingClientRect,不论对哪个层级的dom调用,recalculate style的scope始终是整个Document。