获取元素尺寸/位置对性能的影响
实际开发中我们经常需要获取某个DOM节点的尺寸或位置来做一些逻辑上的计算,但获取它的成本在大部分场景中是高昂的,因为blink要确保我们获取到的值是正确的,这就需要blink在获取前强制重新计算布局,之后再返回结果,这就造成了潜在的性能问题。
通常我们使用getBoundingClientRect
来获取元素尺寸及其相对于视口的位置,来看看调用这个API后在blink层面做了什么事情。
首先构造个简单页面:
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。