Прежде всего, избегайте использования случайных ключей .
Существует множество способов написания ключей, и некоторые из них будут работать лучше, чем другие.
Чтобы понять, как выбранные нами ключи влияют на производительность, необходимо понять алгоритм согласования React.
https://reactjs.org/docs/reconciliation.html
tl; dr Вводит эвристику для сравнения деревьев Virtual DOM, чтобы сделать это сравнение O (n) с n узлами этого дерева VDOM. Эту эвристику можно разделить на следующие пункты:
- Компоненты другого типа создадут новое дерево : Это означает, что при сравнении старого дерева с новым, если примиритель обнаружит, что узел действительно изменил свой тип (например,
<Button />
на <NotButton />
), приведет к тому, что наша кнопка также будет размонтирована с ее дочерними элементами, а NotButton - с ее дочерними элементами.
- Мы можем подсказать Реагировать на то, как экземпляры сохраняются в VDOM, избегая их повторного создания. Эти подсказки предоставляются нами с ключами. : После принятия решения о том, следует ли сохранять экземпляр в узле (поскольку его тип остается прежним), примиритель будет выполнять итерацию по дочерним узлам этого узла, чтобы сравнить их.
Предположим, теперь у нас есть это:
<div>
<Button title="One" />
<Button title="Two" />
</div>
И мы хотели бы добавить кнопку в DOM на следующем рендере, скажем,
<div>
<Button title="Zero" />
<Button title="One" />
<Button title="Two" />
</div>
Алгоритм будет выглядеть следующим образом:
- Сравнивает
<divs>
в обоих VDOM. Поскольку они имеют одинаковый тип, нам не нужно заново создавать новое дерево. Реквизиты те же, поэтому на данный момент нет изменений, которые можно применить к DOM.
- Кнопка
One
сравнивается с Zero
. Reconciler обнаруживает, что здесь произошла смена реквизита, затем обновляет DOM с таким названием.
- Кнопка
Two
сравнивается с One
. Reconcilier также обнаруживает изменение реквизита и использует DOM для записи этого изменения.
- Обнаруживает, что новый
Button
добавлен как последний дочерний элемент, поэтому создает новый экземпляр Button
в VDOM и записывает это изменение в DOM.
Обратите внимание, что они выполняют много операций в DOM, поскольку сравнивают компоненты по их индексу.
Теперь мы можем исправить это поведение, сообщив нашему посреднику, что эти экземпляры следует использовать повторно. Теперь, давайте иметь это:
<div>
<Button title="One" key="One" />
<Button title="Two" key="Two" />
</div>
И мы хотели бы добавить кнопку в DOM на следующем рендере, скажем
<div>
<Button title="Zero" key="Zero" />
<Button title="One" key="One" />
<Button title="Two" key"Two" />
</div>
Алгоритм будет выглядеть следующим образом:
- Сравнивает
<divs>
в обоих VDOM. Поскольку они имеют одинаковый тип, нам не нужно заново создавать новое дерево. Реквизиты те же, поэтому на данный момент нет изменений, которые можно применить к DOM.
- Берет первого ребенка из детей. «Это
Button
», говорит примиритель. «И есть ключ» («One»). Затем ищет детей с тем же ключом в новом списке детей. «О, я столкнулся с этим! но примиритель понимает, что нет никаких изменений в его реквизитах . Тогда никакие операции DOM для этого не понадобятся.
- Тот же сценарий происходит со вторым
Button
, он будет сравниваться на keys
вместо index
. Понимает, что это тот же экземпляр, и реквизиты не были изменены, поэтому React решает не применять изменения в DOM.
- Для
Button
с ключом 'Zero', поскольку не существует дочернего элемента с таким же ключом , он понимает, что экземпляр должен быть создан в VDOM, и это изменение должно быть записано в DOM.
Таким образом, использование ключей с предсказуемым содержимым помогает примирителю выполнять меньше операций с DOM. Здоровые ключи - это те, которые могут быть выведены из отображаемого объекта, например, name
, или id
, или даже url
, если мы преобразуем urls
в <imgs />
.
А как насчет key=index
? Не будет иметь никакого эффекта, так как по умолчанию, компилятор сравнивает по позиции, то есть по индексу.
Эти ключи должны быть глобально уникальными? Не обязательно. Они должны быть уникальными среди братьев и сестер, поэтому примиритель может различать их во время итерации по дочерним узлам.
А как насчет случайных ключей?Этого следует избегать любой ценой.Если ключ изменяется при каждом рендеринге, это будет приводить к тому, что React будет уничтожать и создавать экземпляры в VDOM (и, следовательно, делать дополнительные записи в DOM), поскольку компонент с ключом не найден среди новых дочерних элементов, но новыйс тем же типом.
Если вывод рендеринга похож на
<div>
<Button key={randomGenerator()} />
</div>
Затем каждый раз выполняется render
(например, из-за изменения реквизита / состояния или даже если его родительПосле повторного рендеринга и shouldComponentUpdate
возвращает true
), будет сгенерирован новый ключ randomGenerator()
.Это будет выглядеть так:
'Эй!Я нашел Button
с ключом F67BMkd==
, но ни один не был найден в следующем.Я удалю это.'Ой!Я встретил Button
с ключом SHDSA++5
!Давайте создадим новый ».
Всякий раз, когда примиритель говорит, что экземпляр должен быть удален и размонтирован, его внутреннее состояние будет потеряно;даже если мы установим его снова.Экземпляр в VDOM не будет сохранен в этом случае.
Button
был таким же, но примиритель сделал беспорядок в DOM.
Надеюсь, это поможет.