浅谈React组件性能优化的方式

合理使用state

  • 减轻state:只存储跟组件渲染相关的数据(比如:count/给后端的入参/后端返回数据data/切换状态visible、loading等)

  • 注意:不用做渲染的数据不要放在state中(比如:定时器timer/用作js处理数据定义的变量)

  • 对于需要在多个方法中用到的数据,应该放在this中

     class He1lo extends Component {
       componentDidMount ()
        // timerId存储到this中,而不是state中
        this. timerId - setInterval(() - {}, 2000)
       }
       componentWillUnmount() {
        clearInterval (this.timerId)
       }
       render() { ... }
     }
    

避免不必要的重新渲染

  • 组件更新机制:父组件更新会引起子组件也被更新
  • 问题:子组件没有任何变化时也会重新渲染,如何避免不必要的重新渲染?
  • 解决方式:使用钩子函数shouldComponentUpdate(nextProps,nextState)
  • 作用:通过返回值决定该组件是否重新渲染,返回true表示重新渲染,false表示不重新渲染
  • 触发时机:更新阶段的钩子函数,组件重新渲染前执行

同页面state是否改变

shouldComponentUpdate(nextProps,nextState) {
	return nextState.pageSize !== this.state.pageSize 
}

父子组件props是否改变

shouldComponentUpdate(nextProp,nextState){
  return nextProp.text !== this.props.text
}

上述的比较只是‘浅层比较’,如果类型是基本类型,只要值相同,那么“浅层比较”,对于复杂对象咋整?

对于复杂对象,‘浅层比较’的方式只看这两个prop是不是同一个对象的引用,如果不是,哪怕对象中的内容完全一样也会认为是不同的两个prop。所以把复杂对象提出来,不要放在render函数中,因为每次渲染都会重新创建对象,导致引入地址不同,infoProps每次都会不同

错误写法

<father infoProps = {{ name: 'jack' }} />

正确写法

const infoObj = {name: "jack"};//确保这个初始化只执行一次,不要放在render函数中
<father infoProps = {infoObj} />

函数式组件优化useCallback、React.memo、useMemo

优化思路:

减少重新 render 的次数。因为在 React 里最重(花时间最长)的一块就是 reconction(简单的可以理解为diff),如果不 render,就不会 reconction。 减少计算的量。主要是减少重复计算,对于函数式组件来说,每次 render都会重新从头开始执行函数调用。

React.memo

父组件点击事件改变了数据,render重新执行,会将其他子组件重新渲染,所以用React.memo包裹子组件

import {useState, memo} from 'react'
 
import Child from "./Child";
const Index = ()=>{
    const [subTitle, setSubTitle] = useState('子组件')
    const updateSubTitle = ()=>{
      setSubTitle('修改子组件')
    }
    return (
      <div>
        <div>{subTitle}</div>
        <button onClick={updateSubTitle}>修改子标题</button>
        <Child/>
      </div>
    );
  }
   
  export default memo(Index);

useCallback

父组件点击事件改变了数据,render重新执行,子组件被React.memo包裹,所以不会重新渲染,但是父组件有点击
事件传给了子组件,又会重新渲染子组件,因此用useCallback包裹点击函数

// 父组件
const Index = ()=>{
    const [subTitle, setSubTitle] = useState('子组件')
    const updateSubTitle = ()=>{
      setSubTitle('修改子组件')
    }
    const handlerClick = useCallback(()=>{
      console.log('子组件点击')
    },[])
    return (
      <div>
        <div>{subTitle}</div>
        <button onClick={updateSubTitle}>修改子标题</button>
        <Child onClick={handlerClick}/>
      </div>
    );
  }
 
// Child子组件
const Child = (props)=>{
    console.log('我是子组件')
    return (
        <div>
            <div>我是子组件</div>
            <button onClick={props.onClick}>子组件按钮</button>
        </div>
    )
}
export default React.memo(Child)

useMemo

当组件内部某一渲染的数据,要通过计算得到,这个计算依赖于特定的state或props,我们就用useMemo来缓存这个渲染的数据,以致于我们在修改它们没有依赖的数据源的情况下,多次调用这个计算函数,浪费性能

例: val发生改变,我们不想执行本身的getNum函数,导致多次计算,用useMemo依赖项count

function Example() {
    const [count, setCount] = useState(1);
    const [val, setValue] = useState('');
 
    const getNum = useMemo(() => {
        return count * 100
    }, [count])
 
    return <div>
        <h4>总和:{getNum()}</h4>
        <div>
            <button onClick={() => setCount(count + 1)}>+1</button>
            <input value={val} onChange={event => setValue(event.target.value)}/>
        </div>
    </div>;
}

React组件复用

  • 思想:复用相似功能(函数组件封装思想)
  • 复用内容:1.state 2.操作state的方法
  • 方式:1.render props模式 2.高阶组件(HOC)

Key的使用

  • key属性可以明确的告诉React每个组件的唯一标识
  • 在选取Key值时尽量不要用index,因为如果当数据的添加方式不是顺序添加,而是以其他方式(逆序,随机等),会导致每一次添加数据,每一个数据值的索引号都不一样,这就导致了Key的变化,而当Key变化时,React就会认为这与之前的数据值不相同,会多次执行渲染,会造成大量的性能浪费。
<ul>
  tabData.map((item,index) => {
      <TabItem
        key={item.id} //最好别使用index,除非后端没有唯一id
        text={item.text}
      />
    })
</ul>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值