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

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

Инструменты, которые мы собираемся использовать в этом упражнении:

- react-dev-tools: для параметра выделения обновлений установлено значение true.

- профилировщик реакции: Это профилировщик, специфичный для реакции. Вы даже знали, что это было доступно? Я этого не сделал! для этого требуется последняя версия инструментов реагирования и реагирования

- why-did-you-render: это альтернатива обычному пакету Why-did-you-update, который поддерживает хуки реакции. Это поможет нам выявить ненужные рендеры

Как видите, нам не нужно много, у вас, вероятно, все это уже есть в любой среде, с которой вы работаете.

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

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

Позвольте мне приложить несколько картинок к моим словам. Здесь вы можете увидеть, как весь список отображается каждый раз, когда я нажимаю кнопку на любом элементе в списке.

Кстати, вот как вы должны настроить react-dev-tools, чтобы увидеть это, но имейте в виду, что это сильно влияет на производительность (и это очень раздражает).

Если у вас есть последняя версия реакции и хороший браузер (вероятно, оба они верны), у вас должен быть доступ к профилировщику реакции.

Давайте используем его для профилирования и изучения нашего неоптимального списка.

То, что вы видите, это разные коммиты, сделанные реакцией, и время продолжительности рендеринга.

С левой стороны вы можете увидеть график, который представляет дерево компонентов. Вы видите, как он загорается, как рождественская елка? это потому, что мы каждый раз визуализируем все дерево компонентов.

В качестве времени рендера мы получаем от 20 до 31 мс. Это может выглядеть не очень, но мы просто нажимаем на кнопку, и это очень маленькое приложение, которое не должно занимать более 5 или даже 10 мс. Представьте это в масштабе!

Я знаю, о чем ты думаешь, покажи мне код! Ну, я не могу этого сделать, потому что он слишком большой, но я могу разместить здесь основные части.

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

Это основной контейнер, никакой ракетостроения, просто обычный клей.

Это основной компонент, как видите, это просто список компонентов.

Я почти уверен, что вы уже заметили проблему (вы умные!): я создаю новую функцию для каждого элемента в списке каждый раз, когда я повторно отображаю приложение, которое повторно отображает (благодаря редукции) на каждое изменение магазина. В любом случае, давайте посмотрим, что говорит «почему-ты-рендерил», просто для уверенности. Добавить, почему вы сделали рендеринг, так же просто, как добавить это в любое место (я сделал это на index.js)

const WhyDidYouRender = require(‘@welldone-software/why-did-you-render’);

почемуDidYouRender(React,{include: [/Task/]});

Да, именно то, что мы хотя. Хорошо, давайте попробуем исправить код, выполнив следующие действия:

- Сделайте компонент Task умнее: сделайте так, чтобы он вызывал разные обработчики с фактическим заголовком. Это вводит немного связи, но должно быть хорошо.

- Передавать создателей действий в качестве обработчиков компонентов Задачи. На этот раз мы будем использовать одни и те же функции для всех компонентов вместо одной новой функции для каждого дочернего компонента.

- отменить каррирование создателей действий и сделать их предельно простыми, чтобы мы могли использовать вспомогательный метод bindActionCreators

И этого может быть достаточно, не так ли?

Кстати, если вам интересно, что изменилось в задаче, это так же просто, как это

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

Смотрим логи…

Упс, давайте попробуем запомнить компонент…

Ура! Проблема повторного рендеринга исчезла! В этот момент я мог бы показать вам пустую консоль без каких-либо сообщений рендеринга, но какой в ​​этом смысл? Ты можешь просто доверять мне. Давайте лучше проверим профилировщик

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

Вы смотрели на продолжительность рендера? Это от 5 до 7 мс. Какое улучшение!

Хотите что-то более визуальное? Давайте посмотрим основные моменты в реальном приложении:

Это выглядит намного лучше, верно?

Но почему? Как? Ну, раньше, когда мы привязывали создателей действий к диспетчеру вручную, мы создавали совершенно новые функции при каждом обновлении контейнера. Это потому, что когда вы предоставляете функцию для подключения, она вызывается при каждом обновлении контейнера. Кажется, bindActionCreators достаточно умен, чтобы сделать это только один раз.

Можем ли мы сделать это лучше? Что ж, оказывается, мы можем!

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

точно такой же, как этот

Как это возможно? Ну, это немного спрятано в документации по реакции-редукс, но вот оно:

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

Получается, что в нашем случае все, что нам нужно, это простой объект! Имеет ли это те же преимущества в производительности? Ответ - да, он ведет себя точно так же.

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