Изменения в API

Переход от ранних версий Popper.js к современному API (начиная с версии 2) сопровождается серьёзной переработкой архитектуры. Основной акцент сделан на модульности, расширяемости и предсказуемости поведения.

Вместо монолитного подхода используется система модификаторов (modifiers) — независимых функций, которые управляют позицией, стилями и поведением всплывающих элементов. Это позволяет гибко конфигурировать Popper и подключать только необходимые части.

Ключевое отличие:

  • Ранее: конфигурация через единый объект с набором опций
  • Сейчас: конфигурация через массив модификаторов с чётко определёнными фазами выполнения

Создание экземпляра

В новой версии API основной способ создания Popper — функция createPopper:

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

const popperInstance = createPopper(referenceElement, popperElement, {
  placement: 'bottom',
});

Изменения:

  • Убраны конструкторы через new Popper(...)
  • Используется именованный импорт
  • Возвращается объект с методами управления

Методы экземпляра:

  • update() — пересчёт позиции
  • forceUpdate() — принудительное обновление без ожидания
  • destroy() — уничтожение экземпляра

Переход к системе модификаторов

Модификаторы — центральная часть нового API. Каждый модификатор описывается объектом:

{
  name: 'offset',
  options: {
    offset: [0, 10],
  },
}

Основные свойства модификатора:

  • name — уникальное имя
  • enabled — включён/выключен
  • phase — стадия выполнения
  • fn — функция обработки
  • options — настройки
  • requires — зависимости от других модификаторов

Фазы выполнения модификаторов

Введена строгая последовательность фаз:

  1. beforeRead
  2. read
  3. afterRead
  4. beforeMain
  5. main
  6. afterMain
  7. beforeWrite
  8. write
  9. afterWrite

Это позволяет:

  • избегать конфликтов между модификаторами
  • оптимизировать доступ к DOM
  • минимизировать reflow и repaint

Пример:

const customModifier = {
  name: 'custom',
  phase: 'main',
  fn({ state }) {
    // логика изменения состояния
  },
};

Изменения в работе с позиционированием

Свойство placement осталось, но стало более строгим и предсказуемым:

placement: 'top-start'

Допустимые значения:

  • top, bottom, left, right
  • вариации: -start, -end

Изменения:

  • Убраны устаревшие значения
  • Улучшена логика fallback-позиционирования
  • Более точная работа с границами контейнера

Модификатор flip

Ранее логика переворота (flip) была встроена. Теперь это отдельный модификатор:

{
  name: 'flip',
  options: {
    fallbackPlacements: ['top', 'right'],
  },
}

Особенности:

  • Полный контроль над альтернативными позициями
  • Возможность отключения
  • Гибкая настройка стратегии

Модификатор preventOverflow

Отвечает за предотвращение выхода элемента за границы:

{
  name: 'preventOverflow',
  options: {
    boundary: 'viewport',
  },
}

Изменения:

  • Поддержка разных границ (viewport, clippingParents, DOM-элемент)
  • Более точный расчёт переполнения
  • Улучшенная работа с вложенными контейнерами

Смещения через offset

Ранее использовались числовые значения, теперь — массив:

{
  name: 'offset',
  options: {
    offset: [skidding, distance],
  },
}

Где:

  • skidding — смещение по основной оси
  • distance — расстояние от reference-элемента

Также поддерживаются функции:

offset: ({ placement }) => {
  return placement === 'top' ? [0, 20] : [0, 10];
}

Работа со стилями

Вместо прямого управления стилями введён модификатор computeStyles:

{
  name: 'computeStyles',
  options: {
    adaptive: true,
    gpuAcceleration: true,
  },
}

И модификатор applyStyles:

{
  name: 'applyStyles',
}

Разделение ответственности:

  • computeStyles — вычисляет стили
  • applyStyles — применяет их к DOM

Это даёт возможность:

  • полностью отключить автоматическое применение
  • реализовать собственный рендеринг

Виртуальные элементы

Добавлена поддержка виртуальных reference-элементов:

const virtualElement = {
  getBoundingClientRect: () => ({
    width: 0,
    height: 0,
    top: 100,
    left: 100,
    right: 100,
    bottom: 100,
  }),
};

Использование:

createPopper(virtualElement, popperElement);

Применение:

  • курсор мыши
  • canvas
  • WebGL
  • кастомные координаты

Управление обновлениями

Новая модель обновлений:

popperInstance.update();

Особенности:

  • асинхронный пересчёт
  • оптимизация через batching
  • уменьшение количества layout операций

Принудительное обновление:

popperInstance.forceUpdate();

Удаление устаревших опций

Из API удалены или переработаны:

  • gpuAcceleration (перенесён в computeStyles)
  • boundariesElement (заменён на boundary)
  • modifiers в старом формате
  • eventsEnabled (заменён на модификаторы событий)

События и слушатели

Теперь обработка событий реализуется через модификатор eventListeners:

{
  name: 'eventListeners',
  options: {
    scroll: true,
    resize: true,
  },
}

Изменения:

  • централизованное управление
  • возможность отключения
  • оптимизация производительности

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

Новый API позволяет легко создавать собственные модификаторы:

const myModifier = {
  name: 'myModifier',
  enabled: true,
  phase: 'main',
  requires: ['offset'],
  fn({ state }) {
    state.styles.popper.backgroundColor = 'red';
  },
};

Подключение:

createPopper(reference, popper, {
  modifiers: [myModifier],
});

Состояние (state)

Внутреннее состояние Popper стало доступным и структурированным:

state = {
  placement,
  elements,
  rects,
  modifiersData,
  styles,
  attributes,
};

Это даёт:

  • полный контроль над вычислениями
  • возможность глубокой кастомизации
  • доступ к промежуточным данным

Улучшения производительности

Ключевые изменения:

  • минимизация DOM-доступа
  • батчинг операций
  • строгая последовательность фаз
  • отключаемые модификаторы

Результат:

  • меньше reflow
  • стабильная работа в сложных интерфейсах
  • высокая масштабируемость

Совместимость и миграция

Основные шаги при переходе:

  1. Замена new Popper на createPopper
  2. Переписывание конфигурации в формат модификаторов
  3. Обновление опций позиционирования
  4. Проверка кастомных расширений

Типичный пример миграции:

Было:

new Popper(reference, popper, {
  modifiers: {
    offset: { offset: '0,10' },
  },
});

Стало:

createPopper(reference, popper, {
  modifiers: [
    {
      name: 'offset',
      options: {
        offset: [0, 10],
      },
    },
  ],
});

Итоговые особенности нового API

  • модульная архитектура
  • декларативная настройка
  • строгий жизненный цикл
  • расширяемость через модификаторы
  • улучшенная производительность
  • поддержка виртуальных элементов
  • разделение вычислений и применения стилей