Проверка ключей (key attribute)

В Vue.js атрибут key играет критически важную роль при работе с списками компонентов или элементов. Он позволяет Vue эффективно отслеживать изменения в DOM, минимизируя перерисовки и сохраняя внутреннее состояние компонентов. В тестах это особенно важно, так как некорректные ключи могут привести к неожиданному поведению компонентов и тестов.

Назначение атрибута key

Атрибут key используется для:

  • Идентификации элементов в списках: Vue сопоставляет элементы старого и нового виртуального DOM по ключу, чтобы определить, какие элементы следует обновить, переместить или удалить.
  • Сохранения состояния дочерних компонентов: Если дочерний компонент имеет собственное состояние, правильный key гарантирует его сохранение при обновлении родительского списка.

Пример:

<template>
  <ul>
    <li v-for="item in items" :key="item.id">{{ item.name }}</li>
  </ul>
</template>

<script>
export default {
  data() {
    return {
      items: [
        { id: 1, name: 'Элемент 1' },
        { id: 2, name: 'Элемент 2' },
      ]
    }
  }
}
</script>

Проверка ключей в тестах

Во Vue Test Utils есть несколько способов убедиться, что компоненты получают правильные ключи.

1. Проверка наличия ключей у элементов

Метод findAll возвращает коллекцию обёрток компонентов или элементов. Каждая обёртка имеет метод attributes(), позволяющий проверить атрибут key:

import { mount } from '@vue/test-utils';
import MyList from '@/components/MyList.vue';

const wrapper = mount(MyList);

const items = wrapper.findAll('li');
expect(items[0].attributes('key')).toBe('1');
expect(items[1].attributes('key')).toBe('2');

Особенность: key не является реактивным атрибутом и не попадает в props. Для проверки необходимо использовать attributes() или напрямую проверять VNode, если нужно более глубокое тестирование.


2. Проверка ключей у компонентов

Для компонентов, рендерящихся в списке, важно убедиться, что ключи установлены правильно, чтобы компоненты не пересоздавались при изменении данных:

const wrapper = mount({
  template: `
    <div>
      <child-component v-for="item in items" :key="item.id" :item="item" />
    </div>
  `,
  components: { ChildComponent },
  data() {
    return {
      items: [
        { id: 1, name: 'A' },
        { id: 2, name: 'B' }
      ]
    }
  }
});

const children = wrapper.findAllComponents(ChildComponent);
expect(children[0].vm.$options.key).toBe(1); // Доступ через внутренний VNode
expect(children[1].vm.$options.key).toBe(2);

Для Vue 3 проверка ключа через внутренний VNode выглядит так:

children.forEach((child, index) => {
  expect(child.vm.$.vnode.key).toBe(wrapper.vm.items[index].id);
});

Влияние ключей на поведение компонентов

  1. Сохранение состояния Если ключ не уникален или отсутствует, Vue может пересоздать компонент при обновлении списка, что приведёт к потере локального состояния. Тесты помогают выявить такие ошибки.

  2. Оптимизация рендеринга Неправильные ключи заставляют Vue удалять и создавать DOM-узлы заново вместо их переработки. В тестах это проявляется как лишние вызовы lifecycle hooks (mounted, beforeUpdate).

  3. Отслеживание перемещений элементов При изменении порядка элементов проверка ключей позволяет убедиться, что Vue корректно перемещает компоненты, а не создаёт новые:

await wrapper.setData({ items: [{ id: 2, name: 'B' }, { id: 1, name: 'A' }] });

const children = wrapper.findAllComponents(ChildComponent);
expect(children[0].vm.item.id).toBe(2);
expect(children[1].vm.item.id).toBe(1);

Рекомендации по тестированию ключей

  • Всегда проверять ключи для списков компонентов с локальным состоянием.
  • Использовать реальные идентификаторы данных в качестве ключей вместо индексов, чтобы избежать неожиданных пересозданий.
  • Тестировать поведение после обновления списка — это помогает выявить проблемы с сохранением состояния и оптимизацией рендеринга.
  • Проверять ключи через VNode, если требуется убедиться, что Vue корректно сопоставляет элементы при diffing.

Особые случаи

  • Динамическая генерация компонентов с условием v-if При использовании v-if и v-for одновременно ключи критически важны для правильного рендеринга и сохранения состояния компонентов, особенно если один и тот же компонент может появляться и исчезать:
<child-component v-for="item in items" v-if="item.visible" :key="item.id" />

Тест должен проверять, что после скрытия и повторного отображения компонента ключ остаётся прежним.

  • Списки без ключей Vue выдаёт предупреждение в консоли. Тестирование может включать проверку, что при отсутствии ключа компонент создаётся заново, что демонстрирует неправильное поведение.

Проверка key в Vue Test Utils обеспечивает надёжность тестов компонентов, гарантирует корректную работу с динамическими списками и сохраняет внутреннее состояние компонентов при обновлении данных.