У меня нет проблем с использованием индекса в качестве ключа.

внешний интерфейс Vue.js
У меня нет проблем с использованием индекса в качестве ключа.

предисловие

все знакомоVueМелкие партнеры в стеке технологий знают, что в сцене рендеринга списка его нельзя использовать.indexилиrandomтак какkey.

Есть также много мелких партнеров, которых интервьюер расспросит более подробно во время интервью.indexтак какkeyВ чем будет проблема? Если вы используетеrandomтак какkeyВ чем будет проблема? Если вы используете уникальную константуidтак какkeyКаковы преимущества?

Этот вопрос, на первый взгляд, кажется исследованием нашего процесса сравнения с коллегами.diffАлгоритмическое понимание, единственная константаkeyЭто может помочь нам быстрее найти многоразовыеVNode, чтобы снизить нагрузку на производительность, используйтеindexтак какkeyможет привести кVNodeнеправильное повторное использование, что приводит кbug, при использованииrandomтак какkeyприведет кVNodeЕго нельзя использовать повторно все время, что сильно влияет на производительность.

Есть ли проблема с этим ответом? нет проблем.

Но если этот вопрос из 100, я могу дать вам только 99 баллов.

а также1分,относится кVue 更新流程Если мы не разъясним мелкие детали в статье, это может вызвать у нас проблемы в реальном бизнес-сценарии.

Что вас беспокоит?

взять каштан

Сразу в тему, посмотрите кусок кода,indexтак какkey, если мы удалим определенный элемент, что будет в результате?

<template>
  <div id="app">
    <div v-for="(item, index) in data" :key="index">
      <Child />
      <button @click="handleDelete(index)">删除这一行</button>
    </div>
  </div>
</template>
​
<script>
​
export default {
  name: "App",
  components: {
    Child: {
      template: '<span>{{name}}{{Math.floor(Math.random() * 1000)}}</span>',
      props: ['name']
    }
  },
  data() {
    return {
      data: [
        { name: "小明" },
        { name: "小红" },
        { name: "小蓝" },
        { name: "小紫" },
      ]
    };
  },
  methods: {
    handleDelete(index) {
      this.data.splice(index, 1);
    },
  }
};
</script>

увидеть результаты

Можно заметить, что хотя мы и не удалили последний, но последний в итоге был удален.Как ни странно, но если вы знаетеVueизdiffпроцесс, этот результат должен соответствовать вашим ожиданиям.

diff

Большие разделы исходного кода столбцов усложнят наше понимание, поэтому я помещаюVueиз更新流程Упрощенно на картинке:

Вообще говоря, мы говоримVueизdiffпроцесс, что означаетpatchVnodeupdateChildrenЭто то, что мы называем сравнением на одном уровне, которое на самом деле является сравнением старого и нового.Vnodeмножество.

VueЧетыре переменные указателя будут объявлены для записи нового и старого соответственно.VnodeПервый и последний индекс массива путем перемещения указателей первого и последнего индекса в соответствии с порядком новой головы и старой головы, новой хвостовой части и старой хвостовой части, старой головы и новой хвостовой части, и старый хвост и новая голова, новое и старое сравниваются по очереди.Vnode, если не попалsameVnode, тоoldVnode.keyподдерживать как одинmap, продолжайте запрашивать, содержит ли онnewVnode.key, если попалsameVnode, затем выполнить рекурсивноpatchVnode. Если он не попал в конце, это означает, что повторное использование невозможно.Vnode, создать новоеdomузел.

подобноnewVnodeУказатели головы и хвоста встречаются первыми, указывая на то, чтоnewVnodeОн был пройден и удален непосредственноoldVnodeпревышение, еслиoldVnodeУказатели головы и хвоста встречаются первыми, указывая на то, чтоoldVnodeОбход завершен, прямо добавляемnewVnodeизбыточная часть.

Такое прямое текстовое описание будет выглядеть бледно, поэтому я подготовил动画:

первый шаг:

Шаг 2:

третий шаг:

четвертый шаг:

пятый шаг:

Шаг 6:

Теоретически, пока вы скользите достаточно быстро, вы можете подняться на несколько картинок вверх 😊

Изображения, описывающие вышеприведенный процесс updateChildren, взяты изДемистификация технологии Vue обновление компонентаГлава, рекомендуется прочитать исходный текст

Я долго пытался и не мог сделать анимацию.В то же время я чувствовал, что эти картинки могут дать нам достаточно интуитивное ощущение, поэтому я переместил их напрямую.

Вторжение.

использоватьindexтак какkeyв чем будет проблема

Как мы сказали выше, судить о старом и новомVnodeВозможность повторного использования зависит отsameNodeметод, этот метод очень прост, это сравнитьVnodeНекоторые свойства , гдеkeyявляется самым важным фактором

function sameVnode (a, b) {
    return (
      a.key === b.key &&
      a.asyncFactory === b.asyncFactory && (
        (
          a.tag === b.tag &&
          a.isComment === b.isComment &&
          isDef(a.data) === isDef(b.data) &&
          sameInputType(a, b)
        ) || (
          isTrue(a.isAsyncPlaceholder) &&
          isUndef(b.asyncFactory.error)
        )
      )
    )
  }

Вернемся к каштану выше и посмотрим, что пошло не так.

генерируется приведенным выше кодомVNodeЧто-то вроде этого:

[
  {
    tag: 'div',
    key: 0,
    children: [
      {
        tag: VueComponent, 
        elm: 408, // 这个Vnode对应的真实dom是408
      },
      {
        tag: 'button'
      }
    ]
  },
  {
    tag: 'div',
    key: 1,
    children: [
      {
        tag: VueComponent,
        elm: 227, // 这个Vnode对应的真实dom是227
      },
      {
        tag: 'button'
      }
    ]
  }
  ...
]

Мы удаляем первый фрагмент данных, новыйVNodeЧто-то вроде этого:

[
  {
    tag: 'div',
    key: 0,
    children: [
      {
        tag: VueComponent,
        elm: 227, // 这个Vnode对应的真实dom是227
      },
      {
        tag: 'button'
      }
    ]
  },
  {
    tag: 'div',
    key: 1,
    children: [
      {
        tag: VueComponent,
        elm: 324, // 这个Vnode对应的真实dom是324
      },
      {
        tag: 'button'
      }
    ]
  }
  ...
]

насЛогика человеческой плотипосмотри на этих двоихVnodeмассив, так какkeyВсе равны 0, поэтому при сравнении первого будетsameNode, что приводит к неправильному повторному использованию, тоupdateChildren, дочерний узелVnodeвсе еще хитsameVnode, точно так же попадут второй и третий предметыsameVnode, прямо злоупотребляя связанной с ним истинойdomNode, поэтому мы, очевидно, удаляем первый, но производительность пользовательского интерфейса удаляется последним.

Так это заканчивается здесь?

Конечно нет, потому что многие друзья только выходят на связьVue, также используетсяindexтак какkey, какие-то офигенные проекты даже запущены, и вроде никто не ищет неприятностей

Зачем?

почему я используюindexтак какkeyнет проблем

Если я изменю код на этот и удалю определенный элемент, каков будет результат?

<template>
  <div id="app">
    <div v-for="(item, index) in data" :key="index">
      <Child :name="`${item.name}`" />
      <button @click="handleDelete(index)">删除这一行</button>
    </div>
  </div>
</template>

увидеть результаты

Фейк, мы явно поставилиVueиз更新流程понятно, пользуйтесьindexтак какkeyприведет кVnodeНеправильное повторное использование, почему он ведет себя нормально здесь?

давайте посмотрим еще раз更新流程Упрощенная схема:

тип компонентаVnode,существуетpatchVnodeбудет выполняться в течениеprePatchХук-функция для компонентаpropsDataпереназначение, которое вызываетsetter,еслиpropsDataЕсли значение изменится, сработаетupdate, чтобы повторно отобразить компонент

мы можем сноваЛогика человеческой плотиТеперь на этот раз мы удаляем второй, потому чтоkeyпоследовательный, новыйVnodeМассив по-прежнему будет повторно использовать старыйVnodeПервые три элемента массива, первый элементVnodeПравильное повторное использование, компонентыpropsDataБез изменений, не срабатываетupdate, который напрямую повторно использует связанную с ним истинуdomузел, а второйVnodeнеправильное повторное использование, но компонентpropsDataИзменился с маленького красного на маленький синий, вызывая срабатываниеupdate, компонент перерисовывается, поэтому мы видим, чтоrandomИзменения произошли, и третья такая же.

ха~

В этот момент я, наконец, понял, что я действительно умный маленький призрак.

Так это заканчивается здесь?

На самом деле пока нет, например, давайте изменим код

<template>
  <div id="app">
    <div v-for="(item, index) in data" :key="index">
      <span>{{item.name}}</span>
      <button @click="handleDelete(index)">删除这一行</button>
    </div>
  </div>
</template>

увидеть результаты

На этот раз у нас нет типа компонентаVnode, не будет выполнятьсяprePatch, почему это все еще нормально?

Посмотрите еще раз на выше更新流程рисунок, тип текстаVnode, когда старый и новый тексты различаются, они будут перезаписаны напрямую.

На данный момент мы полностью поняли, почему рекомендуется использовать единственную константу в сцене рендеринга спискаidтак какkey. Помимо спецификации кода, даже в некоторых сценариях проблема неbugФорма открыта, но не может быть повторно использована или использована неправильноVnode, приведет к повторному рендерингу компонента, и нагрузка на производительность этой части по-прежнему очень велика!

последний 1 балл

На бумаге я чувствую себя поверхностным в конце, и я абсолютно точно знаю, что это дело должно быть сделано.

Я прочитал это в первый разVue2Когда я получил исходный код, я думал, что ясно понял эту часть знаний, пока маленький партнер в команде не задал мне вопрос со списком в виде простого текста.

нужно быть осторожнымdebugснова更新流程Считается решенным сомнением в виду, составляют это1分зазор

Цитата выглядит следующим образом:

Исходный код Vue2

Демистификация технологии Vue