Если честно, то эту тему в интернете не поднимал только ленивый, но мне кажется, что большинство разработчиков только сейчас начинают задумываться о том, как повысить производительность React приложений.
В этой статье я попробую поделиться своим опытом. Немного о том, за счет чего можно повысить производительность своего приложения и какие инструменты для этого можно использовать.
Если вы из тех людей, которые думают, что React быстрый сам по себе, то у меня для вас плохие новости. Это так только от части.
Я думаю, когда вы только услышали про React впервые, то наверняка слышали утверждение, что React быстрый, потому что он работает с Virtual DOM. Да, это так. В него заложена идея работы с виртуальным DOM (который никакой не DOM, а просто дерево Javascript объектов), который позволяет нам делать минимальные изменения в реальном DOM (потому, что он оооочень медленный). Таким образом, React, работая с виртуальным DOM делает все изменения, вычисляет разницу с предыдущим состоянием и вносит эти изменения в реальный DOM. За счет этого подхода мы действительно выигрываем в производительности, но это только начало!
Возможно вы слышали о таком методе жизненного цикла React, как shouldComponentUpdate
. Это самый простой и очевидный способ решения проблем с производительностью. Если вы не определили его, то он всегда возвращает true
. Если же он, после сравнения своих текущих и пришедших props
, возвращает false
, то React просто не запускает метод render
(перевычислений DOM для них так же не происходит). За счет этого и время исполнения кода уменьшается, памяти занимается меньше и т.д. Для упрощения решения этой задачи в последних версиях React появился PureComponent
. Это почти тот же React.Component
, но, вместо простого возврата true
, его метод shouldComponentUpdate
проводит неглубокое сравнение всех пришедших props
.
Казалось бы - вот она панацея, но тут тоже есть свои подводные камни.
Напомню, что, в отличии от примитивных типов, два объекта нельзя просто сравнить при помощи ==
или ===
. Если вы передаете в свой компонент объект, которые имеет большую вложенность (свойства объекта так же являются объектами) можно сравнить при помощи глубокого сравнения, но это, возможно, будет не самой лучшей идеей. Это может, наоборот, негативно сказаться на производительности. Особенно это касается массивов таких объектов. Для решения этой проблемы вы можете перейти к иммутабельных объектам.
Иммутабельные объекты - это такие объекты, которые не изменяются. Это может быть обычный Javascript объект, но вместо того, чтобы менять его свойства, вы создаете новый. Приведу пример.
// Мутабельный объект
let mutable = { foo: true, bar: false };
mutable.bar = true;
// Иммутабельный объект
let immutable = { foo: true, bar: false };
immutable = Object.assign({}, immutable, { bar: true });
shouldComponentUpdate (nextProps) {
this.props.mutable === nextProps.mutable // true
this.props.immutable === nextProps.immutable // false
}
Конечно, в данном примере иммутабельность объекта всего-лишь условность и вам придется следить, чтобы его свойства никогда не менялись, чтобы гарантировать правильность работы приложения. Для упрощения этой задачи вы можете воспользоваться какими-нибудь библиотеками, например Immutable.js.
Под этим я понимаю такие props
, которые логически новыми не являются, но фактически они другие. Звучит запутанно? Я думаю на примере вы все поймете.
<MyComponent onChange={(e) => { ... }} settings={{ foo: 'bar' }} />
Встречались с таким? Так вот, при каждом вызове render
родительского компонента в MyComponent
попадет новая функция и новый объект. И shouldComponentUpdate
вам тут уже не поможет. Решение этой проблемы очень простое. Просто сделайте их свойствами родительского класса. В следующем примере все ок.
<MyComponent onChange={this.handleChange} settings={this.settings} />
В этой статье я рассказал вам о самом простом (но, пожалуй, самом действенном) способе повысить производительность React приложения. Скоро я напишу продолжение данной статьи и расскажу о других способах, которые я применяю с своей работе.