JS 基础知识回顾
在
Javascript
中,函数是一种引用类型的变量,即存放的并非真实的值,而是引用值,类似于C语言中的指针类型。
当我们在JavaScript
中, 每次定义函数(如 () => {}
或 function() {}
)时,都会在内存中创建一个新的对象。例如:
Javascript
const a = () => {};
const b = () => {};
console.log(a === b); // false(两个不同的函数对象)
- 关键点 : 函数
a
和函数b
的内容虽然完全相同,但是它们各自创建,代表着内存中的不同位置 - 结果 : 函数
a
和函数b
实际的值(类似于指针)不一样,因此将它们两相比较的结果就是false
。
React中函数引用的频繁变化
在
React
中,函数式组件本身就是一个JavaScript 函数
,每次重新渲染时,整个函数体会从头到尾执行一次,包括内部的所有变量和函数声明,组件内部定义的函数会被重新创建,使用新的内存地址。
当父组件将函数通过 props
传递给子组件时,即使函数内容未变,子组件也会因 props
的引用不同而触发重新渲染。
例如,当我们在组件内部定义一个函数时(以 handleClick
为例),它本质上是这样工作的:
JSX
const Parent = () => {
// 每次 Parent 渲染时,都会重新声明并创建一个新的 handleClick 函数
const handleClick = () => {
console.log("Click");
};
return <Child onClick={handleClick} />;
};
- 关键点:函数组件本身是一个 JavaScript 函数,每次重新渲染时,整个函数体会从头到尾执行一次,包括内部的所有变量和函数声明。
- 结果 :
handleClick
会被重新创建为一个新的函数对象(新的内存地址),即使函数内容没有变化。不仅如此,每当父组件重新渲染时,Child
就会重新渲染,因为onClick
的引用变了。
如何解决?用useCallback
缓存函数
如果有一种方法,能通过缓存函数,仅在某些条件下(比如依赖项变化时)才生成新函数 ,否则复用上一次的函数引用,那么就可以避免重复创建相同内容的函数引用 。可惜,这只是在白日做梦o (T ヘ To)。
事实上,React
中的useCallback
钩子就可以帮助我们实现刚才的想法。 通过 useCallback
缓存函数,可以避免每次渲染时重新创建函数:
JSX
const Parent = () => {
// ✅ 用 useCallback 缓存函数,依赖项为空数组(函数只创建一次)
const handleClick = useCallback(() => {
console.log("Click");
}, []);
return <Child onClick={handleClick} />;
};
- 关键点 :
handleClick
的引用保持不变,子组件Child
的props
不会发生变化。 - 结果 :父组件更新
count
时,Child
不会因onClick
引用变化而重新渲染,从而解决函数引用频繁变化带来的性能问题。
总结
- 根本问题:函数组件每次渲染时都会重新执行,导致内部函数重新声明。
- 关键影响:函数引用变化会触发子组件不必要的渲染(即使内容相同)。
- 解决方案 :用
useCallback
缓存函数,仅在依赖项变化时重新创建函数。
在实际使用时,我们需要清楚何时需要优化函数引用,何时可以直接定义函数。
注意,不要滥用 useCallback
,只在必要时使用(如性能敏感场景或子组件依赖函数引用时)。