MST

星途 面试题库

面试题:深入剖析 React 中 useLayoutEffect 与 useEffect 的实现原理及应用优化

从 React 的底层实现原理角度,解释 useLayoutEffect 和 useEffect 的工作机制。如果在应用中发现使用 useLayoutEffect 导致了性能瓶颈,你会从哪些方面进行优化?并结合 React 最新的特性和发展趋势进行说明。
47.0万 热度难度
前端开发React

知识考点

AI 面试

面试题答案

一键面试

useEffect 和 useLayoutEffect 工作机制

  1. 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;
  1. 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 导致的性能瓶颈

  1. 减少不必要的触发
    • 分析依赖:仔细检查 useLayoutEffect 的依赖数组,确保只有真正需要的变量在数组中。如果依赖数组中包含了不必要的变量,可能会导致 useLayoutEffect 频繁触发。例如,如果某个变量在组件的生命周期内不会改变,就不应该将其放入依赖数组。
    • 使用稳定的引用:在函数组件中,每次渲染函数都会重新执行,函数内部定义的对象和函数都是新的引用。如果将这些不稳定的引用放入 useLayoutEffect 的依赖数组,会导致不必要的触发。可以使用 useCallbackuseMemo 来创建稳定的回调函数和 memoized 值,减少依赖的变化。
  2. 拆分操作
    • 延迟非关键操作:对于一些不是必须在 DOM 更新后立即执行的操作,可以将其从 useLayoutEffect 中移出,放入 useEffect 中。这样可以避免阻塞浏览器的绘制,提高性能。例如,数据上报等操作通常不需要在 DOM 更新后马上执行,可以放在 useEffect 中。
    • 分批处理:如果 useLayoutEffect 中有多个操作,可以考虑将它们分批处理。比如,可以将需要读取 DOM 布局信息的操作放在一起,减少对 DOM 的多次读取,提高效率。
  3. 利用 React 的新特性
    • Concurrent Mode:React 的 Concurrent Mode 旨在提高应用的响应性和性能。在 Concurrent Mode 下,React 可以暂停、终止或重新启动渲染任务,以更好地适应高优先级的任务。虽然 useLayoutEffect 本身是同步执行的,但在 Concurrent Mode 环境中,应用可以通过更智能的任务调度,减少 useLayoutEffect 对整体性能的影响。例如,React 可以在适当的时候暂停 useLayoutEffect 的执行,优先处理用户输入等高优先级任务。
    • 新的渲染优化技术:随着 React 的发展,新的渲染优化技术不断涌现,如自动批处理(在 React 18 中,React 会自动批量更新状态,即使是在异步函数中)。合理利用这些技术可以减少 useLayoutEffect 的触发频率,因为状态更新的批次化可能会减少依赖的变化,从而减少 useLayoutEffect 的重新执行次数。