1. 前言
- 碰巧今天面试
react
,问到这个问题(useEffect 和 useLayoutEffect有啥区别),都在射程范围内,那就来捋一捋
2. 是什么 what
useEffect
和useLayoutEffect
是 React 提供的两个副作用
钩子函数,用于在函数式组件中处理副作用操作
3. useEffect:
- useEffect 在组件
渲染完成后
执行,通常用于处理副作用操作,如数据获取、订阅事件、定时器等。- useEffect 的回调函数会在每次
渲染结束后
异步执行,不会阻塞
浏览器的渲染过程。- useEffect 的回调函数可以
返回
一个清理函数
,用于清除副作用操作,比如取消订阅、清除定时器等。- useEffect 的执行时机是在浏览器完成
绘制后
,对用户来说是不可见的。
4. useLayoutEffect:
- useLayoutEffect 在组件
渲染完
成后,浏览器绘制之前
执行,可以看作是 useEffect 的同步
版本。- useLayoutEffect 的回调函数会在每次渲染
结束后
同步执行,会阻塞
浏览器的渲染
过程,可能导致页面性能下降。- useLayoutEffect 的回调函数也可以
返回
一个清理函数
,用于清除副作用操作。- useLayoutEffect 的执行时机是在浏览器完成
绘制前
,对用户来说是可见的。
5. useEffect 与 useLayoutEffect 对比 demo
import React, { useEffect, useLayoutEffect, useState } from 'react'; const Demo = () => { const [count, setCount] = useState(0); useEffect(() => { console.log('useEffect'); document.title = `Count: ${count}`; }, [count]); useLayoutEffect(() => { console.log('useLayoutEffect'); // 模拟一个耗时操作 for (let i = 0; i < 1000000000; i++) { // Do something } document.title = `Count: ${count}`; }, [count]); return ( <div> <h1>Count: {count}</h1> <button onClick={() => setCount(count + 1)}>Increment</button> </div> ); }; export default Demo;
- 先打印了 useEffect,然后是 useLayoutEffect。
- 这是因为
useLayoutEffect
在更新 DOM 前同步
执行,可能会导致页面渲染的卡顿
。- 而
useEffect
在页面渲染完成后异步
执行,不会阻塞
页面的渲染
6. 表格对比
useEffect | useLayoutEffect | |
调用时机 | 组件渲染完成后异步执行 | 组件渲染完成后同步执行 |
执行时机 | 浏览器完成绘制后,对用户来说不可见 | 浏览器绘制前,对用户来说可见 |
阻塞渲染 | 不会阻塞浏览器的渲染过程 | 会阻塞浏览器的渲染过程 |
副作用操作 | 适用于大多数副作用操作 | 适用于需要立即执行、对布局有影响的副作用操作 |
清理函数 | 可以返回一个清理函数,用于清除副作用操作 | 可以返回一个清理函数,用于清除副作用操作 |
使用建议 | 在大多数情况下使用 useEffect |
在需要同步执行、对布局有影响的情况下使用 useLayoutEffect |
底层函数 | 调用的 mountEffectImpl方法 | 调用的 mountEffectImpl方法,在使用上也没什么差异,基本可以直接替换 |
- 虽然
useLayoutEffect
在某些情况下可能会导致页面性能下降,
但它在某些特定的场景下是非常有用的,例如在需要准确获取元素尺寸、计算布局等情况下。 - 在大多数情况下,使用
useEffect
是更安全和更推荐的选择。