跳到主要内容

认识

2023年02月22日
柏拉文
越努力,越幸运

一、认识


useLayoutEffectReact 中一个特殊的 Hook,它和 useEffect 类似,但在执行时机上有所不同,适用于那些需要在浏览器绘制前同步操作 DOM 的场景。

useLayoutEffect: 初始渲染或者状态更新之后, 在 commit 阶段中同步执行, 所以 useLayoutEffect callback 中代码执行会阻塞浏览器绘制。 useLayoutEffect 是在 DOM 更新之后,浏览器绘制之前,这样可以方便修改 DOM,获取 DOM 信息,这样浏览器只会绘制一次。修改 DOM ,改变布局就用 useLayoutEffect。由于 useLayoutEffect 是同步执行的,它会阻塞浏览器的绘制。如果回调函数执行时间过长,可能会导致页面卡顿。因此,除非必要,否则建议优先使用 useEffect 来避免影响用户体验。只在必须进行 DOM 测量或需要在绘制前进行布局调整时,才使用 useLayoutEffect,其余情况使用 useEffect 已足够满足需求。

useLayoutEffectuseEffect 调用顺序一致, 都遵循 先子后父。为什么呢? 本质上 commit 阶段处理的事情和 dom 元素有关系,commit 阶段生命周期是可以改变真实 dom 元素的状态的,所以如果在子组件生命周期内改变 dom 状态,并且想要在父组件的生命周期中同步状态,就需要确保父组件的生命周期执行时机要晚于子组件。

二、语法


useLayoutEffect(() => { doSomething },[a,b]);
  • 第一个参数: 初始渲染之前触发 doSomething; 组件重新渲染之前根据 [a,b] 决定是否触发 doSomething 副作用。(注意副作用是在渲染完成之前执行)

    • doSomething 返回函数:组件销毁时触发 doSomething 返回函数; 组件重新渲染后根据 [a,b] 决定是否触发 doSomething 返回函数,这时候先执行 doSomething 返回函数,后执行 doSomething 副作用。
  • [a,b]:

    • 如果为 [a,b] ,只有 a,b 更新引起的组件重新渲染之前才会重新调用 doSomething ,以及 doSomething 返回函数

    • 如果为 [] , 无论组件是否重新渲染,都不会重新调用 doSomething , 以及 doSomething 返回函数

    • 如果没有[a,b] 或者 [] 时,组件每次重新渲染都会调用 doSomething 包括 doSomething 的返回函数

三、用法


四、问题


4.1 useLayoutEffect 与 useEffect 的区别? 什么时候用 useLayoutEffect, 什么时候用 useEffect?

useLayoutEffect: 初始渲染或者状态更新之后, 在 commit 阶段中同步执行, 所以 useLayoutEffect callback 中代码执行会阻塞浏览器绘制。 useLayoutEffect 是在 DOM 更新之后,浏览器绘制之前,这样可以方便修改 DOM,获取 DOM 信息,这样浏览器只会绘制一次。修改 DOM ,改变布局就用 useLayoutEffect

useEffect: 初始渲染或者状态更新之后, 在 commit 阶段完成以后异步执行,所以 effect 回调函数不会阻塞浏览器绘制视图。useEffect 执行是在浏览器绘制视图之后,如果修改 DOM 布局放在 useEffect ,那 useEffect 执行是在浏览器绘制视图之后,接下来又改 DOM ,就可能会导致浏览器再次回流和重绘。而且由于两次绘制,视图上可能会造成闪现突兀的效果。除修改 DOM ,改变布局的场景外,其他情况都用 useEffect

useLayoutEffectuseEffect 调用顺序一致, 都遵循 先子后父。为什么呢? 本质上 commit 阶段处理的事情和 dom 元素有关系,commit 阶段生命周期是可以改变真实 dom 元素的状态的,所以如果在子组件生命周期内改变 dom 状态,并且想要在父组件的生命周期中同步状态,就需要确保父组件的生命周期执行时机要晚于子组件。