Otimização de desempenho dos componentes funcionais do React

Ideias de otimização

Existem duas direções principais de otimização:

  1. Reduza o número de re-renderizações. Como a parte mais pesada (e mais longa) do React é a reconction (simplesmente entendida como diff), se você não renderizar, não haverá reconction.
  2. Reduza a quantidade de computação. O objetivo principal é reduzir cálculos repetidos.Para componentes funcionais, cada renderização executará novamente a chamada de função do zero.

Ao usar componentes de classe, as APIs de otimização React usadas são principalmente: shouldComponentUpdate e PureComponent

Então, em componentes funcionais, como fazemos a otimização de desempenho? Use principalmente os seguintes métodos para otimizar

  1. React.memo
  2. useRetorno de chamada
  3. useMemo

React.memo

Veja um exemplo:

Colocamos um botão no componente pai para modificar a legenda e introduzir o subcomponente Filho

Como você pode ver, na primeira vez que você entra, o subcomponente imprime console.log('I am a subcomponent')

Ao clicar para modificar a legenda, o subcomponente Filho também é impresso, causando tempos de renderização repetidos desnecessários

//父组件
import {useState} from 'react'

import Child from "./Child";
const Index = ()=>{
    const [subTitle, setSubTitle] = useState('我是子标题')
    const updateSubTitle = ()=>{
      setSubTitle('修改子标题')
    }
    return (
      <div>
        <div>函数式组件性能优化</div>
        <div>{subTitle}</div>
        <button onClick={updateSubTitle}>修改子标题</button>
        <Child/>
      </div>
    );
  }
  
  export default Index;


//子组件Child.js
const Child = ()=>{
    console.log('我是子组件')
    return (
        <div>我是子组件</div>
    )
}
export default Child
复制代码

Otimize-o, use React.memo para encapsular componentes filhos

import React from "react";

const Child = ()=>{
    console.log('我是子组件')
    return (
        <div>我是子组件</div>
    )
}
export default React.memo(Child)
复制代码

Observe novamente e descubra que o subcomponente Child não é renderizado repetidamente.

useRetorno de chamada

Aqui nós o remodelamos novamente, adicionamos um evento onclick ao subcomponente Child e, em seguida, clicamos no botão Modify sub-title e descobrimos que nosso subcomponente Child é renderizado novamente. Isso ocorre principalmente porque a função handlerClick renderiza novamente alterações ao modificar o subtítulo, fazendo com que o componente filho seja renderizado novamente

// 父组件
const Index = ()=>{
    const [subTitle, setSubTitle] = useState('我是子标题')
    const updateSubTitle = ()=>{
      setSubTitle('修改子标题')
    }
    const handlerClick = ()=>{
      console.log('子组件点击')
    }
    return (
      <div>
        <div>函数式组件性能优化</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)
复制代码

Otimize-o, use useCallback para envolver a função handlerClick do subcomponente, clique em updateSubTitle novamente para modificar o subtítulo e descubra que o subcomponente filho não foi renderizado novamente

// 父组件
const Index = ()=>{
    const [subTitle, setSubTitle] = useState('我是子标题')
    const updateSubTitle = ()=>{
      setSubTitle('修改子标题')
    }
    const handlerClick = useCallback(()=>{
      console.log('子组件点击')
    },[])

    return (
      <div>
        <div>函数式组件性能优化</div>
        <div>{subTitle}</div>
        <button onClick={updateSubTitle}>修改子标题</button>
        <Child onClick={handlerClick}/>
      </div>
    );
  }
  
  export default Index;
复制代码

Aqui está o uso de useCallback

const callback = () => {
  doSomething(a, b);
}

const memoizedCallback = useCallback(callback, [a, b])
复制代码

Passe a função e as dependências como parâmetros para useCallback, e ele retornará uma versão memoized da função callback. Este memoizedCallback só será atualizado quando as dependências forem alteradas.

useMemo

useMemo para cache de resultados de cálculos

我们先看个例子,在之前基础上添加一个calcCount计算函数,然后点击updateSubTitle更新子标题,发现calcCount重新计算了,也就是每次渲染都会造成重复计算,如果是计算量比较大的情况下,会极大的影响性能

// 父组件
const Index = ()=>{
    const [subTitle, setSubTitle] = useState('我是子标题')
    const updateSubTitle = ()=>{
      setSubTitle('修改子标题')
    }
    const handlerClick = useCallback(()=>{
      console.log('子组件点击')
    },[])

    const calcCount = ()=>{
      
      let totalCount = 0
      for(let i=0;i<10000;i++){
        totalCount+=i
      }
      console.log('totalCount',totalCount)
      return totalCount
    }

    const count = calcCount()

    return (
      <div>
        <div>函数式组件性能优化</div>
        <div>{subTitle}</div>
        <button onClick={updateSubTitle}>修改子标题</button>
        <div>count:{count}</div>
        <Child onClick={handlerClick}/>
      </div>
    );
  }
复制代码

优化一下,使用useMemo缓存计算结果,我们再次点击updateSubTitle修改子标题按钮,可以发现calcCount函数不再重复计算

  const calcCount = ()=>{
      
      let totalCount = 0
      for(let i=0;i<10000;i++){
        totalCount+=i
      }
      console.log('totalCount',totalCount)
      return totalCount
    }

    const count = useMemo(calcCount,[])
复制代码

最后,需要注意的是不能盲目的使用useMemo,要根据具体的场景,比如对于一个数据计算量比较大,那么使用是比较适用的,而对于普通的一些值得计算,可以不使用,因为本身useMemo也是会消耗一些性能,盲目使用反而会适得其反

参考阅读

文章最后

本文作者阿健Kerry,高级前端工程师,转载请注明出处。如果觉得本文对你有帮助,记得点赞三连哦,也可以扫码关注我新建立的前端技术公众号【有你前端】,之后我所有文章会同步发到这个公众号上面。另外,我建了一个可以帮助咱们程序员脱单的公众号,每周都会推送几个优秀的单身小姐姐,如果你是程序员技术好又正好是单身,那你可以下面扫码关注【缘来你是程序猿】公众号开启你的脱单之旅。

Acho que você gosta

Origin juejin.im/post/6954595499261296654
Recomendado
Clasificación