看完就懂 useLayoutEffect

差异

useLayoutEffect 与大家熟悉的 useEffect 语法完全一致,从产生副作用的角度上看,功能上也是一样的,唯一差别就是调用时机。

useEffect 会在画面绘制后异步 执行,而 useLayoutEffect 会在画面绘制前同步执行。为了讲清楚这个时机的具体区别,得先复习一下浏览器渲染页面的过程。

注意最后 js 运行的那一块,useLayoutEffect 和 useEffect 就分别位于 paint 之前和之后。

执行的顺序是:

  • useLayoutEffect
  • 画面绘制
  • 下一轮 js 运行 useEffect

顺便我们也能看出来,useLayoutEffect 之所以叫 useLayoutEffect 就是因为它的运行时间点沾着 layout。

使用场景

知道这两个函数的区别,我们还需要知道,到底什么时候用 useLayoutEffect 呢?

答案是,如果进行了 DOM 操作,且这个 DOM 操作会引起回流(reflow)、重绘(repaint),那么就应该使用 useLayoutEffect,例如:

jsx 复制代码
function Tooltip() {
  const ref = useRef<HTMLDivElement>(null);
  const [pos, setPos] = useState({ top: 0, left: 0 });

  // 如果用 useEffect,这里会先渲染一次默认位置,再跳到正确位置 → 可能会造成闪烁
  useLayoutEffect(() => {
    const rect = ref.current!.getBoundingClientRect();
    setPos({
      top: rect.top + rect.height + 8,
      left: rect.left + rect.width / 2,
    });
  }, []);

  return (
    <>
      <div ref={ref}>hover me</div>
      <div style={{ position: 'fixed', top: pos.top, left: pos.left }}>tooltip</div>
    </>
  );
}

因为如果你用 useEffect,在浏览器绘制之后又要重新跑一遍 reflow、repaint,用户可能会看到画面"闪烁"。

如果你有代码洁癖,想要一个最优解,那么你确实该按上面说的这么做,但是事实上在这个场景使用 useEffect 可能也不会有很明显的问题。

其实即使是官网的例子里,作为反模式使用 useEffect,用户也不会感知到明显的"闪烁",因为两次渲染的时间其实是快到肉眼看不清的,为了确定真的存在区别你还要故意写个 while 循环卡一下主进程。

既然一般情况下无论 useEffect 和 useLayoutEffect 都不会有明显区别,那么我觉得,作为一个有专业素养的 React 开发者,应该优先使用 useEffect,只在 reflow、repaint 造成闪烁的场景下,使用 useLayoutEffect。

当然,useEffect本身也不能乱用,之前在useEffect 清除计划里已经讲述了它的必要使用场景。

总结

useLayoutEffect 适用于"需要在浏览器绘制前同步完成的副作用",典型场景是读取布局信息并立即修改 DOM,避免视觉闪动。

但因其会阻塞浏览器绘制,影响性能,因此不应滥用。在绝大多数副作用场景下,优先使用 useEffect,只有在感知到闪动才改为使用 useLayoutEffect。

相关推荐
parade岁月1 小时前
DOM 里有 Tailwind class,为什么样式还是不生效?v4 闭环修复实战
前端·vue.js
ashuicoder1 小时前
vue文件自动生成路由会成为主流
前端·vue.js
白中白121381 小时前
Vue系列-4
前端·javascript·vue.js
Ai runner1 小时前
Show call stack in perfetto from json input
java·前端·json
晴殇i1 小时前
前端防调试攻防战:如何保护你的JavaScript代码不被“偷窥”?
前端·javascript·面试
清粥油条可乐炸鸡2 小时前
tailwind-variants基本使用
前端·css
2301_816997882 小时前
虚拟DOM与Diff算法
前端·vue.js·算法
清粥油条可乐炸鸡2 小时前
Vite创建react项目
前端·vue.js