Хотите получить наглядное представление о том, что происходит с компонентами, когда вы работаете с React? Читайте под катом перевод статьи Ohans Emmanuel, опубликованной на сайте freeCodeCamp.
Часто устранить определенную ошибку не получается потому, что не знаешь каких-то основ. По этой же причине бывает трудно освоить более продвинутые техники.
В этой статье я попробую рассказать о некоторых принципах работы React, которые, по моему мнению, вам необходимо понимать.
Мы не будем разбирать эти принципы с технической точки зрения. Есть масса других статей, в которых рассматриваются такие понятия, как свойства компонента (
Я же хочу поговорить о том, что лежит в основе большинства технических операций, которые вы будете выполнять с React.
Готовы?
Первое, что каждый узнает в React, — это то, как создавать компоненты. Я уверен, что вы тоже этому учились.
Например:
Большая часть компонентов, которые вы прописываете, возвращает вам некоторые элементы.
Изнутри этот процесс выглядит так: большинство компонентов возвращают дерево элементов.
После внутренней оценки компоненты часто возвращают дерево элементов
Кроме того, вы наверняка помните, что компоненты работают как функции, возвращающие значения на основании своих значений
Компоненты — это что-то вроде функций с параметрами props и state
Следовательно, всякий раз, когда значения свойств (
Если значения props или state меняются, дерево элементов перерисовывается. В результате появляется новое дерево элементов
Если компонент основан на наследовании классов, дерево элементов возвращает функция
Если же компонент функциональный, его возвращаемое значение дает дерево элементов.
Почему это важно?
Рассмотрим компонент
Рендеринг этого компонента возвращает дерево элементов.
Дерево элементов, возвращаемое после перерисовки <MyComponent />
Что происходит, когда значение
Ну, возвращается новое дерево элементов!
НОВОЕ дерево элементов, возвращаемое после перерисовки <MyComponent /> с другими props
Хорошо.
Теперь в распоряжении React два разных дерева — предыдущее и текущее дерево элементов.
На этом этапе React сравнивает оба дерева, чтобы найти изменения.
Два разных дерева. Что же именно изменилось?
Дерево не изменилось полностью, а лишь частично обновилось (так происходит в большинстве случаев).
После сравнения React обновляет фактический DOM с учетом изменений в новом дереве элементов.
Все просто, не так ли?
Сравнение двух деревьев на предмет изменений называется «согласование». Мы с вами смогли разобрать этот процесс, несмотря на то что он достаточно сложный.
Еще до того как вы начали работать с React, вам часто приходилось слышать, как он крут, в том числе тем, что вносит только важные изменения в обновляемую модель DOM.
Из React Docs: инспектор DOM, показывающий детали обновления
Все ли так?
Все так.
Однако помните: прежде чем перейти к обновлению DOM, React построит дерево элементов для различных компонентов и проделает необходимое сравнение. Проще говоря, он найдет различия между предыдущим деревом элементов и текущим.
Я повторяю это потому, что новички в React могут не заметить снижения производительности своих приложений, считая, что React обновляет только необходимые элементы в DOM.
Это, конечно, правда, но проблемы с производительностью большинства приложений на React начинаются еще до обновления DOM!
Даже если дерево элементов компонента маленькое, его рендеринг занимает некоторое время (хотя бы незначительное). Чем больше дерево элементов компонента, тем больше времени занимает рендеринг.
Это значит, что перерисовка деревьев элементов компонентов вашего приложения с React будет лишней, если она НЕ необходима.
Позвольте мне показать это на простом примере.
Представьте себе приложение со структурой компонентов, как на иллюстрации ниже.
Приложение с родительским компонентом A и дочерними компонентами B, C и D
Общий компонент-контейнер
Родительский компонент A получает некоторые свойства и передает их дальше дочернему компоненту D
Теперь, когда изменяется значение свойства в компоненте
Когда родительский компонент получает новые свойства, каждый дочерний элемент перерисовывается, и возвращается новое дерево
Соответственно, компоненты
Эта лишняя перерисовка и есть ненужный рендеринг.
В этом примере компоненты
Есть много способов решить эту проблему, и я описывал их в моей недавней статье How to Eliminate React Performance Issues («Как минимизировать проблемы производительности React»).
Идем дальше. Посмотрите на приложение ниже.
Cardie в действии :)
Я назвал это приложение Cardeу.
Когда я нажимаю кнопку, чтобы изменить профессию пользователя, я могу выбрать выделение обновлений для DOM, как показано ниже.
Активируйте визуальное отображение обновлений (Paint Flashing) с помощью Chrome DevTools
Теперь мне видно, что было обновлено в DOM.
Это визуальный способ отмечать элементы, которые нужно обновить в DOM. Обратите внимание на зеленую подсветку вокруг текста I am a Librarian («Я библиотекарь».)
Это все здорово, конечно, но меня беспокоит исходный рендеринг дерева элементов компонентов React.
Я могу проверить и его.
Поставьте галочку в React DevTools, чтобы включить подсветку обновляемых элементов
Теперь я вижу, какие компоненты перерисовываются на самом деле, когда я нажимаю эту кнопку.
Обратите внимание на зеленую подсветку вокруг карты пользователя
Вы видите, насколько отличаются визуальный способ отмечать элементы, которые нужно обновить в DOM, и обновления рендеринга, которые проводит сам React?
React перерисовывает всю карту пользователя, а обновляется только короткий текст.
И это важно.
Думаю, что теперь у вас появилось более наглядное представление о том, что и как происходит с вашими компонентами в React.
На самом деле, происходит гораздо больше, чем я вам сейчас тут рассказал. Тем не менее, это хорошее начало.
Вперед — к созданию крутых приложений!
Учитесь работать с React/Redux? Если да, у меня есть отличная серия книг, посвященная Redux. Некоторые говорят, что это лучшая техническая литература, которую они читали!
Часто устранить определенную ошибку не получается потому, что не знаешь каких-то основ. По этой же причине бывает трудно освоить более продвинутые техники.
В этой статье я попробую рассказать о некоторых принципах работы React, которые, по моему мнению, вам необходимо понимать.
Мы не будем разбирать эти принципы с технической точки зрения. Есть масса других статей, в которых рассматриваются такие понятия, как свойства компонента (
props
), состояние (state
), контекст (context
), изменение состояния компонента (setState
) и прочие.Я же хочу поговорить о том, что лежит в основе большинства технических операций, которые вы будете выполнять с React.
Готовы?
Скрытые процессы React
Первое, что каждый узнает в React, — это то, как создавать компоненты. Я уверен, что вы тоже этому учились.
Например:
// functional component
function MyComponent() {
return <div> My Functional Component </div>
}
// class based component
class MyComponent extends React.Component {
render() {
return <div> My Class Component </div>
}
}
Большая часть компонентов, которые вы прописываете, возвращает вам некоторые элементы.
function MyComponent() {
return <span> My Functional Component </span> //span element
}
class MyComponent extends React.Component {
render() {
return <div> My Class Component </div> //div element
}
}
Изнутри этот процесс выглядит так: большинство компонентов возвращают дерево элементов.
После внутренней оценки компоненты часто возвращают дерево элементов
Кроме того, вы наверняка помните, что компоненты работают как функции, возвращающие значения на основании своих значений
props
и state
.Компоненты — это что-то вроде функций с параметрами props и state
Следовательно, всякий раз, когда значения свойств (
props
) и состояния (state
) компонента меняются, создается новое дерево элементов.Если значения props или state меняются, дерево элементов перерисовывается. В результате появляется новое дерево элементов
Если компонент основан на наследовании классов, дерево элементов возвращает функция
<code>render</code>.
<source lang="javascript">class MyComponent extends React.Component {
render() {
//this function is invoked to return the tree of elements
}
}
Если же компонент функциональный, его возвращаемое значение дает дерево элементов.
function MyComponent() {
// the return value yields the tree of elements
return <div>
</div>
}
Почему это важно?
Рассмотрим компонент
<MyComponent />
, который принимает prop
, как показано ниже.<MyComponent name='Ohans'/>
Рендеринг этого компонента возвращает дерево элементов.
Дерево элементов, возвращаемое после перерисовки <MyComponent />
Что происходит, когда значение
name
меняется?<MyComponent name='Quincy'/>
Ну, возвращается новое дерево элементов!
НОВОЕ дерево элементов, возвращаемое после перерисовки <MyComponent /> с другими props
Хорошо.
Теперь в распоряжении React два разных дерева — предыдущее и текущее дерево элементов.
На этом этапе React сравнивает оба дерева, чтобы найти изменения.
Два разных дерева. Что же именно изменилось?
Дерево не изменилось полностью, а лишь частично обновилось (так происходит в большинстве случаев).
После сравнения React обновляет фактический DOM с учетом изменений в новом дереве элементов.
Все просто, не так ли?
Сравнение двух деревьев на предмет изменений называется «согласование». Мы с вами смогли разобрать этот процесс, несмотря на то что он достаточно сложный.
React обновляет только необходимое, правда?
Еще до того как вы начали работать с React, вам часто приходилось слышать, как он крут, в том числе тем, что вносит только важные изменения в обновляемую модель DOM.
Из React Docs: инспектор DOM, показывающий детали обновления
Все ли так?
Все так.
Однако помните: прежде чем перейти к обновлению DOM, React построит дерево элементов для различных компонентов и проделает необходимое сравнение. Проще говоря, он найдет различия между предыдущим деревом элементов и текущим.
Я повторяю это потому, что новички в React могут не заметить снижения производительности своих приложений, считая, что React обновляет только необходимые элементы в DOM.
Это, конечно, правда, но проблемы с производительностью большинства приложений на React начинаются еще до обновления DOM!
Ненужный рендеринг vs визуальные обновления
Даже если дерево элементов компонента маленькое, его рендеринг занимает некоторое время (хотя бы незначительное). Чем больше дерево элементов компонента, тем больше времени занимает рендеринг.
Это значит, что перерисовка деревьев элементов компонентов вашего приложения с React будет лишней, если она НЕ необходима.
Позвольте мне показать это на простом примере.
Представьте себе приложение со структурой компонентов, как на иллюстрации ниже.
Приложение с родительским компонентом A и дочерними компонентами B, C и D
Общий компонент-контейнер
A
получает определенное свойство. Однако делается это только для того, чтобы передать это свойство компоненту D
.Родительский компонент A получает некоторые свойства и передает их дальше дочернему компоненту D
Теперь, когда изменяется значение свойства в компоненте
A
, все дочерние элементы A
перерисовываются для вычисления нового дерева элементов.Когда родительский компонент получает новые свойства, каждый дочерний элемент перерисовывается, и возвращается новое дерево
Соответственно, компоненты
B
и С
также повторно рендерятся, даже если они не изменились вообще! Они не получили никаких новых свойств!Эта лишняя перерисовка и есть ненужный рендеринг.
В этом примере компоненты
B
и C
перерисовывать не нужно, но React об этом не знает.Есть много способов решить эту проблему, и я описывал их в моей недавней статье How to Eliminate React Performance Issues («Как минимизировать проблемы производительности React»).
Идем дальше. Посмотрите на приложение ниже.
Cardie в действии :)
Я назвал это приложение Cardeу.
Когда я нажимаю кнопку, чтобы изменить профессию пользователя, я могу выбрать выделение обновлений для DOM, как показано ниже.
Активируйте визуальное отображение обновлений (Paint Flashing) с помощью Chrome DevTools
Теперь мне видно, что было обновлено в DOM.
Это визуальный способ отмечать элементы, которые нужно обновить в DOM. Обратите внимание на зеленую подсветку вокруг текста I am a Librarian («Я библиотекарь».)
Это все здорово, конечно, но меня беспокоит исходный рендеринг дерева элементов компонентов React.
Я могу проверить и его.
Поставьте галочку в React DevTools, чтобы включить подсветку обновляемых элементов
Теперь я вижу, какие компоненты перерисовываются на самом деле, когда я нажимаю эту кнопку.
Обратите внимание на зеленую подсветку вокруг карты пользователя
Вы видите, насколько отличаются визуальный способ отмечать элементы, которые нужно обновить в DOM, и обновления рендеринга, которые проводит сам React?
React перерисовывает всю карту пользователя, а обновляется только короткий текст.
И это важно.
Заключение
Думаю, что теперь у вас появилось более наглядное представление о том, что и как происходит с вашими компонентами в React.
На самом деле, происходит гораздо больше, чем я вам сейчас тут рассказал. Тем не менее, это хорошее начало.
Вперед — к созданию крутых приложений!
Учитесь работать с React/Redux? Если да, у меня есть отличная серия книг, посвященная Redux. Некоторые говорят, что это лучшая техническая литература, которую они читали!