Создание пользовательских директив

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


Регистрация пользовательской директивы

Директиву в Quasar можно зарегистрировать как глобально, так и локально для конкретного компонента. Для глобальной регистрации используется объект конфигурации boot или основной файл приложения quasar.conf.js. Структура регистрации:

import { boot } from 'quasar/wrappers'

export default boot(({ app }) => {
  app.directive('focus', {
    mounted(el) {
      el.focus()
    }
  })
})

Ключевые моменты:

  • mounted(el) — вызывается после добавления элемента в DOM.
  • el — ссылка на DOM-элемент, к которому применяется директива.
  • Для локальной регистрации в компоненте директива передается в объект directives:
export default {
  directives: {
    focus: {
      mounted(el) {
        el.focus()
      }
    }
  }
}

Основные хуки директив

Quasar использует те же жизненные циклы директив, что и Vue 3, с возможностью их расширения:

  • beforeMount(el, binding, vnode, prevVnode) — выполняется до вставки элемента в DOM. Полезно для начальной инициализации.
  • mounted(el, binding, vnode, prevVnode) — после вставки в DOM. Здесь лучше применять визуальные эффекты и фокусировку.
  • beforeUpdate(el, binding, vnode, prevVnode) — до обновления реактивных данных. Можно анализировать изменения значений.
  • updated(el, binding, vnode, prevVnode) — после обновления данных. Используется для корректировки состояния DOM.
  • beforeUnmount(el, binding, vnode, prevVnode) — перед удалением элемента. Здесь можно освобождать ресурсы, события.
  • unmounted(el, binding, vnode, prevVnode) — после удаления элемента. Финальная очистка.

Пример с полным набором хуков:

app.directive('highlight', {
  beforeMount(el, binding) {
    el.style.transition = 'background-color 0.3s'
  },
  mounted(el, binding) {
    el.style.backgroundColor = binding.value || 'yellow'
  },
  updated(el, binding) {
    el.style.backgroundColor = binding.value || 'yellow'
  },
  unmounted(el) {
    el.style.backgroundColor = ''
  }
})

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

Директивы в Quasar поддерживают аргументы и модификаторы, что делает их гибкими:

  • Аргумент (arg) — позволяет передавать дополнительную информацию к директиве. Например:
app.directive('color', {
  mounted(el, binding) {
    el.style.color = binding.arg || 'black'
  }
})

Использование: <p v-color:red>Текст</p>binding.arg вернет 'red'.

  • Модификатор (modifiers) — булев объект с ключами, которые позволяют включать/отключать дополнительные опции:
app.directive('padding', {
  mounted(el, binding) {
    if (binding.modifiers.top) el.style.paddingTop = '10px'
    if (binding.modifiers.bottom) el.style.paddingBottom = '10px'
  }
})

Использование: <div v-padding.top.bottom>Контент</div> — добавляет паддинг сверху и снизу.


Доступ к значениям директивы

binding.value предоставляет возможность передавать динамические данные в директиву:

app.directive('tooltip', {
  mounted(el, binding) {
    const tooltipText = binding.value || 'Подсказка'
    el.setAttribute('title', tooltipText)
  }
})

Использование: <button v-tooltip="'Нажмите сюда'">Кнопка</button>.

Для сложных сценариев binding.value может быть объектом с несколькими свойствами, что упрощает управление поведением:

v-tooltip="{ text: 'Сохранить', color: 'blue' }"

Внутри директивы:

const { text, color } = binding.value
el.setAttribute('title', text)
el.style.backgroundColor = color

Подключение сторонних библиотек

Директивы идеально подходят для интеграции с библиотеками, которые требуют прямого доступа к DOM, например, для масок ввода или drag-and-drop:

import IMask from 'imask'

app.directive('mask', {
  mounted(el, binding) {
    el._mask = IMask(el, { mask: binding.value })
  },
  unmounted(el) {
    if (el._mask) el._mask.destroy()
  }
})

Использование: <input v-mask="'0000-0000-0000-0000'"> — автоматически накладывает маску ввода.


Взаимодействие с событиями

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

app.directive('click-outside', {
  mounted(el, binding) {
    el._handler = event => {
      if (!el.contains(event.target)) {
        binding.value(event)
      }
    }
    document.addEventListener('click', el._handler)
  },
  unmounted(el) {
    document.removeEventListener('click', el._handler)
  }
})

Применение: <div v-click-outside="handleClose">Меню</div> — вызовет handleClose при клике вне элемента.


Советы по производительности

  • Удалять события в unmounted, чтобы избежать утечек памяти.
  • Использовать реактивные объекты в binding.value аккуратно, чтобы не вызывать лишние перерендеры.
  • Для сложных директив лучше использовать инициализацию один раз в mounted и минимальные обновления в updated.

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