логотип PurpleSchool
логотип PurpleSchool

Использование директив в Vue и их расширенные возможности

Автор

Олег Марков

Введение

Vue.js — это современный фреймворк JavaScript для создания реактивных пользовательских интерфейсов. Одной из ключевых возможностей Vue являются директивы. Директивы позволяют декларативно связывать данные с DOM, изменять поведение элементов, а в некоторых случаях даже вмешиваться в низкоуровневые операции с DOM. В этой статье подробно рассмотрим не только стандартные директивы, но и узнаем о создании кастомных решений, о расширенном взаимодействии с жизненным циклом директив, плюс обратим внимание на best practices и возможные сложности. Я покажу вам, как извлечь максимум из этой мощной функциональности Vue.

Что такое директивы в Vue

Директива в Vue — это специальный атрибут, который добавляет реактивное или особое поведение элементу DOM при его рендеринге. Все встроенные директивы начинаются с префикса v-.

Зачем используются директивы

  • Реагируют на данные и изменяют DOM в зависимости от них.
  • Позволяют повторно использовать логику на компонентах.
  • Упрощают декларативный стиль кода в шаблонах.

Основные стандартные директивы Vue

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

  • v-bind
  • v-model
  • v-if, v-else-if, v-else
  • v-for
  • v-show
  • v-on
  • v-pre, v-cloak, v-once, v-memo

Давайте взглянем на примеры и разберём каждую подробнее.

v-bind

v-bind связывает атрибуты элемента с выражением Vue.

<template>
  <img v-bind:src="imgUrl" v-bind:alt="imgAlt">
</template>

<script>
export default {
  data() {
    return {
      imgUrl: 'logo.png',
      imgAlt: 'Company logo'
    }
  }
}
</script>

// Здесь v-bind:src устанавливает значение imgUrl как источник изображения. // Можно использовать сокращение :src вместо v-bind:src.

v-model

Используется для двусторонней синхронизации данных с input, textarea и select.

<template>
  <input v-model="message">
  <p>Вы ввели: {{ message }}</p>
</template>

<script>
export default {
  data() {
    return {
      message: ''
    }
  }
}
</script>

// Когда пользователь вводит текст, message автоматически обновляется.

v-if / v-else-if / v-else

Для условного рендеринга элементов.

<template>
  <p v-if="isLoggedIn">Добро пожаловать!</p>
  <p v-else>Пожалуйста, войдите в систему.</p>
</template>

<script>
export default {
  data() {
    return {
      isLoggedIn: false
    }
  }
}
</script>

// В зависимости от значения isLoggedIn показывается разный текст.

v-for

Каждый раз, когда вам нужно повторить элемент для элементов массива или объекта, используйте эту директиву.

<template>
  <ul>
    <li v-for="(user, index) in users" :key="user.id">
      {{ index + 1 }}. {{ user.name }}
    </li>
  </ul>
</template>

<script>
export default {
  data() {
    return {
      users: [
        { id: 1, name: "Анна" },
        { id: 2, name: "Иван" }
      ]
    }
  }
}
</script>

// v-for работает с key, чтобы Vue оптимально отслеживал изменения списка.

v-show

Прячет или отображает элемент с помощью CSS-свойства display, не удаляя его из DOM.

<template>
  <button v-show="isVisible">Показать/Скрыть</button>
</template>

// Отличие от v-if — элемент всегда в DOM, просто его невидно.

v-on

Вешает обработчики на события.

<template>
  <button v-on:click="handleClick">Нажми меня</button>
</template>

<script>
export default {
  methods: {
    handleClick() {
      // Реагируем на нажатие кнопки
      alert('Вы нажали на кнопку!')
    }
  }
}
</script>

// Для краткости используйте @click.

v-pre, v-cloak, v-once, v-memo

  • v-pre — пропускает элемент и его потомков при компиляции Vue.
  • v-cloak — используется для предотвращения мерцания некомпилированных шаблонов на этапе загрузки (обычно работает в связке с CSS).
  • v-once — рендерит элемент один раз и не обновляет его при изменении данных.
  • v-memo (Vue 3) — кеширует результат рендера для производительности.

Кастомные директивы в Vue

В дополнение ко встроенным директивам вы можете создавать свои собственные, чтобы расширить функциональность приложения. Кастомные директивы часто используют для интерактивных манипуляций с DOM, которые не подходят для обработки через обычные пропсы или события.

Простейший пример кастомной директивы

Создадим директиву, которая фокусирует input, когда компонент появляется на странице.

Глобальная регистрация

// main.js
import { createApp } from 'vue'
import App from './App.vue'

// Регистрируем директиву глобально
const app = createApp(App)
app.directive('focus', {
  // Вызывается после вставки элемента в DOM
  mounted(el) {
    el.focus()
  }
})

app.mount('#app')
<!-- Используем кастомную директиву -->
<input v-focus>

// Теперь любой input с v-focus автоматически получает фокус.

Локальная регистрация директивы

Если вы хотите, чтобы директива была доступна только в конкретном компоненте:

export default {
  directives: {
    focus: {
      mounted(el) {
        el.focus()
      }
    }
  }
}

// Можно использовать в шаблоне компонента .

Директива с аргументом и модификатором

Vue позволяет указывать аргументы (например, название события или свойства) и модификаторы (например, .once, .prevent).

<!-- Директива с аргументом и модификатором -->
<button v-my-directive:alert.once="message">Click</button>

В директиве это выглядит так:

app.directive('my-directive', {
  mounted(el, binding) {
    // binding.arg — значение аргумента (alert)
    // binding.modifiers — объект модификаторов (например, { once: true })
    if (binding.arg === 'alert') {
      el.addEventListener('click', () => {
        alert(binding.value)
      }, { once: binding.modifiers.once })
    }
  }
})

// Теперь кнопка вызовет alert с message и сделает это только один раз.

Жизненный цикл кастомных директив

У директив есть свои хуки, похожие на жизненный цикл компонентов:

  • created — вызывается при инициализации директивы.
  • beforeMount — перед монтированием элемента.
  • mounted — после монтирования.
  • beforeUpdate — перед обновлением привязанного элемента.
  • updated — после обновления элемента.
  • beforeUnmount — перед удалением.
  • unmounted — после удаления из DOM.

Вот как можно использовать эти хуки:

app.directive('example', {
  created(el, binding, vnode, prevVnode) {
    // Логика при инициализации
  },
  beforeMount(el, binding, vnode, prevVnode) {
    // Перед вставкой в DOM
  },
  mounted(el, binding, vnode, prevVnode) {
    // Уже в DOM
  },
  beforeUpdate(el, binding, vnode, prevVnode) {
    // Перед повторным обновлением
  },
  updated(el, binding, vnode, prevVnode) {
    // После обновления
  },
  beforeUnmount(el, binding, vnode, prevVnode) {
    // Перед размонтированием
  },
  unmounted(el, binding, vnode, prevVnode) {
    // После удаления из DOM
  }
})

// Обычно используют только mounted и unmounted, но для сложных случаев может понадобиться полный цикл.

Пример: Кастомная директива для отслеживания клика вне элемента

Предположим, вам нужно закрывать меню по клику вне его области. Давайте решим эту задачу с помощью директивы.

app.directive('click-outside', {
  mounted(el, binding) {
    el.__vueClickOutside__ = event => {
      // Проверяем, был ли клик вне нашего элемента
      if (!(el === event.target || el.contains(event.target))) {
        binding.value(event) // вызываем функцию, переданную директиве
      }
    }
    document.body.addEventListener('click', el.__vueClickOutside__)
  },
  unmounted(el) {
    // Чистим слушатель при удалении элемента из DOM
    document.body.removeEventListener('click', el.__vueClickOutside__)
    delete el.__vueClickOutside__
  }
})
<template>
  <div v-click-outside="closeMenu">
    <!-- Ваше меню -->
  </div>
</template>

<script>
export default {
  methods: {
    closeMenu() {
      // Логика закрытия меню
    }
  }
}
</script>

// Такой подход позволяет многократно использовать директиву в проекте без дублирования кода.

Пример: Передача параметров через модификаторы

Иногда удобно передавать дополнительные настройки через модификаторы:

<template>
  <button v-tooltip.top="'Вверхний тултип'">Наведи мышку</button>
  <button v-tooltip.bottom="'Нижний тултип'">Наведи мышку</button>
</template>
app.directive('tooltip', {
  mounted(el, binding) {
    const position = binding.modifiers.top ? 'top' :
      binding.modifiers.bottom ? 'bottom' : 'right'
    el.addEventListener('mouseenter', () => {
      // Показать тултип в нужном положении
      showTooltip(el, binding.value, position)
    })
    el.addEventListener('mouseleave', () => {
      hideTooltip(el)
    })
  },
  unmounted(el) {
    // Чистим все слушатели и тултипы, если нужно
  }
})

// Такой способ добавляет гибкости для переиспользуемых решений.

Расширенные возможности и best practices работы с директивами

Когда использовать директивы

  • Если вам нужно напрямую манипулировать DOM-элементом.
  • Когда требуется повторное использование точки расширения в шаблоне.
  • Когда не хватает стандартных событий или реактивностей компонентов.

Best practices

  • Всегда чистите внешние ресурсы и слушателей событий в хуке unmounted.
  • Не используйте директивы для логики, которую можно выразить обычными props, events или computed.
  • Если ваша логика слишком сложна, подумайте о создании отдельного компонента, а не директивы.
  • Используйте директивы для компактной интеграции сторонних библиотек, например, для автофокуса, drag’n’drop, кастомных тултипов и т.д.
  • Всегда старайтесь избегать сайд-эффектов в директивах.

Пример: Интеграция стороннего плагина через директиву

Допустим, вам нужно сделать маску ввода для телефона. Используйте библиотеку, которую удобно подключить через директиву.

import Inputmask from "inputmask"

app.directive('inputmask', {
  mounted(el, binding) {
    Inputmask(binding.value).mask(el)
  },
  unmounted(el) {
    Inputmask.remove(el)
  }
})
<template>
  <input v-inputmask="'+7 (999) 999-99-99'" type="text">
</template>

// Такой подход позволяет повторно использовать директиву для разных инпутов без дублирования кода.

Кратко про SSR

Некоторые директивы не будут корректно работать на серверном рендеринге (SSR), если они зависят от DOM API. Например, фокусировка, интеграция сторонних плагинов — такие действия корректно срабатывают только на этапе mounted, когда DOM уже доступен.

Работа с Typescript

Если используете TypeScript, желательно расширять типы для кастомных директив, чтобы улучшить автодополнение и избежать ошибок во время разработки. Для более сложных директив можно объявлять типы из @vue/runtime-core или создать отдельные интерфейсы.

Заключение

Директивы — это мощный инструмент для непосредственного управления поведением DOM в Vue-приложениях. Встроенные директивы закрывают большинство рутинных задач: работа с атрибутами, событиями, условным или циклическим выводом. Однако кастомные директивы открывают новые возможности управления элементами, упрощая интеграцию сторонних библиотек и реализацию типовых UI-паттернов, таких как клик вне элемента или маска ввода.

Использование директив требует понимания жизненного цикла и правильного управления ресурсами. Старайтесь следовать best practice, не использовать директивы вместо компонентной логики, но не бойтесь применять их для расширения стандартных возможностей Vue. Кастомные директивы остаются отличным способом минимизировать дублирование кода и повысить переиспользуемость решений.

Частозадаваемые технические вопросы по теме

Как передать несколько параметров в директиву?

В параметр директивы можно передавать объект: vue <input v-my-directive="{ value: someValue, option: flag }"> Внутри директивы получаете это через binding.value.

Как обращаться к данным компонента из кастомной директивы?

В директиве 4-го параметра vnode есть ссылка на инстанс компонента (vnode.context в Vue 2, в Vue 3 — используйте emit или provide/inject либо прокидывайте необходимые данные через prop директивы).

Как использовать директиву во вложенных компонентах?

Локально зарегистрированная директива доступна только в том компоненте, где она объявлена. Чтобы применять во всех компонентах, регистрируйте директиву глобально через экземпляр приложения.

Как протестировать кастомные директивы?

Используйте e2e или unit-тесты с Jest и @vue/test-utils. Можно смонтировать компонент с директивой и эмулировать события или изменения binding.value, далее проверять эффект над DOM.

Можно ли использовать директиву с компонентами, а не только с DOM-элементами?

В большинстве случаев директивы применимы только к обычным HTML-элементам. В Vue 3 директивы нельзя навешивать на компоненты — только на raw DOM (исключение: если обертка компонента содержит single root DOM-элемент, можно попасть в этот элемент через ref или slot).

Стрелочка влевоРуководство по div во VueОсновы и применение директив в VueСтрелочка вправо

Все гайды по Vue

Руководство по валидации форм во Vue.jsИнтеграция Tiptap для создания редакторов на VueРабота с таблицами во Vue через TanStackИнструкция по установке и компонентам Vue sliderУправление пакетами Vue js с помощью npmУправление пакетами и node modules в Vue проектахКак использовать meta для улучшения SEO на VueПолный гайд по компоненту messages во Vuejs5 правил использования Inertia с Vue и LaravelРабота с модулями и пакетами в VueИнструкция по работе с grid на VueGithub для Vue проектов - подробная инструкция по хранению и совместной работеНастройка ESLint для Vue проектов и поддержка качества кодаОбработка ошибок и отладка в Vue.jsИспользование Vue Devtools для отладки и мониторинга приложенийРабота с конфигурационными файлами и скриптами VueСоздание и настройка проектов Vue с помощью Vue CLI3 способа интеграции Chart.js с Vue для создания графиковРабота с Canvas во VueИнструкция по реализации календаря во VueРабота с Ant Design Vue для создания UI на Vue
Обзор и использование утилит Vue для удобной разработкиРабота с обновлениями компонента и жизненным циклом updateРазрешение конфликтов и ошибок с помощью Vue resolveИспользование query-параметров и их обработка в маршрутах VueЗагрузка и управление состоянием загрузки в VueИспользование библиотек Vue для расширения функционалаРабота с JSON данными в приложениях VueКак работать с экземплярами компонента Instance во VueПолучение данных и API-запросы во Vue.jsЭкспорт и импорт данных и компонентов в VueОбработка событий и их передача между компонентами VuejsГайд по defineEmits на Vue 3Понимание core функционала Vue и его применениеПонимание и применение Composition API в Vue 3Понимание и работа с компилятором VueКогда и как использовать $emit и call во VueВзаимодействие с внешними API через Axios в Vue
Веб приложения на Vue архитектура и лучшие практикиИспользование Vite для быстрого старта и сборки проектов на Vue 3Работа с URL и ссылками в приложениях на VueРабота с пользовательскими интерфейсами и UI библиотеками во VueОрганизация и структура исходных файлов в проектах VueИспользование Quasar Framework для разработки на Vue с готовыми UI-компонентамиОбзор популярных шаблонов и стартовых проектов на VueИнтеграция Vue с PHP для создания динамичных веб-приложенийКак организовать страницы и маршруты в проекте на VueNuxt JS и Vue 3 для SSR приложенийСоздание серверных приложений на Vue с помощью Nuxt jsИспользование Vue Native для разработки мобильных приложенийОрганизация и управление индексной страницей в проектах VueИспользование Docker для контейнеризации приложений на VueИнтеграция Vue.js с Django для создания полноценных веб-приложенийСоздание и работа с дистрибутивом build dist Vue приложенийРабота со стилями и CSS в Vue js для красивых интерфейсовСоздание и структурирование Vue.js приложенияКак исправить ошибку cannot find module vueНастройка и сборка проектов Vue с использованием современных инструментовИнтеграция Vue с Bitrix для корпоративных решенийРазработка административных панелей на Vue js
5 библиотек для создания tree view во VueИнтеграция Tailwind CSS с Vue для современных интерфейсовИнтеграция Vue с серверной частью и HTTPS настройкамиКак обрабатывать async операции с Promise во VueИнтеграция Node.js и Vue.js для разработки приложенийРуководство по интеграции Vue js в NET проектыПримеры использования JSX во VueГайд по импорту и регистрации компонентов на VueМногоязычные приложения на Vue с i18nИнтеграция FLIR данных с Vue5 примеров использования filter во Vue для упрощения разработки3 примера реализации drag-and-drop во Vue
Управление переменными и реактивными свойствами во VueИспользование v for и slot в VueПрименение v-bind для динамической привязки атрибутов в VueУправление пользователями и их данными в Vue приложенияхСоздание и использование UI Kit для Vue приложенийТипизация и использование TypeScript в VuejsИспользование шаблонов в Vue js для построения интерфейсовИспользование Swiper для создания слайдеров в VueРабота со стилями и стилизацией в VueСтруктура и особенности Single File Components SFC в VueРабота со SCSS в проектах на Vue для стилизацииРабота со скроллингом и прокруткой в Vue приложенияхПрименение script setup синтаксиса в Vue 3 для упрощения компонентовИспользование scoped стилей для изоляции CSS в компонентах Vue3 способа улучшить навигацию Vue с push()Обработка запросов и асинхронных операций в VueПонимание и использование provide inject для передачи данных между компонентамиПередача и использование props в Vue 3 для взаимодействия компонентовПередача данных между компонентами с помощью props в Vue jsУправление property и функциями во Vue.jsРабота со свойствами компонентов VueУправление параметрами и динамическими данными во VueРабота с lifecycle-хуком onMounted во VueОсновы работы с объектами в VueПонимание жизненного цикла компонента Vue js на примере mountedИспользование модальных окон modal в Vue приложенияхИспользование методов в компонентах Vue для обработки логикиИспользование метода map в Vue для обработки массивовИспользование хуков жизненного цикла Vue для управления состоянием компонентаРабота с ключами key в списках и компонентах VueОбработка пользовательского ввода в Vue.jsРабота с изображениями и их оптимизация в VueИспользование хуков жизненного цикла в VueОрганизация сеток и гридов для верстки интерфейсов на VueСоздание и управление формами в VueОрганизация файлов и структура проекта Vue.jsКомпоненты Vue создание передача данных события и emitРабота с динамическими компонентами и данными в Vue3 способа манипулирования DOM на VueРуководство по div во VueИспользование директив в Vue и их расширенные возможностиОсновы и применение директив в VueИспользование директив и их особенности на Vue с помощью defineИспользование компонентов datepicker в Vue для выбора датОрганизация циклов и итераций во VueКак работает компиляция Vue CoreСоздание и использование компонентов в Vue JSОбработка кликов и пользовательских событий в VueИспользование классов в Vue для организации кода и компонентовИспользование директивы checked для управления состоянием чекбоксов в VueГайд на checkbox компонент во VueОтображение данных в виде графиков с помощью Vue ChartСоздание и настройка кнопок в VueСоздание и настройка кнопок в Vue приложенияхРабота с lifecycle-хуками beforeCreate и beforeMount во VueИспользование массивов и методов их обработки в VueИспользование массивов и их обработка в Vue
Использование Vuetify для создания современных интерфейсов на VueИспользование transition во VueТестирование компонентов и приложений на VueРабота с teleport для управления DOM во VueПять шагов по настройке SSR в VuejsИспользование Shadcn UI компонентов с Vue для продвинутых интерфейсовИспользование router-link для навигации в Vue RouterКак использовать require в Vue для динамического импорта модулейРабота с динамическим рендерингом и виртуальным DOM на Vue.jsИспользование ref для управления ссылками и реактивностью в Vue 3Использование Vue Pro и его преимущества для профессиональной разработкиРуководство по nextTick для работы с DOMСоздание и использование компонентов с помощью Vue js и CУправление состоянием и реактивностью через inject и provideДинамическое обновление компонентов и данных на VueГлубокое изучение документации Vue и как эффективно её использоватьИспользование Crystal с Vue для разработкиИспользование вычисляемых свойств для динамического отображения данных на Vue jsОптимизация производительности и предупреждения в Vue
Открыть базу знаний