предисловие
Мы сравним два способа назначения ключей на двух простых примерах и покажем вам разницу между использованием индекса в качестве ключа и разницу между правильным назначением ключа. и проанализируйте его.
Использовать индекс как индекс
Предположим, есть массив:
list = ['a','b','c','d','e']
Новичкам нравится писать React вот так при обходе массива:
list.map((item, index) => {
return <li key={index}>{item}</li>
})
Обход массива при написании Vue:
<ul>
<li v-for="(item, index) in list" :key="index">
{{ item }}
</li>
</ul>
Вроде так писать ошибку не будет? Действительно, нет никакой грамматической проблемы с написанием этого таким образом.
Однако, как только мы поработаем с пройденными элементами, результаты будут сильно отличаться от того, что мы себе представляли.
Я использую React Написать простой пример простого примера друзей, которые знают второй (● '∀ `●):
Принцип написания с vue тот же, нам не нужно ограничиваться используемым фреймворком
код показывает, как показано ниже:
class Items extends React.Component {
constructor(props) {
super(props)
this.state = {list: ['a','b','c','d','e']}
}
render() {
let list = this.state.list
return (
<ul>
{list.map((item, index) => (
<li
key={index}
onClick={this.deleteItem.bind(this, index)}
>
{item}
</li>
))}
</ul>
)
}
// 删除点击的item
deleteItem(index) {
let newlist = [...this.state.list]
newlist.splice(index, 1)
this.setState(()=>({list: newlist}))
}
}
Эти коды сгенерируют такой список:
Мы привязываем событие deleteItem к каждому элементу в коде, то есть нажатие на элемент приведет к удалению соответствующего элемента.
Что ж, давайте продемонстрируем эффект клика:
В это время маленький друг сказал, что ты хотел объяснить спустя долгое время, есть ли проблемы с этим эффектом?
Я хочу сказать, этот эффект не является проблемой,Но! После каждого клика по удалению соответствующего тега li работа браузера на доме очень проблематична!
Мы нажимаем на первый элемент (текст представляет собой ), который, кажется, удаляет его, но что на самом деле делает браузер?
Браузер удалил последний пункт!
Можно подумать, что я видел, что a было удалено, затем b было удалено, затем c....
Взглянем на консоль справа. Каждый раз, когда я нажимаю на первый тег li, я обнаруживаю, что тег ul в структуре dom и все остальные теги li мерцают!
浏览器竟然重新渲染了所有的li标签!
Внутренние действия React после нажатия на удаление узла:
И vue, и react используют механизм виртуального дома (VDom) для обновления реального дома в браузере.
Как оценить разницу между старым и новым виртуальным DOM, использующим алгоритм diff.
В этом случае, когда мы удалим первый узел, тогда реакция сгенерирует новый виртуальный DOM (newVDom), Чтобы упростить задачу, мы анализируем только часть списка.
<!-- 真正的虚拟DOM是js中的对象。 -->
<!-- newVDom -->
<ul>
<li>b</li> <!-- key==0 -->
<li>c</li> <!-- key==1 -->
<li>d</li> <!-- key==2 -->
<li>e</li> <!-- key==3 -->
</ul>
<!-- oldVDom -->
<ul>
<li>a</li> <!-- key==0 -->
<li>b</li> <!-- key==1 -->
<li>c</li> <!-- key==2 -->
<li>d</li> <!-- key==3 -->
<li>e</li> <!-- key==4 -->
</ul>
Алгоритм diff сравнивает новыйVDom со структурой Dom до изменения (oldVDom), мы находим теги li с одинаковым значением ключа, и сравниваем их по очереди сверху вниз. Сравнение обнаружило:
newVDom | oldVDom | Разнообразие |
---|---|---|
key==0 | key==0 | изменение текста |
key==1 | key==1 | изменение текста |
key==2 | key==2 | изменение текста |
key==3 | key==3 | изменение текста |
без | key==4 | удалить узел |
- Текст метки li, ключ которой равен 0, изменился с исходного a на b.
- Текст метки li с ключом 1 изменился с оригинального b на c
- Текст метки li с ключом 2 изменился с оригинального c на d
- Текст метки li с ключом 3 изменился с оригинального d на e
- Последний тег с ключом 4 удален
В реакции перерисовываются только измененные элементы
1.diff помещает все сравниваемые выше различия в пакет исправлений, а затем применяет пакет исправлений к реальному DOM.
2. Изначально нам нужно только удалять первый узел li в списке один за другим каждый раз, когда мы нажимаем
3. После того, как событие щелчка происходит из-за использования индекса в качестве ключа, ключ li во вновь сгенерированном новом VDOM динамически назначается, поэтому происходит дислокация приведенного выше сравнения, из-за чего diff думает, что каждый li имеет изменено, поэтому страница повторно отображает все элементы списка.
Использовать элемент как индекс
Измените код:
list.map((item, index) => (
<li
key={item}
onClick={this.deleteItem.bind(this, index)}
>
{item}
</li>
)
Пусть сейчас ключом будет item (конечно, при выполнении проектов это все же нецелесообразно). Таким образом, у каждого li есть уникальный ключ, а значения — «a», «b», «c», «d», «e» соответственно.
Еще раз демо:
Видно, что каждый раз, когда вы нажимаете, соответствующий новый VDom будет генерироваться с одним узлом меньше.При вводе алгоритма diff для сравнения старого и нового VDom вы получите результат: удаляется только один узел. Затем сопоставьте новый VDom с реальным Dom и выполните только одну операцию, чтобы удалить dom, и не выполняйте повторный рендеринг других li, что значительно повышает производительность.
резюме
Будь то Vue или React, когда мы делаем обход массива для генерации дочерних узлов в пакетах, помните, что значение ключа каждого дочернего узла на одном уровне не может повторяться и не изменится, иначе возникнут непредсказуемые ошибки.