useEffect,useLayoutEffect的基础知识和底层机制

useEffect 是 React 中一个重要的 Hook,用来处理组件的副作用操作。它的基础知识包括两个方面:执行时机和参数。

执行时机:

useEff ect 的执行时机包括两种情况:

  1. 组件挂载时,即第一次渲染之后。
  2. 组件更新时,即组件的 props 或状态发生变化时。

参数:

useEffect 接收两个参数:一个函数和一个依赖数组。

  1. 函数:这个函数会在组件的挂载和更新时被调用,它可以包含一些副作用操作,比如修改 DOM、发送网络请求等等。
  2. 依赖数组:这个数组是一个可选参数,用来指定 useEffect 的依赖项。如果一个组件中需要多次调用 useEffect,那么每次调用都需要指定依赖数组。当依赖数组中的任何一个值发生变化时,才会触发 useEffect 的重新执行。如果依赖数组为空,则表示 useEffect 没有任何依赖,只会在组件挂载和卸载时执行。

写法

  1. useEffect(callback):font color=red>这个参数表示只要组件发生更新,就会执行回调函数 callback,包括组件挂载时的初始化和后续状态或属性的更新。如果不需要依赖任何状态或属性,相当于类组件中的 componentDidMountcomponentDidUpdate 的合并。
  2. useEffect(callback, []):这个参数表示只在组件挂载时执行一次回调函数 callback,不会在组件更新时执行。这个参数当中传入的空数组 [] 表示没有任何依赖,因此只有在组件挂载时才会执行。
  3. useEffect(callback, [state1, state2, ...]) 这个参数表示在组件挂载时执行一次回调函数 callback,之后只有 state1state2 等指定的依赖状态发生改变时才会执行回调函数。如果多个状态都需要依赖,可以在数组中传入多个状态名称。
   useEffect( ()=>{
       return ()=>{
      //返回的小函数,会在组件释放的时候执行
      //如果组件更新,会把上一次函数执行,返回的小函数执行
       console.[log( '@4' ,num );
    });

需要注意的是,如果在 callback 中进行了一些异步操作,比如发起网络请求,需要在组件被销毁时主动取消这些异步操作,以防止内存泄漏。可以使用 useEffect 返回的清除函数来实现这个功能。例如:

useEffect(() => {
  const fetchData = async () => {
    // 发起异步请求
  }
  fetchData();

  return () => {
    // 取消异步请求
  }
}, [state1])

在这个例子中,fetchData 函数是一个异步操作,会在组件挂载和 state1 发生改变时被调用。在 useEffect 的回调函数中返回了一个清除函数,该函数会在组件被销毁时调用,用来取消异步请求操作。

底层机制:

基于effect链表实行!!!是异步的,effect 链表是指在一个 useEffect 钩子中,可以有多个副作用函数(或称为 effect 函数)。这些副作用函数会被按照它们声明的顺序依次执行,形成一条链表。当组件更新时,这些函数也会被按照它们声明的顺序依次执行。

在这里插入图片描述


useLayoutEffect和useEffect的细节

1.需求:在num大于5的时候我们再ueseffect

if ( num > 5){
useEffect( ( ) => {
lconsole.log ( 'OK');
});
}

会报错useEffect必须在函数的最外层上下文中调用,不能把其嵌入到条件判断、循环等操作语句中,应该这样做

useEffect(() => {
  if (num > 5) {
    console.log('OK');
  }
}, [num]);

2.模拟从服务器异步获取数据

错误写法:

在这里插入图片描述

在这个例子中,这里返回的是一个promise实例,而不是一个函数,我们试图在 useEffect 中使用 async/await,然后在函数体内调用 queryData 函数获取数据并打印到控制台上。但是你需要注意,useEffect 钩子中的副作用函数必须是同步的函数(或者返回一个清除函数的函数),而 async/await 函数是异步的函数,因此不能直接在 useEffect 中使用。

可以将副作用函数的主体逻辑封装到一个单独的函数中,然后在 useEffect 中调用这个函数:

const fetchData = async () => {
  let data = await queryData();
  console.log('成功: ', data);
};

useEffect(() => {
  fetchData();
  return () => {
        // 在组件卸载前,清除副作用
        console.log('清除 fetchData 副作用');
    };
}, []);

在这个例子中,我们将获取数据并打印到控制台的操作封装到了 fetchData 函数中,这个函数是一个异步的 async 函数。然后我们在 useEffect 中调用了这个 fetchData 函数。由于 useEffect 存在异步特性,因此不会阻塞其它的组件渲染,而副作用 fetchData 会在组件初次渲染时被调用,并且不会被重复执行(因为依赖列表是空的)。

需要注意的是,我们不能直接将 fetchData 函数放在 useEffect 中调用,而是需要先将它定义成一个变量。因为在 useEffect 中定义的函数会被作为闭包存储,并且它们的引用关系不会随着组件更新而改变。如果我们直接在 useEffect 中定义 async/await 函数,那么这个函数的引用关系会在每个组件更新时被重置,从而无法实现我们的预期效果。

3.什么是副作用函数??

根据上一个问题,fetchData 函数中包含了异步操作,比如网络请求,这些异步操作具有副作用。因此 fetchData 函数被视为具有副作用的函数。

在函数式编程语言中,副作用是指函数对外部环境造成的影响,这些影响可能会改变系统状态或返回一些不可预测的结果。在 JavaScript 中,一些常见的副作用包括修改全局状态、改变 DOM、读写本地存储、发出网络请求等等。

由于副作用的不确定性和可变性,React 为我们提供了钩子函数,例如 useEffect,以及一些限制和规范,来管理和控制副作用。在 React 中,我们应该尽量避免直接操作 DOM,而应该通过钩子函数和状态管理来更新视图和响应用户操作。


useLayoutEffect

1.useLayoutEffectuseEffect 很相似,都是 React 提供的用于处理副作用的钩子函数。它们的主要区别在于执行时间不同。
在这里插入图片描述

useLayoutEffect在第二步结束的时候渲染,useEffect在第三步结束的时候渲染

2.useEffect 会在浏览器完成绘制后,即组件渲染到页面上后异步执行。也就是说,可能会在用户看到更新前看到旧的 UI。而 useLayoutEffect 会在浏览器绘制之前同步执行,useLayoutEffect会阻止浏览器渲染真实DOM,优先执行Effect链表中的callback;
这里的渲染是指绘制
,useLayoutEffect 执行时,组件返回的 JSX 已经被渲染成 DOM,但 DOM 还没有完成 layout,也就是还没有完成测量和定位。相对于 useEffect,useLayoutEffect 触发的时机更早,可以拿到更快更准确的测量值,同时又避免了闪烁和触发重渲染的问题。因此可以在 useLayoutEffect 中进行对真实 DOM 的操作。,确保无论是 React 还是 DOM 视图都是最新的,即用户看到的 UI 是最新的。

如果你需要在更新 DOM 前同步更新某些组件或页面状态并且必须优先更新,可以使用 useLayoutEffect。如果不需要优先更新或状态更新较慢也不会影响用户体验,可以使用 useEffect

需要注意,由于 useLayoutEffect 的同步执行,如果钩子函数的操作逻辑较耗时,则可能会导致页面卡顿或延迟加载。因此,应该谨慎使用 useLayoutEffect,避免出现性能问题。

渲染次数问题:

如果在callback函数中又修改了状态值「视图又要更新」
1.useEffect:浏览器肯定是把第一次的真实已经绘制了,再去渲染第二次真实DOM
2.useLayoutEffect:浏览器是把两次真实DOM的渲染,合并在一起渲染的

总结

1. 视图更新的步骤

  • 将 JSX 编译为 createElement 格式并调用,创建出 Virtual DOM。
  • 基于 root.render 方法将 Virtual DOM 转换为真实 DOM 对象。
  • 执行更新队列中已记录的所有组件的生命周期方法,包括 shouldComponentUpdaterendergetSnapshotBeforeUpdatecomponentDidUpdate 等方法。
  • 基于 Virtual DOM 树比较上一次渲染时的 virtual DOM 树和本次渲染时的 virtual DOM 树,找出两棵树之间的差异,并记录需要更新的 DOM 对象的最小集合。
  • 在浏览器空闲时间内执行更新和重绘操作,优化更新过的 DOM 对象,尽可能减少页面的重排和重绘的次数,提高页面的性能和响应速度。

2. useLayoutEffect 和 useEffect 的区别

  • useLayoutEffect 与 useEffect 在实现上是类似的,不同之处在于 useLayoutEffect 的回调函数会在 DOM 更新之后,浏览器执行绘制之前被调用,而 useEffect 的回调函数则是在浏览器执行绘制之后被调用。
  • useLayoutEffect 在执行回调函数时会产生阻塞效果,可能会导致页面感觉卡顿,而 useEffect 则不会产生这种阻塞效果。
  • 因此,如果回调函数需要操作 DOM 或者进行某些与界面交互相关的操作,可以考虑使用 useLayoutEffect;如果不需要进行交互,或者交互操作可以忍受一定的延迟,可以使用 useEffect。

3. 视图更新如何影响性能和用户体验

  • 视图更新是一种相对费时的操作,如果更新操作过于频繁或过于复杂,可能会影响页面的性能和响应速度,甚至引起页面卡顿等问题。
  • 对于频繁进行更新的组件,可以考虑使用 React.memouseMemouseCallback 等优化方式,避免重复的计算和更新。
  • 对于需要操作 DOM 或进行其他交互操作的组件,要特别注意使用 useLayoutEffectuseEffect 时的延迟问题和阻塞效果,避免影响页面的流畅度和响应速度。同时,也要考虑好性能和用户体验之间的平衡,避免过度追求优化而影响用户的体验。
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
useEffectuseLayoutEffectReact中的两个钩子函数,它们用于在组件渲染完成后执行一些副作用操作。它们的主要区别在于执行时机和对页面渲染的影响。 useEffect的回调函数会在浏览器执行绘制之后被调用,它是异步执行的,不会阻塞渲染过程。因此,如果副作用操作不涉及对DOM的操作或页面交互,或者可以接受一定的延迟,可以使用useEffectuseLayoutEffect的回调函数会在DOM更新之后,浏览器执行绘制之前被调用。它在执行回调函数时会产生阻塞效果,可能导致页面感觉卡顿。因此,如果副作用操作需要操作DOM或进行与界面交互相关的操作,可以考虑使用useLayoutEffect。 综上所述,优先使用useEffect,因为它是异步执行的,不会阻塞渲染。只有当需要进行DOM操作或与界面交互的操作时,才考虑使用useLayoutEffect。但需要注意,useLayoutEffect在服务端渲染时可能会导致首屏实际内容和服务端渲染出来的内容不一致,会有一个警告。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [四、useEffectuseLayoutEffect深入学习超大章](https://blog.csdn.net/NinthMonee/article/details/113124449)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [useEffectuseLayoutEffect基础知识底层机制](https://blog.csdn.net/qq_52563729/article/details/131155613)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [useEffectuseLayoutEffect](https://blog.csdn.net/qq_37548296/article/details/126471059)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值