Vue интеграция

Popper.js предоставляет мощный и гибкий механизм позиционирования всплывающих элементов относительно других элементов DOM. При работе с Vue важно учитывать реактивную природу компонентов, чтобы позиционирование оставалось корректным при изменении состояния приложения.


Установка и подключение

Для использования Popper.js в Vue-проектах рекомендуется использовать современный пакет @popperjs/core. Установка производится через npm или yarn:

npm install @popperjs/core
# или
yarn add @popperjs/core

Импорт в компоненте Vue:

import { createPopper } from '@popperjs/core';

Базовое использование в компоненте

В шаблоне Vue создаются два элемента: reference (относительно которого позиционируется поппер) и popper (сам всплывающий элемент).

<template>
  <div ref="referenceElement">Кнопка</div>
  <div ref="popperElement" class="popper">Всплывающее окно</div>
</template>

<script>
import { createPopper } from '@popperjs/core';

export default {
  mounted() {
    this.popperInstance = createPopper(this.$refs.referenceElement, this.$refs.popperElement, {
      placement: 'bottom-start',
    });
  },
  beforeUnmount() {
    if (this.popperInstance) {
      this.popperInstance.destroy();
    }
  }
}
</script>

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

  • ref используется для получения DOM-элементов в Vue.
  • placement задаёт начальное положение поппера: 'top', 'bottom', 'left', 'right' и их вариации (-start, -end).
  • В beforeUnmount необходимо уничтожить экземпляр Popper, чтобы избежать утечек памяти.

Обновление позиции при изменениях

Vue активно обновляет DOM, поэтому часто требуется обновление позиции поппера после изменения контента или состояния.

methods: {
  updatePopper() {
    if (this.popperInstance) {
      this.popperInstance.update();
    }
  }
}

Можно вызывать updatePopper после изменения состояния или контента динамически, либо использовать nextTick для ожидания завершения рендера:

this.$nextTick(() => {
  this.updatePopper();
});

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

Popper.js поддерживает модификаторы, которые расширяют функциональность:

  • offset – смещение поппера относительно reference
  • flip – автоматическое изменение позиции при нехватке пространства
  • preventOverflow – предотвращает выход поппера за пределы контейнера
  • arrow – позиционирование стрелки

Пример:

this.popperInstance = createPopper(this.$refs.referenceElement, this.$refs.popperElement, {
  placement: 'bottom',
  modifiers: [
    { name: 'offset', options: { offset: [0, 10] } },
    { name: 'flip', options: { fallbackPlacements: ['top', 'right'] } },
    { name: 'preventOverflow', options: { boundary: 'viewport' } },
    { name: 'arrow', options: { element: this.$refs.arrowElement } }
  ],
});

Особенности модификаторов в Vue:

  • Для реактивного использования динамических значений можно хранить параметры модификаторов в data и пересоздавать Popper при изменении значений.
  • Arrow элемент нужно добавлять в шаблон и связывать через ref.

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

Если необходимо, чтобы позиция поппера менялась динамически в зависимости от состояния Vue, можно использовать watch:

watch: {
  tooltipVisible(newValue) {
    this.$nextTick(() => {
      if (newValue) this.popperInstance.update();
    });
  }
}

Это гарантирует корректное позиционирование при появлении/скрытии элементов.


Взаимодействие с Vue Transition

При анимации появления поппера через <transition> важно учитывать, что Popper.js рассчитывает позицию только по завершении рендера элемента. Рекомендуется использовать onAfterEnter хука transition:

<transition @after-enter="updatePopper">
  <div v-if="tooltipVisible" ref="popperElement" class="popper">Всплывающее окно</div>
</transition>

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

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

methods: {
  showPopper() {
    if (!this.popperInstance) {
      this.popperInstance = createPopper(this.$refs.referenceElement, this.$refs.popperElement);
    }
  },
  hidePopper() {
    if (this.popperInstance) {
      this.popperInstance.destroy();
      this.popperInstance = null;
    }
  }
}

Это экономит ресурсы и предотвращает накопление ненужных экземпляров.


Интеграция с Composition API

В Vue 3 с Composition API Popper.js интегрируется через ref и onMounted/onBeforeUnmount:

import { ref, onMounted, onBeforeUnmount } from 'vue';
import { createPopper } from '@popperjs/core';

export default {
  setup() {
    const referenceElement = ref(null);
    const popperElement = ref(null);
    let popperInstance = null;

    onMounted(() => {
      popperInstance = createPopper(referenceElement.value, popperElement.value, {
        placement: 'bottom',
      });
    });

    onBeforeUnmount(() => {
      if (popperInstance) popperInstance.destroy();
    });

    return { referenceElement, popperElement };
  }
}

Преимущества Composition API:

  • Чёткое управление жизненным циклом поппера
  • Возможность вынесения логики в отдельные composables
  • Лучшая интеграция с реактивностью Vue

Использование с динамическим контентом

При работе с динамическим контентом важно учитывать, что Popper.js вычисляет размеры и позиции элементов в момент инициализации. Для корректного позиционирования после изменения контента необходимо вызывать update() или пересоздавать экземпляр Popper.

watch(content, () => {
  this.$nextTick(() => {
    if (this.popperInstance) this.popperInstance.update();
  });
});

Popper.js в сочетании с Vue обеспечивает гибкое, реактивное и надёжное позиционирование всплывающих элементов, полностью совместимое с компонентной архитектурой. Использование модификаторов, реактивных параметров и жизненного цикла Vue позволяет создавать сложные интерфейсы с минимальными усилиями.