发现问题

大家在使用hook进行开发的时候有没有遇到过以下的情况:

  • 当我set某一个值的时候,有好多不相关的值都重新进行了计算,当代码量较小的时候可能不明显,但是长此以往会让页面越来越卡。
  • 父组件的某个值更新了,但是某些并没有引用这个值的子组件竟然也更新了。

以上的两种情况如果一直放任不管的话,随着代码的累计,页面的性能就会越来越差。当用class组件的时候我们可以通过PureComponent或者生命周期中的shouldComponentUpdate方法来进行优化,但是对于hooks要怎么做呢?

hooks性能优化

在我们编写hook的时候可以通过React.memo和useMemo进行性能的优化。至于他们之间的区别大家可以跟着我的代码往下走。

使用useMemo进行优化

App.tsx:

function App() {
  const [num, setNum] = useState(0);
  const [name, setName] = useState('mx');

  useEffect(() => {
    setTimeout(() => {
      setName('xx');
    }, 2000)
  }, [])

  const memoVal = useMemo(() => {
    console.log('useMemo内部运行了,num值是:', num);
    return num + 1;
  }, [num]);

  console.log('memo值是:', memoVal);
  console.log('---------------------------------------父组件运行分割线---------------------------------------------')

  return (
    <div className="App">
   
    </div>
  );
}

export default App;

这个组件的最终输出是:
在这里插入图片描述

在这里我们可以看到,尽管我们调用了setName,但是memo内部的函数并没有运行,也就是说只有num改变的时候才会重新计算memoVal的值。

使用React.memo进行优化

增加了一个Children.tsx

const Children = (props:any) => {
  console.log('children运行了,对应的props是:',props);
  console.log('--------------------------------子组件运行分割线----------------------------------')
  return <div>children</div>
}

export default Children;

同时在App.tsx中调用该组件

// 省略之前代码
return (
    <div className="App">
      <Children num={num} />
    </div>
  );

此时的输出是:
在这里插入图片描述
我们明明只调用了setName,child也不需要name项,但是child还是进行了一次重新渲染,这就会造成不必要的性能损耗。这个时候React.memo就派上用场了。只需要对child进行一个小小的改动:

import React, { memo } from "react";

const Children = (props:any) => {
  console.log('children运行了,对应的props是:',props);
  console.log('--------------------------------子组件运行分割线----------------------------------')
  return <div>children</div>
}

export default memo(Children);// 增加memo

此时你就会发现setName的时候children就不会再次运行。

React.memo的另一种情况

当num是对象的时候。

const [num, setNum] = useState({});
.
.
.
// 在某处业务代码上
setNum({});

这个时候我们会发现child组件又重新运行了一次,虽然对象的内容都是一样的。
这种情况下我们就需要对child做进一步处理:

export default memo(Children, (props, nextProps) => {
	// 此处的逻辑可以根据你的业务自己code
	return JSON.stringify(props) === JSON.stringify(nextProps) 
});

memo的第二个参数就是对组件的入参前后进行对比,通过返回的布尔值来进行判断是否重新渲染。
个人建议这个参数要慎用,因为有些时候进行深度对比的开销有可能比重新渲染视图要高。

总结

hook中的性能优化主要有useMemo和React.memo。其中React.memo在没有第二的参数的时候相当于class中的PureComponent,当增加了第二个参数的时候相当于声明周期中的shouldComponentUpdate,但是可以明显感觉到hook的使用要更加的简单和灵活。
useMemo则是hook对比class一个显著的优势,因为通过useMemo我们可以进行更加细粒度的性能优化,这是class难以实现的。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐