8 способов оптимизировать производительность React

React.js

React имеет эффективную производительность с виртуальным DOM и алгоритмами сравнения. Кроме того, существует множество других методов и приемов для дальнейшего повышения производительности реакции. В этой статье я перечислю несколько методов, которые могут эффективно улучшить производительность реакции, чтобы помочь нам улучшить react.code для повышения производительности. Но мы не обязаны использовать эти методы в проекте, но нам нужно знать, как использовать эти методы.

Используйте React.Memo для кэширования компонентов

Одним из способов повышения производительности приложения является реализация мемоизации. Мемоизация — это метод оптимизации, который в основном ускоряет работу программ за счет сохранения результатов дорогостоящих вызовов функций и возврата кэшированных результатов при повторении того же ввода.
Каждое обновление состояния родительского компонента приведет к повторному рендерингу дочернего компонента, даже если состояние входящего дочернего компонента не изменилось, чтобы уменьшить повторный рендеринг, мы можем использовать React.memo для кэширования компонента, чтобы только когда происходит значение состояния входящего компонента. Он будет повторно отображаться только при его изменении. Если передается одно и то же значение, возвращается кэшированный компонент. Пример выглядит следующим образом:

export default React.memo((props) => {
  return (
    <div>{props.value}</div>  
  )
});

Кэширование больших вычислений с помощью useMemo

Иногда рендеринг неизбежен, но если ваш компонент является функциональным компонентом, повторный рендеринг приведет к тому, что каждый раз будет вызываться большая вычислительная функция, что очень требовательно к производительности, мы можем использовать новый хук useMemo, чтобы «запомнить» эту вычислительную функцию. результат расчета. Таким образом, функция расчета будет повторно вызываться для расчета нового результата только после изменения входящих параметров.
Таким образом, вы можете сэкономить дорогостоящее время вычислений, используя результаты, вычисленные из предыдущих визуализаций. Общая цель состоит в том, чтобы уменьшить объем работы, которую JavaScript должен выполнять во время рендеринга компонентов, чтобы основной поток блокировался на меньшее время.

// 避免这样做
function Component(props) {
  const someProp = heavyCalculation(props.item);
  return <AnotherComponent someProp={someProp} /> 
}
  
// 只有 `props.item` 改变时someProp的值才会被重新计算
function Component(props) {
  const someProp = useMemo(() => heavyCalculation(props.item), [props.item]);
  return <AnotherComponent someProp={someProp} /> 
}

Используя React.PureComponent, shouldComponentUpdate

Каждое обновление состояния родителя приведет к повторному рендерингу дочернего элемента, даже с теми же переданными реквизитами. Но повторный рендеринг здесь не означает, что DOM будет обновляться, а каждый раз будет вызываться алгоритм diif, чтобы определить, нужно ли обновлять DOM. Это очень дорого для больших компонентов, таких как деревья компонентов.
Здесь мы можем использовать React.PureComponent, жизненный цикл shouldComponentUpdate, чтобы обеспечить повторный рендеринг только при изменении состояния свойств компонента. Следующий пример:

export default function ParentComponent(props) {
  return (
    <div>
      <SomeComponent someProp={props.somePropValue}
    <div>
      <AnotherComponent someOtherProp={props.someOtherPropValue} />
    </div>
   </div>
 )
}

export default function SomeComponent(props) {
  return (
    <div>{props.someProp}</div>  
  )
}

// 只要props.somePropValue 发生变化,不论props.someOtherPropValue是否发生变化该组件都会发生变化
export default function AnotherComponent(props) {
  return (
    <div>{props.someOtherProp}</div>  
  )
}

Мы можем использовать React.PureComponent или shouldComponentUpdate для оптимизации следующим образом:

// 第一种优化
class AnotherComponent extends React.PureComponent {
  render() {
    return <div>{this.props.someOtherProp}</div>   
  }
}

//第二种优化
class AnotherComponent extends Component {
  shouldComponentUpdate(nextProps) {
    return this.props !== nextProps
  }
  render() {
    return <div>{this.props.someOtherProp}</div>   
  }
}


PureComponent выполнит неглубокое сравнение, чтобы определить, следует ли повторно отображать компонент.Для входящих реквизитов базового типа, если значения совпадают, поверхностное сравнение будет считаться одинаковым.Для входящих реквизитов ссылочного типа , поверхностное сравнение будет рассматривать только входящие реквизиты как не одну и ту же ссылку, в противном случае, даже если содержимое двух объектов точно такое же, они будут считаться разными реквизитами.
Следует отметить, что PureComponent следует использовать с осторожностью для компонентов, время рендеринга которых можно игнорировать, или компонентов, состояние которых постоянно меняется, потому что поверхностные сравнения также потребуют времени.Эта оптимизация больше подходит для больших компонентов дисплея. Крупные компоненты также можно разделить на несколько мелких компонентов, а использование меморандумов для переноса мелких компонентов также может повысить производительность.

Избегайте использования встроенных объектов

При использовании встроенного объекта react воссоздает ссылку на этот объект при каждом рендеринге, что заставляет компонент, который получает этот объект, видеть его как другой объект, поэтому неглубокое сравнение свойств компонента всегда возвращает false, заставляя компонент повторно рендерить все время.
Косвенность во встроенном стиле, которую используют многие люди, может привести к повторному рендерингу компонентов, что может привести к проблемам с производительностью. Чтобы решить эту проблему, мы можем гарантировать, что объект инициализируется только один раз, указывая на одну и ту же ссылку. Другим случаем является передача объекта, который также будет создавать разные ссылки во время рендеринга, что также может вызвать проблемы с производительностью.Мы можем использовать оператор распространения ES6 для уничтожения переданного объекта. Таким образом, компонент получает базовый тип реквизита, компонент обнаруживает, что полученный реквизит не изменился при поверхностном сравнении, поэтому он не будет перерисовываться. Пример выглядит следующим образом:

// Don't do this!
function Component(props) {
  const aProp = { someProp: 'someValue' }
  return <AnotherComponent style={{ margin: 0 }} aProp={aProp} />  
}

// Do this instead :)
const styles = { margin: 0 };
function Component(props) {
  const aProp = { someProp: 'someValue' }
  return <AnotherComponent style={styles} {...aProp} />  
}

Избегайте использования анонимных функций

Хотя анонимные функции — отличный способ передать функции (особенно те, которые нужно вызывать с другим свойством в качестве аргумента), они имеют разные ссылки при каждом рендеринге. Это похоже на встроенный объект, описанный выше. Чтобы сохранить ту же ссылку на функцию, переданную в качестве реквизита для компонента React, вы можете объявить ее как метод класса (если вы используете компонент на основе класса) или использовать хук useCallback, чтобы помочь вам сохранить ту же ссылку ( если вы используете функциональные компоненты).
Конечно, иногда встраивание анонимных функций является самым простым способом и на самом деле не вызывает проблем с производительностью вашего приложения. Это может быть из-за использования его на очень «легком» компоненте или из-за того, что родительский компонент фактически должен повторно отображать весь свой контент каждый раз при изменении реквизита. Так что не волнуйтесь, если функция является другой ссылкой, потому что компонент все равно будет повторно отображаться.

// 避免这样做
function Component(props) {
  return <AnotherComponent onChange={() => props.callback(props.id)} />  
}

// 优化方法一
function Component(props) {
  const handleChange = useCallback(() => props.callback(props.id), [props.id]);
  return <AnotherComponent onChange={handleChange} />  
}

// 优化方法二
class Component extends React.Component {
  handleChange = () => {
   this.props.callback(this.props.id) 
  }
  render() {
    return <AnotherComponent onChange={this.handleChange} />
  }
}

Ленивая загрузка компонентов, которые не нужны сразу

Ленивая загрузка компонентов, которые на самом деле не видны (или не нужны сразу), чем меньше компонентов загружает React, тем быстрее будут загружаться компоненты. Поэтому, если ваш первоначальный рендеринг выглядит довольно грубо, вы можете уменьшить количество загружаемых компонентов, загружая их по мере необходимости после завершения первоначальной установки. В то же время это позволит пользователям быстрее загружать вашу платформу/приложение. Наконец, разделив первоначальный рендеринг, вы разделите рабочую нагрузку JS на более мелкие задачи, что даст вашей странице время для ответа. Это можно легко сделать с помощью новых React.Lazy и React.Suspense.

// 延迟加载不是立即需要的组件
const MUITooltip = React.lazy(() => import('@material-ui/core/Tooltip'));
function Tooltip({ children, title }) {
  return (
    <React.Suspense fallback={children}>
      <MUITooltip title={title}>
        {children}
      </MUITooltip>
    </React.Suspense>
  );
}

function Component(props) {
  return (
    <Tooltip title={props.title}>
      <AnotherComponent />
    </Tooltip>
  )
}

Настройте CSS вместо принудительной загрузки и выгрузки компонентов

Рендеринг стоит дорого, особенно когда нужно изменить DOM. Всякий раз, когда у вас есть какая-то функция аккордеона или вкладки, например, вы хотите видеть только один элемент за раз, вы можете захотеть выгрузить невидимый компонент и перезагрузить его, когда он станет видимым. Если загружаемый/выгружаемый компонент является «тяжелым», эта операция может быть очень дорогостоящей и вызывать задержки. В этих случаях лучше всего скрыть его с помощью CSS при сохранении содержимого в DOM.
Хотя этот подход не является панацеей, поскольку установка этих компонентов может вызвать проблемы (например, компоненты конкурируют с бесконечной нумерацией страниц в окне), мы должны выбрать метод настройки CSS, когда это не так. С другой стороны, настройка непрозрачности на 0 почти не требует затрат для браузера (поскольку это не вызывает перекомпоновки) и должна иметь приоритет над видимостью и отображать как можно больше.
Иногда может быть полезно скрыть с помощью CSS, оставив компонент загруженным, а не скрывать путем выгрузки. Это эффективная оптимизация производительности для тяжелых компонентов со значительным временем загрузки/выгрузки.

// 避免对大型的组件频繁对加载和卸载
function Component(props) {
  const [view, setView] = useState('view1');
  return view === 'view1' ? <SomeComponent /> : <AnotherComponent />  
}

// 使用该方式提升性能和速度
const visibleStyles = { opacity: 1 };
const hiddenStyles = { opacity: 0 };
function Component(props) {
  const [view, setView] = useState('view1');
  return (
    <React.Fragment>
      <SomeComponent style={view === 'view1' ? visibleStyles : hiddenStyles}>
      <AnotherComponent style={view !== 'view1' ? visibleStyles : hiddenStyles}>
    </React.Fragment>
  )
}

Избегайте добавления дополнительного DOM с помощью React.Fragment.

В некоторых случаях нам нужно вернуть несколько элементов в компоненте, например следующие элементы, но реакция предусматривает, что в компоненте должен быть родительский элемент.

            <h1>Hello world!</h1>
            <h1>Hello there!</h1>
            <h1>Hello there again!</h1>

Таким образом, вы можете сделать это, но это создаст дополнительные ненужные элементы div, даже если все работает нормально. Это приводит к созданию множества бесполезных элементов по всему приложению:

function Component() {
        return (
            <div>
                <h1>Hello world!</h1>
                <h1>Hello there!</h1>
                <h1>Hello there again!</h1>
            </div>
        )
}

Фактически, чем больше элементов на странице, тем больше времени требуется для ее загрузки. Чтобы сократить ненужное время загрузки, мы можем использовать React.Fragment, чтобы избежать создания ненужных элементов.

function Component() {
        return (
            <React.Fragment>
                <h1>Hello world!</h1>
                <h1>Hello there!</h1>
                <h1>Hello there again!</h1>
            </React.Fragment>
        )
}

Суммировать

То, что мы перечислили в этой статье, — это в основном методы оптимизации производительности, предоставляемые React внутри компании. Эти методы могут помочь React работать лучше, и не перечисляют методы оптимизации сторонних библиотек инструментов, таких как Immutable.js. На самом деле существует множество способов оптимизации производительности, но, как упоминалось выше, в соответствующих сценариях следует использовать соответствующие методы, и чрезмерное использование оптимизации производительности перевесит преимущества.

Ссылка на ссылку

наконец

Оригинал здесь:gitHubЕсли есть какие-то упущения, пожалуйста, поправьте меня! !

Если вы найдете это полезным! Пожалуйста, не забудьте поставить лайк или подписаться! Ваше внимание будет для меня движущей силой двигаться вперед! ! Утка! ! !