面试题答案
一键面试避免不必要渲染优化性能
- useMemo:用于缓存值,只有当依赖数组中的值发生变化时才重新计算。例如:
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
这样,只要 a
和 b
不变,computeExpensiveValue
就不会重新执行,避免了不必要的计算,也就减少了可能导致的不必要渲染。
2. useEffect:可以控制副作用的执行时机。通过正确设置依赖数组,只有依赖数组中的值发生变化时,副作用才会重新执行。例如:
useEffect(() => {
// 副作用代码
document.title = `You clicked ${count} times`;
}, [count]);
只有 count
变化时,才会更新 document.title
,避免了在其他无关状态变化时触发不必要的副作用执行,进而避免不必要渲染。
useEffect和useMemo依赖数组工作原理
- useEffect:依赖数组决定了副作用何时重新运行。当组件渲染时,
useEffect
会执行一次。后续每次渲染,如果依赖数组中的值有任何一个发生变化,useEffect
就会再次执行副作用函数。如果依赖数组为空[]
,则useEffect
只会在组件挂载和卸载时执行,因为没有任何状态变化会触发它再次运行。 - useMemo:依赖数组决定了缓存值何时重新计算。只有依赖数组中的值发生变化时,
useMemo
才会重新执行传入的函数并返回新的缓存值。如果依赖数组为空[]
,则useMemo
在组件挂载时计算一次值,之后不会再重新计算,始终返回第一次计算的值。
复杂场景下正确设置依赖
- 确定真正依赖:仔细分析副作用或计算逻辑中使用到的所有外部变量,将它们全部放入依赖数组。如果遗漏依赖,可能导致副作用使用到过期的数据,或者
useMemo
没有在相关数据变化时重新计算。 - 拆分副作用:对于复杂的副作用,可以拆分成多个
useEffect
,每个useEffect
专注于处理不同的依赖关系,使依赖数组更清晰,避免因为将所有依赖放在一起而导致不必要的频繁触发。
useEffect触发多次的原因
- 初始渲染和更新:在组件挂载(初始渲染)时,
useEffect
会执行一次。之后,只要依赖数组中的值发生变化,useEffect
就会再次执行,所以看起来像是触发了多次。 - 函数式更新:如果在
useEffect
内部使用函数式更新(如setState(prevState => ({...prevState, newData: 'value' }))
),并且没有正确设置依赖,可能导致多次触发。因为函数式更新会创建新的函数引用,而如果依赖数组中没有包含这些函数,useEffect
就会因为捕获到不同的函数引用而多次触发。 - 嵌套组件和父组件更新:如果
useEffect
所在组件是嵌套在其他组件中的,父组件的更新可能会导致子组件重新渲染,进而可能触发子组件中的useEffect
。如果父组件频繁更新且子组件的useEffect
依赖数组设置不当,就会出现多次触发的情况。