背景
eg
这里有两份代码,你知道有啥区别吗?
代码A:
const testA=()=>{ return <div></div> }
存在两种调用方式:
- 第一种:以一个函数的形式直接执行
<div>{ testA() }</div>
- 第二种:以一个组件的形式调用
<div><testA/></div>
两者好像没什么区别,实现的效果基本是一样的,那么真的一样吗?
您知道这两种写法的区别吗?
区别
渲染区别
- 直接使用
testA(),进行函数调用,从生成逻辑上来说会直接变成<div>。- 这种方式在React的渲染过程中相当于直接插入了函数值。React会根据返回的元素创建对应的虚拟DOM节点,并在渲染过程中将其插入到实际DOM中。
- 但是如果我们通过组件渲染,就是通过
createElement去创建,会有一个中间是fiber的过程。- 在这种调用方式中,React会将
<testA />视为一个组件声明,它会在组件树中创建一个新的组件实例。
- 在这种调用方式中,React会将
如图所示:
在图中可以看到直接调用,也就是 testA() 是直接 div => div, 而使用 jsx 调用则是 div => testA(函数组件) => div,中间会有一个过渡。
使用区别
那么我们前面提到的“fiber”状态,会造成使用上的哪些区别?
- 对于直接进行函数调用的情况:由于是直接执行函数,每次父组件重新渲染时,
testA都会被重新调用,这可能导致不必要的性能开销,尤其是在testA函数执行成本较高或包含复杂逻辑时。 - 对于使用组件进行处理的情况:组件声明允许React进行更细粒度的更新和优化。例如,如果
testA组件实现了shouldComponentUpdate或使用了React.memo,React可以决定是否需要重新渲染这个组件。
相关问题(欢迎探讨)
Q1: 如果需要复用一段纯DOM,是memo缓存组件的计算结果性能好,还是直接用一个DOM的变量性能好?
Ans: 用useMemo,直接用一个dom变量,下次渲染的时候,这些dom jsx,换会再走一遍createElement,从这个角度来说当然是useMemo好,这样只计算一次
Q2: 所有传入子组件的函数,只要内部有状态处理(useState),都应该用useCallback包一层吗?
Ans: 用useCallback的目的是为了让一个函数的地址不变,这样两次比较props的时候,如果地址相同,那么对于pure Component就不会再重新渲染子组件,从本质上是性能优化的策略,就算你不用useCallback写也没关系,其次,我个人不推荐上来直接用use Callback包裹一下,万一你依赖性写漏了,就会导致你这个函数中拿到的是旧状态,出问题还不好定位,更何况,你用use Callback的生效条件也比较苛刻,需要子组件是memo过的或者pure Component。