Реактивность и шаблоны — Примеры
Пример 1 — Отфильтрованный список, сохранённый в состоянии
Поле поиска фильтрует список, а отфильтрованный массив записывает в ref наблюдатель. Иногда после очистки поля результаты остаются на экране. Скажите, зачем производное значение вообще хранится, а затем выведите его вместо этого.
<script setup>
import { ref, watch } from "vue";
const items = ref([{ id: 1, name: "Настольная лампа" }]);
const query = ref("");
const visible = ref([]);
watch([items, query], () => {
visible.value = items.value.filter((item) => item.name.includes(query.value));
}, { immediate: true });
</script>
Решение
<script setup>
import { ref, computed } from "vue";
const items = ref([{ id: 1, name: "Настольная лампа" }]);
const query = ref("");
const visible = computed(() =>
items.value.filter((item) => item.name.includes(query.value)),
);
</script>
Второй ref, в который пишет наблюдатель, создаёт для одного списка два источника истины. Разойтись они могут, и устаревший отфильтрованный массив — ровно такой случай; вдобавок нужен флаг immediate, иначе на первом рендере результат вообще неверен. computed, наоборот, выводит значение из состояния, от которого зависит, и пересчитывается только при изменении одной из этих зависимостей, поэтому отстать не может. Шаблон остаётся тонким, у логики фильтрации одно место; к тому же это чистая функция реактивного состояния, и её можно тестировать отдельно. А вот сохранение в хранилище или логирование — побочные эффекты, и для них нужен именно watch.
Пример 2 — Список с ключом по индексу
Ключ у строк редактируемого списка задан по позиции. Стоит удалить первую строку, и введённые заметки остаются при не тех элементах. Объясните, что ключ сообщает фреймворку, и задайте его по чему-то стабильному.
<template>
<ul>
<li v-for="(item, index) in items" :key="index">
<input v-model="item.note" />
{{ item.name }}
</li>
</ul>
</template>
Решение
<template>
<ul>
<li v-for="item in items" :key="item.id">
<input v-model="item.note" />
{{ item.name }}
</li>
</ul>
</template>
Индекс сообщает фреймворку не что иное, как «то, что стоит на первой позиции», тогда как ключом он сопоставляет отрисованный элемент данным, из которых тот появился: после удаления или переупорядочивания на первой позиции оказывается другой элемент, и фреймворк переиспользует уже стоявшее там поле ввода вместе с введённым в него текстом. С ключом по стабильному id элемент следует за своими данными, поэтому удаляется именно нужная строка, а локальное состояние всех остальных остаётся у своих владельцев. Когда список может меняться или переупорядочиваться, берите для ключа идентичность из предметной области и никогда — позицию в массиве. v-if и v-show — здесь отдельный выбор: один создаёт и уничтожает поддерево, другой лишь скрывает его.