Модификаторы в Popper.js представляют собой функции, которые изменяют поведение и расположение всплывающих элементов относительно их привязанных reference-элементов. Они работают как плагины, влияя на вычисление позиции, размер, смещение и визуальные свойства поппера. Каждый модификатор имеет структуру:
{
name: 'название',
enabled: true, // включен ли модификатор
phase: 'phase', // этап выполнения (read, main, write)
fn: ({ state, options }) => { ... }, // функция модификатора
requires: ['другие_модификаторы'], // зависимости
options: {} // дополнительные настройки
}
Фазы модификаторов:
read — чтение размеров и позиции элементов.main — вычисление смещения и коррекция положения.write — применение стилей и отрисовка на странице.Правильная организация модификаторов и их фаз критически важна для оптимизации производительности.
Popper.js выполняет модификаторы каждый раз при обновлении поппера. Чтобы уменьшить нагрузку:
Отключение ненужных модификаторов Если
модификатор не используется, enabled: false предотвращает
его вызов, что экономит ресурсы.
Использование requires Указание
зависимостей гарантирует, что модификатор выполняется только после
необходимых вычислений других модификаторов. Например,
offset часто зависит от
computeStyles.
Фильтрация вычислений по фазам Модификаторы, не
влияющие на отрисовку, можно перенести в фазу read, чтобы
они не блокировали основное вычисление позиции.
computeStyles для ускоренияcomputeStyles отвечает за установку CSS-свойств
top, left, transform. Его
оптимизация включает:
gpuAcceleration: true позволяет применять
translate3d, минимизируя перерисовку.offset, отключение некоторых внутренних
вычислений computeStyles ускоряет рендеринг.const popperInstance = Popper.createPopper(reference, popper, {
modifiers: [
{
name: 'computeStyles',
options: {
gpuAcceleration: true,
adaptive: true
}
}
]
});
flipМодификатор flip автоматически меняет сторону поппера,
если текущая позиция выходит за границы видимой области. Оптимизация
включает:
Уменьшение количества проверяемых направлений
Параметр fallbackPlacements задаёт ограниченный список
альтернатив вместо проверки всех вариантов.
Использование boundary Ограничение
области проверки (viewport или конкретный контейнер)
снижает количество дорогостоящих вычислений.
{
name: 'flip',
options: {
fallbackPlacements: ['top', 'bottom'],
boundary: document.querySelector('#container')
}
}
flip при фиксированных
позициях Если известно, что поппер всегда помещается,
enabled: false экономит цикл рендеринга.offsetМодификатор offset позволяет задавать смещение по оси X
и Y. Правильное использование снижает нагрузку на
computeStyles:
{
name: 'offset',
options: {
offset: [0, 8] // 0px по горизонтали, 8px по вертикали
}
}
Создание поппера только при необходимости снижает нагрузку на DOM:
let popperInstance = null;
function showPopper() {
if (!popperInstance) {
popperInstance = Popper.createPopper(reference, popper, { ... });
}
popperInstance.update();
}
destroy
освобождает память и отключает слушатели событий.Для динамических интерфейсов важна оптимизация обновлений:
Ручной вызов update() только при изменении
позиции Автообновление через
popperInstance.forceUpdate() при каждом событии
scroll/mutation может сильно нагрузить страницу.
Использование requestAnimationFrame
Обновление позиции поппера в RAF уменьшает количество лишних
перерисовок:
function scheduleUpdate() {
requestAnimationFrame(() => {
popperInstance.update();
});
}
Создание своих модификаторов позволяет:
Пример кастомного модификатора, ограничивающего пересчёт позиции при минимальных изменениях:
{
name: 'limitedUpdate',
enabled: true,
phase: 'main',
fn({ state }) {
const dx = Math.abs(state.rects.reference.width - state.modifiersData.reference.width);
const dy = Math.abs(state.rects.reference.height - state.modifiersData.reference.height);
if (dx < 2 && dy < 2) return; // не обновлять при незначительных изменениях
}
}
Popper.js автоматически кеширует результаты некоторых вычислений, но дополнительные меры ускорения:
Кэширование размеров reference и popper При частых обновлениях можно хранить размеры элементов в объекте и передавать их в модификатор.
Избегание глубоких селекторов в boundary Использование простых селекторов ускоряет вычисление видимой области.
Эти методы совместно позволяют добиться высокой производительности при работе с динамическими интерфейсами, минимизируя лишние вычисления и оптимально распределяя нагрузку между модификаторами.