React useEffect、useLayoutEffect底层机制及区别介绍
useEffect
useEffect 是 React 中的一个 Hook,允许你在函数组件中执行副作用操作。副作用(Side Effects)是指组件中不直接涉及渲染过程的行为,例如数据获取、事件监听、订阅、设置定时器、手动修改 DOM 等。
基本用法:
不设置依赖
- 第一次渲染完成后,执行
callback
,等价于componentDidMount
- 在组件每一次更新完毕后,也会执行
callback
,等价于componentDidUpdate
下面的写法可以获取到最新的状态值
const Demo = function Demo() { let [num, setNum] = useState(0), [x, setX] = useState(100); useEffect(() => { // 获取最新的状态值 console.log('@1', num); }); const handle = () => { setNum(num + 1); }; return <div className="demo"> <span className="num">{num}</span> <Button type="primary" size="small" onClick={handle}> 新增 </Button> </div>; };
设置空数组,无依赖
只有第一次渲染完毕后,才会执行callback
,每一次视图更新完毕后,callback
不再执行,「类似于componentDidMount
」
初次渲染,打印@1 @2 ,点击按钮之后,只打印出@1
const Demo = function Demo() { let [num, setNum] = useState(0), [x, setX] = useState(100); useEffect(() => { // 获取最新的状态值 console.log('@1', num); }); useEffect(() => { console.log('@2', num); }, []); const handle = () => { setNum(num + 1); }; return <div className="demo"> <span className="num">{num}</span> <Button type="primary" size="small" onClick={handle}> 新增 </Button> </div>; };
设置多个依赖
- 第一次渲染完毕会执行
callback
- 依赖的状态值(或者多个依赖状态中的一个)发生变化,也会出发
callback
执行 - 但是依赖的状态如果没有变化,在组件更新的时候,
callback
是不会执行
const Demo = function Demo() { let [num, setNum] = useState(0), [x, setX] = useState(100); useEffect(() => { console.log('@3', num); }, [num]); const handle = () => { setNum(num + 1); }; return <div className="demo"> <span className="num">{num}</span> <Button type="primary" size="small" onClick={handle}> 新增 </Button> </div>; };
返回值是一个函数
- 初始渲染之后返回一个小函数,放到链表当中
- 如果组件更新,会通过
updateEffect
会把上一次返回的函数执行「可以“理解为”上一次渲染的组件释放了」
const Demo = function Demo() { let [num, setNum] = useState(0), [x, setX] = useState(100); useEffect(() => { return () => { // 获取的是上一次的状态值 console.log('@4', num); }; }); const handle = () => { setNum(num + 1); }; return <div className="demo"> <span className="num">{num}</span> <Button type="primary" size="small" onClick={handle}> 新增 </Button> </div>; };
总结
useEffect的使用环境
useEffect必须是在函数的最外层上下文中调用,不能把其嵌入到条件判断、循环等操作语句中。
下面是错误的写法:
const Demo = function Demo() { let [num, setNum] = useState(0); if (num > 5) { useEffect(() => { console.log('OK'); }); } const handle = () => { setNum(num + 1); }; return <div className="demo"> <span className="num">{num}</span> <Button type="primary" size="small" onClick={handle}> 新增 </Button> </div>; };
正确的应该是这样,把逻辑写在useEffect内部:
useEffect 中发送请求
首先模拟一个请求
错误示例
这样写会直接进行报错。useEffect
如果设置返回值,则返回值必须是一个函数
「代表组件销毁时触发」;下面案例中,callback
经过async
的修饰,返回的是一个promise
实例,不符合要求,所以报错!
用.then获取数据
直接调用queryData
,通过.then
获取数据
在useEffect创建一个函数
在useEffect
返回值里创建一个函数并调用
总结
useLayoutEffect
useLayoutEffect
和 useEffect
具有相似的 API 和用法,但它们的执行时机不同。useLayoutEffect 是 同步执行 的,它会在浏览器 绘制(paint)之前 执行副作用操作。
基本用法:
useLayoutEffect 和useEffect区别
useLayoutEffect
会阻塞浏览器渲染真实DOM,优先执行Effect链表
中的callback
;
useEffect
不会阻塞浏览器渲染真实DOM,在渲染真实DOM的同时,去执行Effect链表中的callback
;
useLayoutEffect
设置的callback
要优先于useEffect
去执行- 在两者设置的
callback
中,依然可以获取DOM元素「因为这是的DOM对象已经创建了,区别只是浏览器是否渲染」 - 如果在
callback
函数中又修改了状态值「视图又要更新」- useEffect:浏览器肯定是把第一次的真实DOM已经绘制,再去渲染第二次的真实
- DOMuseLayoutEffect:浏览器是把两次真实DOM的渲染,合并在一起渲染
视图更新的步骤
1、基于babel-preset-react-app把JSX便衣乘
createElement`格式
2、把createElement
执行,创建virtualDOM
3、基于root.render
方法把virtual
变为真实DOM对象「DOM- DIFF」useLayoutEffect
阻塞第4步操作,先去执行Effect链表中的方法「同步操作」useEffect
第4步操作和Effect链表中的方法执行,是同时进行的「异步操作」
4、浏览器渲染和绘制真实DOM对象
下面先打印出useLayoutEffect
,再打印出useEffect
执行时机:浏览器渲染的关系
useEffect:
useEffect 是 异步 执行的,它是在 React 更新完 DOM 后(即浏览器绘制之后)执行的。浏览器渲染通常分为几个阶段:
浏览器渲染:更新 DOM、进行布局计算、绘制页面等。
React 执行副作用(useEffect):在页面渲染完成后,再去执行副作用。
这种顺序意味着 useEffect 中的副作用操作不会阻塞浏览器渲染。换句话说,React 在触发 useEffect 后,会立即开始浏览器的绘制过程,所以不会影响页面的视觉展示。
举个例子,如果你使用 useEffect 来发起 API 请求,React 会等到浏览器完成渲染后,再去发起请求,不会影响渲染速度。
useLayoutEffect:
useLayoutEffect 与 useEffect 的最大区别是它会 同步执行,并且会在 DOM 更新后但在浏览器渲染(绘制)之前执行。执行顺序如下:
React 更新虚拟 DOM 和 DOM:这一步会根据组件的变化更新页面结构。
useLayoutEffect 执行:同步执行副作用,这时 DOM 已经更新,但浏览器还没进行绘制。
浏览器绘制:完成页面渲染。
这意味着 useLayoutEffect 会 阻塞 渲染,直到它执行完成后,浏览器才会进行页面渲染。因此,如果 useLayoutEffect 中执行的操作非常耗时,可能会导致页面渲染延迟,影响用户体验。
对浏览器渲染的影响
useEffect 的影响:
异步执行:不会阻塞页面渲染,可以在渲染完成后执行副作用操作。
不会影响页面视觉:由于 useEffect 在浏览器渲染完成后才执行,它不会导致页面布局变化,也不会造成视觉闪烁。
性能优化:因为是异步执行的,所以浏览器渲染不会被卡住,页面的响应速度和流畅性得到保证。
useLayoutEffect 的影响:
同步执行:会在 DOM 更新后但在页面渲染之前立即执行副作用,阻塞浏览器的绘制过程。
可能影响性能:由于同步执行,浏览器渲染必须等待 useLayoutEffect 完成,如果副作用中有复杂的操作,可能会导致页面加载时间延迟或出现白屏现象。
影响布局计算:适合用于获取 DOM 元素的大小、位置等布局信息,因为它在页面渲染之前执行,你可以确保你拿到的是最新的、正确的布局信息。
使用场景
useEffect 的常见场景:
数据获取:例如从 API 获取数据,或者发起网络请求。
事件监听和取消订阅:如为组件添加事件监听器(例如 resize 或 scroll),并在组件卸载时移除它们。
定时器/计时器:设置定时任务(如 setInterval 或 setTimeout),并在组件卸载时清理。
更新状态:例如当某个副作用触发时更新组件状态,通常与 DOM 操作无关。
例如: 通过 useEffect 实现获取数据并更新状态:
在组件中监听 resize 或 scroll 事件时,useEffect 是一个常见的选择。你可以在 useEffect 中添加事件监听器,并在组件卸载时清除这些监听器。
使用定时器来执行某些定期操作,如每隔一定时间更新状态。
副作用可能会触发状态更新,特别是在某些条件发生变化时,比如从 API 获取数据或处理输入事件等。useEffect 在 inputValue 改变时触发,设置一个 500 毫秒的延迟,用 setTimeout 更新 delayedValue。每次 inputValue 更新时,都会清理上一个定时器,避免旧的定时器执行。这样,delayedValue 会延迟显示输入框的值,实现了一个防抖的效果。
useLayoutEffect 的常见场景:
DOM 操作:需要在页面渲染之前操作 DOM(比如滚动条位置、修改样式、元素大小调整等)。
获取布局信息:例如测量 DOM 元素的宽度、高度或位置,因为这些信息可能在浏览器绘制过程中发生变化,所以你必须在渲染之前获取。
修复布局闪烁:如果你需要在页面渲染之前进行 DOM 操作,否则会导致闪烁或视觉不一致。
例如: 使用 useLayoutEffect 获取 DOM 元素尺寸:
使用 useLayoutEffect 来确保在浏览器绘制页面之前,能获取到最新的 DOM 元素的尺寸。
性能对比
- useEffect 的性能优势:由于是异步执行,它不会阻塞浏览器的渲染过程。即使副作用中有较重的操作(如网络请求、设置定时器等),它们也会在浏览器渲染完成后执行,不会影响页面渲染速度。
- useLayoutEffect 的性能成本:由于它是同步执行,并且会阻塞浏览器绘制,可能会导致页面渲染的延迟,特别是在副作用操作比较复杂时(比如大量的 DOM 计算)。如果在 useLayoutEffect 中执行了复杂的逻辑,它可能会影响页面的响应速度,给用户带来不流畅的体验。
上一篇:JavaScript 中问号的三种用法 ??和?.以及?:
栏 目:JavaScript
下一篇:Vue.js 的计算属性和侦听器详解(提升数据处理与交互的关键工具)
本文标题:React useEffect、useLayoutEffect底层机制及区别介绍
本文地址:https://www.fushidao.cc/wangluobiancheng/23695.html
您可能感兴趣的文章
- 07-21Vue.js 的计算属性和侦听器详解(提升数据处理与交互的关键工具)
- 07-21React useEffect、useLayoutEffect底层机制及区别介绍
- 07-21JavaScript 中问号的三种用法 ??和?.以及?:
- 07-21JavaScript检查变量类型的常用方法
- 07-21一文详解javascript语言中的类(class)
- 07-21使用Vue3实现一个简单的思维导图组件
- 02-11js中基本事件的总结(onclick、onblur、onchange等)
- 02-11详解如何在Node.js中使用中间件处理请求
- 02-11Vue3中Provide和Inject的用法及工作原理详解
- 02-11Vue+vant实现图片上传添加水印


阅读排行
推荐教程
- 04-23JavaScript Array实例方法flat的实现
- 04-23Vue3使用v-if指令进行条件渲染的实例代码
- 04-23THREE.JS使用TransformControls对模型拖拽的代码实例
- 04-23vue3+ts项目搭建的实现示例
- 07-21JavaScript检查变量类型的常用方法
- 04-23JavaScript实现下载超大文件的方法详解
- 07-21JavaScript 中问号的三种用法 ??和?.以及?:
- 04-23vue如何使用pdf.js实现在线查看pdf文件功能
- 04-23vue.js调用python脚本并给脚本传数据
- 12-18使用JavaScript遍历输出页面中的所有元素的方法详解