← Курс/v-if, v-else-if, v-else и v-show#207 из 257+20 XP

v-if, v-else-if, v-else и v-show

Условный рендеринг с v-if

Директива v-if полностью **добавляет или удаляет элемент из DOM** в зависимости от условия. Когда условие ложно, элемент не существует в DOM вообще.

<template>
  <div v-if="isLoggedIn">
    <p>Добро пожаловать, {{ username }}!</p>
    <button @click="logout">Выйти</button>
  </div>
</template>

<script setup>
const isLoggedIn = ref(false)
const username = ref('Алексей')
</script>

Цепочка v-if / v-else-if / v-else

Работает аналогично обычным условным операторам JavaScript:

<template>
  <p v-if="score >= 90">Отлично!</p>
  <p v-else-if="score >= 70">Хорошо</p>
  <p v-else-if="score >= 50">Удовлетворительно</p>
  <p v-else">Нужно подтянуть знания</p>
</template>

Важно: v-else-if и v-else должны идти **сразу после** элемента с v-if — без других элементов между ними.

v-if на элементе template

Если нужно условно скрыть группу элементов без обёртки в лишний <div>, используется тег <template> — он не рендерится в DOM:

<template v-if="isAdmin">
  <h2>Панель администратора</h2>
  <p>Здесь управление пользователями</p>
  <AdminTable />
</template>

v-show: скрытие через CSS

Директива v-show работает иначе: элемент **всегда находится в DOM**, но при ложном условии к нему применяется display: none:

<template>
  <div v-show="isVisible">
    Этот блок скрыт через display: none
  </div>
</template>

<script setup>
const isVisible = ref(true)
</script>

Сравнение v-if и v-show

| Характеристика | v-if | v-show |

|---|---|---|

| Элемент в DOM при false | Нет (удалён) | Да (display: none) |

| Стоимость переключения | Высокая (пересоздание) | Низкая (CSS) |

| Стоимость первого рендера | Низкая (если false) | Высокая (всегда рендерится) |

| Поддержка v-else | Да | Нет |

| Поддержка <template> | Да | Нет |

**Когда использовать v-show:** когда элемент часто переключается (например, выпадающее меню, модальное окно которое открывают/закрывают много раз).

**Когда использовать v-if:** когда условие редко меняется, или при false рендеринг элемента не нужен вообще (например, панель администратора, которую видит лишь малый процент пользователей).

Важная деталь: v-if ленив

Если v-if изначально равно false, Vue вообще не рендерит элемент — не создаёт компонент, не запускает его хуки жизненного цикла. Это важно при работе с тяжёлыми компонентами:

<!-- HeavyChart создастся только когда showChart = true -->
<HeavyChart v-if="showChart" :data="chartData" />

С v-show компонент создастся при первом рендере страницы, даже если он скрыт.

Примеры

Эмуляция v-if и v-show: разница между удалением из DOM и скрытием через display:none

// Сравниваем поведение v-if (удаление из DOM) и v-show (display: none)

// Псевдо-DOM для демонстрации
class PseudoDOM {
  constructor() {
    this.elements = new Map()
    this.parent = { children: [] }
  }

  createElement(tag, id) {
    const el = { tag, id, style: {}, inDOM: false }
    this.elements.set(id, el)
    return el
  }

  // v-if: добавляем/удаляем элемент из DOM
  vIf(el, condition) {
    if (condition && !el.inDOM) {
      this.parent.children.push(el)
      el.inDOM = true
      console.log(`[v-if] <${el.tag}#${el.id}> ДОБАВЛЕН в DOM`)
    } else if (!condition && el.inDOM) {
      this.parent.children = this.parent.children.filter(c => c !== el)
      el.inDOM = false
      console.log(`[v-if] <${el.tag}#${el.id}> УДАЛЁН из DOM`)
    } else {
      console.log(`[v-if] <${el.tag}#${el.id}> без изменений (condition=${condition})`)
    }
  }

  // v-show: меняем только display
  vShow(el, condition) {
    if (!el.inDOM) {
      this.parent.children.push(el)
      el.inDOM = true
    }
    el.style.display = condition ? '' : 'none'
    console.log(`[v-show] <${el.tag}#${el.id}> display="${el.style.display || 'block'}" (в DOM: да)`)
  }

  printDOM() {
    console.log('\nТекущее состояние DOM:')
    if (this.parent.children.length === 0) {
      console.log('  (пусто)')
    }
    this.parent.children.forEach(el => {
      const display = el.style.display === 'none' ? ' [скрыт: display:none]' : ''
      console.log(`  <${el.tag} id="${el.id}"${display}>`)
    })
  }
}

const dom = new PseudoDOM()
const panel = dom.createElement('div', 'admin-panel')
const menu = dom.createElement('nav', 'dropdown-menu')

console.log('=== v-if: переключаем панель администратора ===')
dom.vIf(panel, false)  // изначально скрыт — в DOM нет вообще
dom.printDOM()

dom.vIf(panel, true)   // пользователь вошёл как админ
dom.printDOM()

dom.vIf(panel, false)  // вышел из аккаунта
dom.printDOM()

console.log('\n=== v-show: переключаем выпадающее меню ===')
dom.vShow(menu, false) // закрыто, но в DOM присутствует
dom.printDOM()

dom.vShow(menu, true)  // открыто
dom.printDOM()

dom.vShow(menu, false) // снова закрыто — просто display:none
dom.printDOM()

console.log('\n=== Вывод ===')
console.log('v-if: дорогое создание/удаление, идеально для редко меняющихся условий')
console.log('v-show: дешёвое переключение, идеально для частых переключений')