Практический React: управляем рендерингом - производительность

Реальные проблемы и как их решить, за пять минут или быстрее.

Проблема

Вы используете Redux и решили добавить некоторые UI фичи или анимацию, но ваша производительность рендеринга оставляет желать лучшего. Все тормозит, и вы не знаете где фиксить.

Предостережения:

  • Независимо от Redux, это будет происходить по мере роста вашего приложения. Ваше дерево компонентов будет усложняться, в результате чего многие компоненты будут обновляться без необходимости.

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

  • Погоня за производительностью в 60fps является серьезной задачей. В то время, как это поможет вам на вашем пути, вы захотите изучить «воспринимаемую производительность» и управление временем рендеринга, сбор и обработку данных, и попытка сделать эти задачи парарллельно будет приближать вас к конечной цели. Объедините данные задачи с вынесением возможных частей в web workers, и будет вам счастье.

  • В React, как и в жизни, нет панацеи, поэтому выбирайте разумно. Иными словами, если у вас уже есть молоток, то будьте осторожны, предполагая, что каждая проблема - это еще один гвоздь.

Решение: По крайней мере, старт.

Это одна из первых проблем, с которыми я столкнулся, когда начал писать на React. Как только мое приложение достигло нетривиального размера, (но это не была по-прежнему одна страница без маршрутизации), я обнаружил, что моя производительность рендеринга была в заднице, поэтому я застрял, и вот первые вещи, которые работали для меня.

Шаг 1: Контролируйте когда компоненты обновились.

Отчасти, причина, по которой React и многие другие UI фреймворки являются фантастическими для работы, заключается в том, что выставляете некую привязку между данными и вашими компонентами. Обновите данные и вуаля! Ваш UI изменяется, чтобы отображать их, как правило, без особых усилий. Это фантастика.

React отслеживает ваше приложение и те изменения в деревоподбной структуре в «виртуальном доме». Проблема заключается в том, что React часто не знает, какие компоненты в этом дереве нужно перерисовать, и поэтому слишком многие из них обновляются. К счастью для нас, они предоставляют нам возможность проверять и фильтровать данные, которые мы хотим обновлять в наших компонентах с помощью метода жизненного цикла - shouldComponentUpdate().

Вариант A: использование PureComponents

Совсем недавно React представил «Чистые компоненты», в которых есть механизм, позволяющий уменьшить эту проблему, сделав поверхностное сравнение. В то время как это далеко не окончательное решение, оно, безусловно, поможет вам на этом пути.

class Greeting extends React.PureComponent {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

Примечание из документации:

shouldComponentUpdate() у React.PureComponent сравнивает объекты только поверхностно. Если они содержат сложные структуры данных, это может привести к ложным отрицаниям для более глубоких различий.

Подробнее о чистых компонентах в документации читайте здесь: React Top-Level API

Вариант B: используйте глубокое сравнение там, где вам это нужно

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

Вот пример. Обратите внимание, что я использую библиотеку underscore.js для сравнения объектов. Вы же можете написать свой собственный метод или использовать аналогичную функцию из другой библиотеки, если хотите.

//... inside your React.Component
shouldComponentUpdate(nextProps, nextState) {
  return !_.isEqual(this.props, nextProps) || !_.isEqual(this.state, nextState);
}

Стоит сохранить этот маленький фрагмент, так как часто он будет очень полезен.

Вариант C: обновление только для очень конкретных изменений

У вас может быть компонент или два, которые необходимо обновить, если конкретное свойство или комбинации свойств истинны (например, видимость). В этих случаях вы можете проверить очень специфическое изменение и только тогда перерендеривать.

//... inside your React.Component
shouldComponentUpdate(nextProps) {
  return !(this.props.name === nextProps.name);
}

Шаг 2. Конкретизируйте обновления, к которым вы подключаетесь с Redux

Не используете redux? пропустите этот шаг и начните работу с redux, это значительно улучшит ваше приложение, вашу жизнь и мнение вашей мамы о вас. Потом возвращайтесь чтобы прочесть этот пункт.

Будьте конкретны в том, какую часть состояния вы вносите в свой компонент из store. Здесь ключевым моментом являются селекторы. Они могут быть написаны inline, хотя я обычно извлекаю их в функции, например:

// in reasonable place, ie: ../selectors/common.js
export const selectById = (items = [], id = '') => (id ? items.filter(item => items.id === id)[0] || {} : {});

// in your connected component file
const mapStateToProps = state => ({
  design: selectById(state.designs, state.cart.designId)
});

Шаг 3: Улучшение производительности селекторов с Reselect

Reselect - небольшая дополнительная зависимость, которая позволяет нам создавать селектор, который кэширует результаты, и только если указанный выбор из хранилища изменится, он повторно запустит функцию селектора.

Это особенно полезно для вычисляемых свойств, таких как суммарный, промежуточный итог или налог, рассчитанный для списка товаров в корзине. Как и в случае с базой данных, мы обычно хотим сохранить вычисленные свойства и кэшировать их. Reselect позволяет нам сделать это, а затем обновлять наши компоненты только тогда, когда мы хотим, чтобы они были обновлены.

Шаг 4: Освоить chrome dev tools и закопаться.

Как я сказал в предостережениях, это только начало. По мере того, как ваше приложение будет усложняться, будет множество возможностей и угроз в отношении производительности. Если вы сможете освоить свои инструменты разработки, вы получите четкое представление о том, что происходит в вашем пользовательском интерфейсе, и где начать его исправление.

Вывод

Это действительно просто начало, если вы серьезно относитесь к производительности своего UI. Лучшее место для начала - это официальные документы по оптимизации: Optimizing Performance - React

Присоединяйтесь к нашим каналам FrontEndDev и Web Stack в Telegram, чтобы не пропустить самое интересное из мира веб-разработки!

Оригинал статьи - Practical React: Control Render Thrashing & Rendering Performance