一。背景
React的性能优化的一个方面就是避免组件的重复渲染(re-rend)。官方文档也提供了很过性能优化的奇技淫巧,且大部分都有一个比较明确的上下文。
这里我们讨论的是情况:
- 父组件修改
state后,子组件无依赖传参,如何避免子组件re-rend, - 父组件修改
state后,子组件有依赖传参,如何避免子组件re-rend。
二。实例
1. 如何避免无依赖传参时,子组件的渲染
function Child () {
console.log('child re-rend')
return (<div>我是儿砸。</div>)
}
function Parent() {
const [count, setCount] = useState(0);
return (<>
{count}<button onClick={() => setCount(count => count + 1)}>计数器给我加</button>
<Child />
</>)
}
当我们点击父组件的按钮,修改state的值 5次 后,由于Parent的re-rend,导致了整个子组件re-rend了 5次。下图可以看到,初始输出 1次,重新渲染输出了 5次。
避免方式————通过props.children
function Child () {
console.log('child re-rend')
return (<div>我是儿砸。</div>)
}
function Parent({children}) {
const [count, setCount] = useState(0);
return (<>
{count}<button onClick={() => setCount(count => count + 1)}>计数器给我加</button>
{children}
</>)
}
// 顶级App组件
function App() {
return(<>
<Parent>
<Child />
</Parent>
</>)
}
可以看到当点击5次后,子组件仅仅初始输出一次,并未re-rend。
但是这种子组件和父组件无传参的情况还是少见,对于有传参的情况,我们如何处理,随之而来:
2. 如何避免有依赖传参时,子组件的渲染
首先我们看看有依赖传参的情况。
function Child ({ tips }) {
console.log("[child] re-rend:", tips);
return (<div>我是儿砸。{tips}</div>)
}
function Parent({children}) {
const [count, setCount] = useState(0);
console.log('[parent] count:', count)
return (<>
{count}<button onClick={() => setCount(count => count + 1)}>计数器给我加</button>
<Child tips={count % 3 === 0? '无余数': '有余数'} />
</>)
}
同样是点击了5次, 可以从下图看到,子组件有相同的传参时(tips='有余数')仍然re-rend了。
我们如何避免上面这种情况呢—————使用React.memo
const Child = React.memo(({ tips }) => {
console.log("[child] re-rend:", tips);
return <div>我是儿砸。{tips}</div>;
})
function Parent({children}) {
const [count, setCount] = useState(0);
console.log('[parent] count:', count)
return (<>
{count}<button onClick={() => setCount(count => count + 1)}>计数器给我加</button>
<Child tips={count % 3 === 0? '无余数': '有余数'} />
</>)
}
可以看到当有相同的传参时,re-rend已经被避免了。
为何memo能达到这种效果能,我们可以从官网找到答案:
如果你的组件在相同
props的情况下渲染相同的结果,那么你可以通过将其包装在React.memo中调用,以此通过记忆组件渲染结果的方式来提高组件的性能表现。这意味着在这种情况下,'React' 将跳过渲染组件的操作并直接复用最近一次渲染的结果。—— 官网React.memo
但是得注意:使用React.memo对props的比较是浅比较!
如果要进行自定义比较函数,可如下操作:
function MyComponent(props) {
/* 使用 props 渲染 */
}
function areEqual(prevProps, nextProps) {
/*
如果把 nextProps 传入 render 方法的返回结果与
将 prevProps 传入 render 方法的返回结果一致则返回 true,
否则返回 false
*/
}
export default React.memo(MyComponent, areEqual);
三。总结
今天讨论了两种避免子组件重复渲染的方式。若有问题欢迎大家讨论和分享哈。