在官方 React 文档中,对于 useLayoutEffect
,其中提到:
The signature is identical to
useEffect
, but it fires synchronously after all DOM mutations. Use this to read layout from the DOM and synchronously re-render. Updates scheduled insideuseLayoutEffect
will be flushed synchronously, before the browser has a chance to paint.
此外,在 useLayoutEffect
中,我们能够在浏览器实际重新绘制之前读取更新的尺寸。
react 是如何做到这一点的?
最佳答案
所以@Marko 是对的。
浏览器渲染周期分为几个阶段,相关的阶段是:
- 样式 - 将 HTML 与 CSS 合并
- 布局 - 计算所有元素的大小和位置
- 油漆
useLayoutEffect 在布局之后、绘制之前运行
要实现这一点,您需要做的(或 React 所做的)就是在 DOM 树中添加或更改元素后同步运行 useLayoutEffect
函数。
除非您释放线程,否则浏览器无法运行绘画阶段。
如果您向浏览器询问元素的大小,它会同步运行布局阶段来给您答案。
这是一个片段和一个 sandbox演示这个想法:
const conatiner = document.getElementById("container");
function busyWait() {
const array = new Array(100000).fill(Math.random());
let sum = 0;
array.forEach((val, index) => {
const item = array.find((val, i) => i === index);
sum = sum + item;
});
return sum;
}
function addElement() {
const newElement = document.createElement("p");
newElement.innerText =
"this is a paragraph, browser need to measure it to know its size";
if (conatiner) {
conatiner.appendChild(newElement);
console.log(
"layout phase element size",
JSON.stringify(newElement.getBoundingClientRect())
);
const sum = busyWait();
console.log(sum);
setTimeout(() => {
console.log(
"after paint element size",
JSON.stringify(newElement.getBoundingClientRect())
);
}, 10);
}
}
setTimeout(addElement, 100);
<div>
we are going to test layout / replaint stages
<div id="container" style="width: 200px;"></div>
</div>
- 我添加一个新元素
- 询问其尺寸
- 记录它
- 然后忙等待(不将线程释放给浏览器) => 您可以在日志中看到正确的大小,但在屏幕上看不到该元素
关于reactjs - 在浏览器有机会绘制之前,React 如何正确测量 useLayoutEffect Hook 中的 DOM 元素?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60769022/