Vue
Условия и списки: v-if, v-show, v-for
Лента уведомлений в почтовом клиенте показывает список писем, скрывает блок 'входящих нет', когда список не пуст, и держит наготове свёрнутую панель фильтров, которую пользователь дёргает десятки раз за сессию. Список рисуется через v-for, пустое состояние - через v-if, а панель, которую часто переключают, - через v-show, потому что её дешевле прятать стилем, чем создавать заново. Неверный выбор между v-if и v-show либо тормозит частые переключения, либо держит в DOM лишние тяжёлые узлы.
- Список товаров или писем через v-for с уникальным key по id для корректного переиспользования узлов
- Пустое состояние 'ничего не найдено' через v-if, когда результат фильтрации пуст
- Часто переключаемая боковая панель или таб через v-show: переключение видимости без пересоздания DOM
- Условный рендер по роли: блок администратора через v-if, чтобы вообще не попасть в DOM обычного пользователя
Предварительные знания
- Шаблонный синтаксис: интерполяция и привязка атрибутов через v-bind
- Понимание реактивного состояния (ref) как источника данных для шаблона
- Базовая работа с массивами и объектами в JavaScript
v-if против v-show
Обе директивы управляют видимостью, но по-разному. v-if - настоящий условный рендеринг: при ложном условии элемент и его дочерние компоненты не создаются вовсе, их нет в DOM. v-show всегда рендерит элемент и переключает только CSS-свойство display. Отсюда правило: v-if дешевле при первом рендере (не строит скрытое), v-show дешевле при частых переключениях (не пересоздаёт).
| Критерий | v-if | v-show |
|---|---|---|
| Элемент в DOM при false | Отсутствует | Присутствует (display:none) |
| Стоимость первого рендера | Низкая, если условие false | Высокая, рендерится всегда |
| Стоимость переключения | Высокая, создание/удаление | Низкая, смена CSS |
| Поддержка v-else | Да | Нет |
Эмпирика выбора: если переключение редкое или ветка тяжёлая и часто не нужна - v-if. Если элемент переключают часто (вкладки, аккордеон, тултип) - v-show, чтобы не платить за пересоздание DOM каждый раз.
Боковая панель фильтров открывается и закрывается десятки раз за сессию. Какая директива уместнее и почему?
v-for: массивы, объекты, диапазоны
v-for перебирает источник и рендерит элемент на каждую запись. Для массива доступны элемент и индекс: v-for="(item, index) in items". Для объекта - значение, ключ и индекс: v-for="(value, key) in obj". Для диапазона v-for="n in 5" даёт числа от 1 до 5, что удобно для рейтинга-звёздочек или пагинации.
Для отрисовки группы элементов по условию без лишней обёртки используют тег template с v-for или v-if: он не создаёт реальный DOM-узел, а служит невидимым контейнером для директивы.
При переборе объекта порядок ключей следует порядку Object.keys и не гарантируется спецификацией для всех типов ключей. Если порядок важен, источником лучше делать массив, где порядок явный.
Как через v-for получить значение и ключ при переборе объекта { name: 'Anna', age: 30 }?
key: зачем и почему не индекс
Атрибут key даёт Vue стабильную идентичность каждого элемента списка. По нему Vue понимает, какой узел переиспользовать, какой создать, а какой удалить при изменении массива. Без стабильного key Vue вынужден угадывать соответствие старых и новых элементов, что приводит к перепутанному состоянию: введённый текст или активный чекбокс остаются не у той строки.
Почему индекс ломается
Стабильный id вместо индекса сохраняет привязку состояния DOM к правильной записи
Список из трёх задач с key по индексу 0,1,2. Пользователь отметил чекбокс у второй задачи, и тут в начало вставляется новая. Теперь индексы сдвинулись: бывшая вторая задача стала третьей, но Vue видит, что под key=1 всё ещё есть элемент, и переиспользует старый DOM с уже стоящей галочкой - галочка оказывается не у той задачи.
Индекс как key допустим только для статичного списка, который никогда не переупорядочивается, не фильтруется и не меняет длину в середине. Как только возможны вставки, удаления или сортировка - нужен стабильный идентификатор записи.
Почему использование индекса массива как key опасно при вставке элемента в начало списка?
Почему v-if и v-for не на одном элементе
Ставить v-if и v-for на один и тот же элемент не рекомендуется. В Vue 3 при таком сочетании v-if имеет приоритет выше v-for и выполняется первым, а значит не видит переменную итерации - условие пытается обратиться к элементу цикла, которого на момент проверки ещё нет. Помимо ошибки, это перепроверяет условие на каждой итерации, что неэффективно.
Предпочтительнее фильтровать список заранее в computed: шаблон остаётся чистым, фильтрация кэшируется и пересчитывается только при изменении источника, а не на каждый рендер.
Почему в Vue 3 не стоит ставить v-if и v-for на один элемент для фильтрации списка?
Связь с другими темами
Условия и списки - ядро шаблонов. Дальше они соединяются с другими директивами:
- Обработка событий — На каждый элемент списка вешают обработчик действия, передавая в него данные строки
- Реактивность изнутри — Корректное обновление списка по key опирается на отслеживание изменений реактивного массива
Итог
- v-if добавляет и удаляет элемент из DOM по условию; ложная ветка не существует в разметке вовсе
- v-show всегда держит элемент в DOM и переключает только CSS display, что дешевле при частых переключениях
- v-for перебирает массивы, объекты и диапазоны; каждому элементу нужен стабильный уникальный key
- key по индексу ломается при вставке и удалении в середине; ключом берут стабильный id записи
- v-if и v-for не ставят на один элемент: их приоритет неоднозначен, перебор оборачивают в template или фильтруют заранее
Связанные уроки
- vue-14-event-handling — v-for и обработка событий часто работают вместе: список элементов с кнопкой действия на каждом
- vue-18-reactivity-deep — key в v-for и отслеживание изменений массива опираются на реактивную систему, разбираемую глубже там