useEffect 和 useLayoutEffect 工作机制
- useEffect
- 原理:
useEffect
是 React 提供的用于处理副作用(如数据获取、订阅或手动修改 DOM 等)的 Hook。它会在组件渲染到屏幕后异步执行。这意味着,当组件渲染完成,浏览器已经将更新渲染到屏幕上之后,useEffect
中的回调函数才会被执行。
- 它可以接受两个参数,第一个是需要执行的副作用函数,第二个是依赖数组。当依赖数组中的值发生变化时,副作用函数会重新执行。如果依赖数组为空,副作用函数只会在组件挂载和卸载时执行。
- 示例:
import React, { useEffect } from'react';
const MyComponent = () => {
useEffect(() => {
console.log('Component has been rendered or dependencies changed');
return () => {
console.log('Component is about to unmount or dependencies are changing');
};
}, []);
return <div>My Component</div>;
};
export default MyComponent;
- useLayoutEffect
- 原理:
useLayoutEffect
的用法与 useEffect
基本相同,但它的执行时机不同。useLayoutEffect
会在所有 DOM 变更之后,浏览器绘制屏幕之前同步执行。这使得它适合用于需要在 DOM 更新后立即读取布局信息并进行相应操作的场景,比如根据元素的尺寸调整布局。
- 同样接受副作用函数和依赖数组作为参数,依赖数组决定副作用函数的重新执行时机。
- 示例:
import React, { useLayoutEffect } from'react';
const MyLayoutComponent = () => {
useLayoutEffect(() => {
const element = document.getElementById('my - element');
if (element) {
const { width, height } = element.getBoundingClientRect();
console.log(`Element width: ${width}, height: ${height}`);
}
}, []);
return <div id="my - element">My Layout Component</div>;
};
export default MyLayoutComponent;
优化 useLayoutEffect 导致的性能瓶颈
- 减少不必要的触发
- 分析依赖:仔细检查
useLayoutEffect
的依赖数组,确保只有真正需要的变量在数组中。如果依赖数组中包含了不必要的变量,可能会导致 useLayoutEffect
频繁触发。例如,如果某个变量在组件的生命周期内不会改变,就不应该将其放入依赖数组。
- 使用稳定的引用:在函数组件中,每次渲染函数都会重新执行,函数内部定义的对象和函数都是新的引用。如果将这些不稳定的引用放入
useLayoutEffect
的依赖数组,会导致不必要的触发。可以使用 useCallback
和 useMemo
来创建稳定的回调函数和 memoized 值,减少依赖的变化。
- 拆分操作
- 延迟非关键操作:对于一些不是必须在 DOM 更新后立即执行的操作,可以将其从
useLayoutEffect
中移出,放入 useEffect
中。这样可以避免阻塞浏览器的绘制,提高性能。例如,数据上报等操作通常不需要在 DOM 更新后马上执行,可以放在 useEffect
中。
- 分批处理:如果
useLayoutEffect
中有多个操作,可以考虑将它们分批处理。比如,可以将需要读取 DOM 布局信息的操作放在一起,减少对 DOM 的多次读取,提高效率。
- 利用 React 的新特性
- Concurrent Mode:React 的 Concurrent Mode 旨在提高应用的响应性和性能。在 Concurrent Mode 下,React 可以暂停、终止或重新启动渲染任务,以更好地适应高优先级的任务。虽然
useLayoutEffect
本身是同步执行的,但在 Concurrent Mode 环境中,应用可以通过更智能的任务调度,减少 useLayoutEffect
对整体性能的影响。例如,React 可以在适当的时候暂停 useLayoutEffect
的执行,优先处理用户输入等高优先级任务。
- 新的渲染优化技术:随着 React 的发展,新的渲染优化技术不断涌现,如自动批处理(在 React 18 中,React 会自动批量更新状态,即使是在异步函数中)。合理利用这些技术可以减少
useLayoutEffect
的触发频率,因为状态更新的批次化可能会减少依赖的变化,从而减少 useLayoutEffect
的重新执行次数。