Переход с Popper.js v1

Popper.js v2 представляет собой полностью переработанную архитектуру позиционирования элементов. Основные изменения затрагивают систему модификаторов, производительность, API и способы подключения. Версия v1 больше не развивается, что делает переход необходимым для получения исправлений ошибок, улучшенной точности позиционирования и поддержки современных браузеров.

Ключевые улучшения:

  • Модульная система модификаторов
  • Упрощённый и более предсказуемый API
  • Снижение размера библиотеки
  • Улучшенная производительность за счёт оптимизированных вычислений
  • Поддержка tree-shaking

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

В v1 использовался пакет popper.js, в v2 — @popperjs/core.

npm uninstall popper.js
npm install @popperjs/core

Импорт в коде:

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

В v1:

import Popper from 'popper.js';

Изменения в создании экземпляра

Popper.js v1

new Popper(reference, popper, {
  placement: 'top',
});

Popper.js v2

createPopper(reference, popper, {
  placement: 'top',
});

Основное отличие:

  • Используется функция createPopper вместо конструктора
  • Экземпляр больше не создаётся через new

Новая система модификаторов

Структура в v1

modifiers: {
  offset: {
    offset: '0,10'
  }
}

Структура в v2

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

Изменения:

  • Модификаторы теперь массив, а не объект
  • У каждого модификатора есть name и options
  • Улучшена читаемость и расширяемость

Изменение синтаксиса offset

В v1:

offset: '0, 10'

В v2:

offset: [0, 10]

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

offset: ({ placement, reference, popper }) => {
  return [0, 10];
}

Изменение поведения flip

В v1:

modifiers: {
  flip: {
    beh * avior: ['top', 'bottom']
  }
}

В v2:

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

Изменения:

  • behavior заменён на fallbackPlacements
  • Более явная логика переключения позиций

Изменения preventOverflow

В v1:

modifiers: {
  preventOverflow: {
    boundariesElement: 'viewport'
  }
}

В v2:

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

Переименования:

  • boundariesElementboundary

Работа с arrow

В v1:

modifiers: {
  arrow: {
    element: arrowElement
  }
}

В v2:

{
  name: 'arrow',
  options: {
    element: arrowElement,
  },
}

Изменения минимальны, но структура модификаторов унифицирована.


Стратегия позиционирования

В v1 использовалось свойство positionFixed:

positionFixed: true

В v2:

strategy: 'fixed'

Возможные значения:

  • 'absolute' (по умолчанию)
  • 'fixed'

Обновление позиции

В v1:

popper.scheduleUpdate();

В v2:

instance.update();

Или асинхронно:

await instance.update();

Уничтожение экземпляра

В v1:

popper.destroy();

В v2:

instance.destroy();

Метод сохранился, но возвращаемый объект отличается.


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

В v1 использовалось:

data.offsets.popper

В v2:

state.rects.popper

Или:

state.modifiersData

Структура данных изменилась и стала более детализированной.


Пользовательские модификаторы

В v1

modifiers: {
  myModifier: {
    enabled: true,
    fn: function(data) {
      return data;
    }
  }
}

В v2

{
  name: 'myModifier',
  enabled: true,
  phase: 'main',
  fn({ state, options }) {
    return state;
  },
}

Новые концепции:

  • Фазы выполнения (phase)
  • Явная передача state
  • Возможность более точного контроля порядка выполнения

Фазы модификаторов

В v2 введена система фаз:

  • beforeRead
  • read
  • afterRead
  • beforeMain
  • main
  • afterMain
  • beforeWrite
  • write
  • afterWrite

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

  • Контролировать порядок вычислений
  • Избегать лишних перерасчётов
  • Улучшать производительность

Удалённые и изменённые опции

v1 v2
positionFixed strategy
boundariesElement boundary
behavior (flip) fallbackPlacements
gpuAcceleration computeStyles.options

computeStyles и GPU-ускорение

В v1:

gpuAcceleration: false

В v2:

{
  name: 'computeStyles',
  options: {
    gpuAcceleration: false,
  },
}

Теперь это отдельный модификатор.


Изменение applyStyle

В v1 стили применялись автоматически через applyStyle.

В v2 используется модификатор:

{
  name: 'applyStyles',
}

Можно отключить и управлять стилями вручную.


Использование без DOM (Virtual Elements)

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

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

createPopper(virtualElement, popper);

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

  • Работать с курсором мыши
  • Создавать кастомные точки привязки

Производительность и оптимизация

В v2:

  • Минимизированы перерасчёты layout
  • Используются эффективные алгоритмы позиционирования
  • Модификаторы выполняются только при необходимости

Типичные ошибки при миграции

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

v1-объект не работает в v2 — требуется массив.

2. Неправильные имена опций

Например:

  • boundariesElement больше не существует
  • behavior заменён

3. Попытка использовать new Popper

В v2 только createPopper.

4. Игнорирование фаз модификаторов

Неправильная фаза может ломать логику позиционирования.


Пример полной миграции

v1

new Popper(reference, popper, {
  placement: 'bottom',
  modifiers: {
    offset: {
      offset: '0, 8',
    },
    preventOverflow: {
      boundariesElement: 'viewport',
    },
  },
});

v2

createPopper(reference, popper, {
  placement: 'bottom',
  modifiers: [
    {
      name: 'offset',
      options: {
        offset: [0, 8],
      },
    },
    {
      name: 'preventOverflow',
      options: {
        boundary: 'viewport',
      },
    },
  ],
});

Рекомендации по миграции

  • Переходить постепенно, переписывая конфигурацию модификаторов
  • Проверять кастомные модификаторы на соответствие новой архитектуре
  • Использовать документацию v2 для сопоставления опций
  • Удалить устаревшие параметры
  • Тестировать позиционирование во всех сценариях интерфейса