Ответ 1
Как вы решаете, как вы выбираете эти три на основе цели/размера/реквизита/поведения наших компонентов?
Расширение от React.PureComponent
или React.Component
с помощью настраиваемого метода shouldComponentUpdate
имеет последствия для производительности. Использование функциональных компонентов без состояния является "архитектурным" выбором и не имеет каких-либо преимуществ по производительности из коробки (пока).
-
Для простых, только для презентаций компонентов, которые необходимо легко использовать повторно, предпочитайте функциональные компоненты без состояния. Таким образом, вы уверены, что они отделены от реальной логики приложения, что они мертвы - легко тестируются и что у них нет неожиданных побочных эффектов. Исключение составляет то, что по какой-то причине у вас их много или вам действительно нужно оптимизировать свой метод рендеринга (поскольку вы не можете определить
shouldComponentUpdate
для функционального компонента без состояния). -
Расширьте
PureComponent
, если вы знаете, что ваш результат зависит от простого реквизита/состояния ( "простой" означает отсутствие вложенных структур данных, поскольку PureComponent выполняет неглубокое сравнение). И вам нужно/может получить некоторые улучшения производительности. -
Расширьте
Component
и реализуйте свой собственныйshouldComponentUpdate
, если вам нужно получить некоторые выгоды от производительности, выполнив собственную логику сравнения между следующими/текущими реквизитами и состоянием. Например, вы можете быстро выполнить глубокое сравнение с помощью lodash # isEqual:class MyComponent extends Component { shouldComponentUpdate (nextProps, nextState) { return !_.isEqual(this.props, nextProps) || !_.isEqual(this.state, nextState); } }
Кроме того, реализация вашего собственного shouldComponentUpdate
или расширение от PureComponent
- это оптимизация, и, как обычно, вы должны начать изучать это, только если у вас есть проблемы с производительностью (избегать преждевременных оптимизаций).
Как правило, я всегда стараюсь делать эти оптимизации после того, как приложение находится в рабочем состоянии, причем большинство функций уже реализованы. Гораздо проще сосредоточиться на проблемах производительности, когда они действительно мешают.
Подробнее
Функциональные компоненты без состояния:
Они определяются только с помощью функции. Так как нет внутреннего состояния для компонента без состояния, вывод (то, что отображается) зависит только от реквизита, указанного как вход для этой функции.
Плюсы:
-
Самый простой способ определения компонента в реактиве. Если вам не нужно управлять каким-либо государством, зачем беспокоиться о классах и наследовании? Одно из главных отличий между функцией и классом состоит в том, что с помощью функции вы уверены, что вывод зависит только от ввода (не от истории предыдущих исполнений).
-
В идеале в вашем приложении вы должны стремиться иметь как можно больше элементов без состояния, потому что это обычно означает, что вы переместили свою логику за пределы уровня представления и переместили ее на что-то вроде redux, что означает, что вы можете протестировать свою реальную логики без необходимости делать что-либо (гораздо проще тестировать, многократно использовать и т.д.).
Минусы:
-
Нет методов жизненного цикла. У вас нет возможности определить
componentDidMount
и других друзей. Обычно вы делаете это в родительском компоненте выше в иерархии, чтобы вы могли превратить всех детей в безстоящие. -
Никакой способ ручного управления, когда требуется повторная визуализация, поскольку вы не можете определить
shouldComponentUpdate
. Повторное рендеринг происходит каждый раз, когда компонент получает новые реквизиты (нет возможности для мелкого сравнения и т.д.). В будущем React может автоматически оптимизировать компоненты без гражданства, так как теперь есть некоторые библиотеки, которые вы можете использовать. Поскольку компоненты без состояния являются просто функциями, в основном это классическая проблема "memoization функции". -
Refs не поддерживаются: https://github.com/facebook/react/issues/4936
Компонент, который расширяет класс PureComponent VS. Нормальный компонент, расширяющий класс Component:
React используется для PureRenderMixin
, который вы могли бы прикрепить к классу, определенному с помощью синтаксиса React.createClass
. Смесин просто определял бы shouldComponentUpdate
, выполняя неглубокое сравнение между следующими реквизитами и следующим состоянием, чтобы проверить, не изменилось ли что-либо. Если ничего не меняется, тогда нет необходимости выполнять повторную визуализацию.
Если вы хотите использовать синтаксис ES6, вы не можете использовать mixins. Поэтому для удобства React представил класс PureComponent
, который вы можете наследовать вместо использования Component
. PureComponent
просто реализует shouldComponentUpdate
тем же способом PureRendererMixin
. Это в основном удобная вещь, поэтому вам не нужно ее реализовывать самостоятельно, так как нечеткое сравнение текущего/следующего состояния и реквизита, вероятно, является наиболее распространенным сценарием, который может дать вам несколько быстрых побед.
Пример:
class UserAvatar extends Component {
render() {
return <div><img src={this.props.imageUrl} /> {{ this.props.username }} </div>
}
}
Как вы можете видеть, результат зависит от props.imageUrl
и props.username
. Если в родительском компоненте вы оказываете <UserAvatar username="fabio" imageUrl="http://foo.com/fabio.jpg" />
с тем же реквизитом, React будет вызывать render
каждый раз, даже если вывод будет точно таким же. Помните, что React реализует dom diffing, поэтому DOM не будет фактически обновляться. Тем не менее, выполнение диверсии дома может быть дорогостоящим, поэтому в этом случае это будет пустой тратой.
Если компонент UserAvatar
расширяет PureComponent
, то выполняется неглубокое сравнение. И поскольку реквизиты и nextProps одинаковы, render
вообще не будет вызываться.
Примечания к определению "чистый" в реактиве:
В общем, "чистая функция" - это функция, которая всегда оценивает один и тот же результат при одном и том же входе. Результат (для React, то, что возвращается методом render
) не зависит от какой-либо истории/состояния и не имеет никаких побочных эффектов (операции, которые изменяют "мир" вне функции).
В React, компоненты без состояния не обязательно являются чистыми компонентами в соответствии с вышеприведенным определением, если вы вызываете "без состояния" компонент, который никогда не вызывает this.setState
и который не использует this.state
.
Фактически, в PureComponent
вы все равно можете выполнять побочные эффекты во время жизненного цикла. Например, вы можете отправить запрос ajax внутри componentDidMount
, или вы могли бы выполнить некоторый расчет DOM для динамической настройки высоты div внутри render
.
Определение "немых компонентов" имеет более "практическое" значение (по крайней мере, в моем понимании): немой компонент "получает ответ", что делать с помощью родительского компонента через реквизит, и не знает, как это делать но вместо этого использует обратные вызовы реквизита.
Пример "умного" AvatarComponent
:
class AvatarComponent extends Component {
expandAvatar () {
this.setState({ loading: true });
sendAjaxRequest(...).then(() => {
this.setState({ loading: false });
});
}
render () {
<div onClick={this.onExpandAvatar}>
<img src={this.props.username} />
</div>
}
}
Пример "немой" AvatarComponent
:
class AvatarComponent extends Component {
render () {
<div onClick={this.props.onExpandAvatar}>
{this.props.loading && <div className="spinner" />}
<img src={this.props.username} />
</div>
}
}
В конце я бы сказал, что "немой", "без гражданства" и "чистый" - это совершенно разные концепции, которые иногда могут перекрываться, но не обязательно, в зависимости от вашего варианта использования.