useEffect 与 useLayoutEffect 执行时机

5,918 阅读2分钟

useLayoutEffectuseEffect 的结构、功能相同,区别只是它们在事件循环中的调用时机不同

生命周期先不谈,只说事件循环与渲染相关

useEffect

useEffect 的回调函数是【异步宏任务】,在下一轮事件循环才会执行。根据 JS 线程与 GUI 渲染线程互斥原则,在 JS 中页面的渲染线程需要当前事件循环的宏任务与微任务都执行完,才会执行渲染线程,渲染页面后,退出渲染线程,控制权交给 JS 线程,再执行下一轮事件循环。

  1. 好处:这使得它适用于许多常见的副作用场景,比如设置订阅和事件处理等情况,因为绝大多数操作不应阻塞浏览器对屏幕的渲染更新。

  2. 坏处:产生二次渲染问题,第一次渲染的是旧的状态,接着下一个事件循环中,执行改变状态的函数,组件又携带新的状态渲染,在视觉上,就是二次渲染。

下面 gif 图,先渲染数字 1,再渲染数字 2,即二次渲染

useLayoutEffect

而 useLayoutEffect 与 componentDidMount、componentDidUpdate 生命周期钩子是【异步微任务】,在渲染线程被调用之前就执行。这意味着回调内部执行完才会更新渲染页面,没有二次渲染问题。

  1. 好处:没有二次渲染问题,页面视觉行为一致。

  2. 坏处:在回调内部有一些运行耗时很长的代码或者循环时,页面因为需要等 JS 执行完之后才会交给渲染线程绘制页面,等待时期就是白屏效果,即阻塞了渲染。

下面 gif 图,并没有渲染数字 1,而是直接渲染数字 2