Разработка модальных окон и pop-up на Vue.js для 1С-Битрикс
Стандартные битриксовые компоненты используют jQuery-диалоги из BX.PopupWindow — они работают, пока не нужна сложная логика внутри: многошаговые формы, реактивная валидация, асинхронная загрузка контента. Попытка расширить BX.PopupWindow кастомным HTML быстро превращается в борьбу с его внутренним рендерингом. Vue.js решает это чисто — компонент модального окна получает реактивное состояние, props, emit-события и полный контроль над DOM.
Архитектура модального окна на Vue.js в Битрикс
Интеграция Vue в Битрикс строится через точку монтирования в шаблоне компонента. Вместо того чтобы инициализировать приложение глобально, монтируем изолированный экземпляр на конкретный элемент:
// local/templates/main/components/project/modal-form/template.php
<div id="vue-modal-root"
data-form-id="<?= $arResult['FORM_ID'] ?>"
data-endpoint="<?= $arResult['AJAX_URL'] ?>">
</div>
<script>
BX.ready(function() {
const { createApp } = Vue;
const app = createApp(ModalApp, {
formId: document.getElementById('vue-modal-root').dataset.formId,
endpoint: document.getElementById('vue-modal-root').dataset.ajaxEndpoint
});
app.mount('#vue-modal-root');
});
</script>
Данные из Битрикса передаются через data-* атрибуты или через глобальный JS-объект window.BX_STATE, который формируется в result_modifier.php. Прямой PHP-интерполяции в JS-коде избегаем — это нарушает кеширование компонента.
Структура Vue-компонента модального окна:
-
ModalWrapper.vue— обёртка с оверлеем, управляетz-index, блокировкой скролла (document.body.style.overflow), обработкойEscape -
ModalContent.vue— слот для контента, анимации входа/выхода через<Transition> -
ModalTrigger.vue— кнопка-триггер, эмитит событие открытия черезprovide/injectили Pinia store
Для управления состоянием нескольких модалей на странице используем useModalStore на Pinia — стек открытых модалей, чтобы закрытие верхней не закрывало нижнюю.
Типичные сценарии и их реализация
Форма обратной связи с валидацией. Самый частый случай. Поля валидируются реактивно через VeeValidate или вручную через computed. После отправки — AJAX на битриксовый обработчик через bitrix:form.result.new, ответ обрабатывается в компоненте. Никакого перезапуска страницы.
Модальная корзина. Пользователь добавляет товар, в модале показывается актуальное состояние корзины. Данные получаем через REST API Битрикса: /api/v1/sale/basket/ или кастомный AJAX-контроллер на Bitrix\Main\Engine\Controller. Реактивное обновление количества без перезагрузки.
Галерея изображений / lightbox. Vue-компонент получает массив изображений через data-images атрибут или через предзагруженный JSON. Swipe-жесты через vue-use/useSwipe, клавиатурная навигация через onKeydown.
Многошаговые формы. Шаги как отдельные Vue-компоненты, состояние хранится в родительском setup(). Прогресс-бар реагирует на текущий шаг. Данные каждого шага аккумулируются и отправляются единым запросом на последнем шаге.
Кейс: модальная форма заявки на промо-странице
Промо-сайт продукта, несколько CTA-кнопок на странице, каждая открывает одну и ту же форму, но с разным utm_source и предзаполненным полем «Откуда вы о нас узнали».
Стандартное решение через BX.PopupWindow требовало бы JS-манипуляций с DOM формы при каждом открытии. Решили иначе: одна точка монтирования в </body>, Vue-компонент LeadModal принимает props через eventBus. Кнопки на странице диспатчат кастомное событие:
document.querySelectorAll('[data-modal-trigger]').forEach(btn => {
btn.addEventListener('click', () => {
window.dispatchEvent(new CustomEvent('open-lead-modal', {
detail: { source: btn.dataset.source, productId: btn.dataset.productId }
}));
});
});
Vue-компонент слушает событие через onMounted:
window.addEventListener('open-lead-modal', (e) => {
formData.source = e.detail.source;
formData.productId = e.detail.productId;
isOpen.value = true;
});
Форма отправляет данные в битриксовый модуль crm через REST API (crm.lead.add), статус записывается в b_user_field через CUserTypeManager. Разработка заняла 3 дня вместо недели на jQuery-варианте.
Производительность и SEO
Модальные окна рендерятся только при открытии — используем v-if, а не v-show, чтобы DOM не раздувался. Для тяжёлых компонентов (видеоплеер, карта) — defineAsyncComponent:
const HeavyMap = defineAsyncComponent(() => import('./HeavyMap.vue'));
Чанк загружается только тогда, когда пользователь открывает модал. Битрикс через AssetManager подключает сборку Vue только на нужных страницах:
\Bitrix\Main\Page\Asset::getInstance()->addJs('/local/dist/modal-bundle.js');
SEO модальные окна не затрагивают — контент внутри них поисковики не индексируют как основной. Ссылки на акции или товары внутри модалей дублируются в <noscript> или в скрытых <a> для краулеров, если содержимое важно для поиска.
Этапы разработки
- Анализ — инвентаризация существующих pop-up на проекте, определение сценариев взаимодействия, согласование состояний (открыт/закрыт/загрузка/ошибка/успех)
- Прототип компонента — базовая анимация, блокировка скролла, закрытие по оверлею и Escape, фокус-трап для accessibility
- Интеграция с данными — подключение к REST API или кастомному AJAX-контроллеру Битрикса, обработка ошибок
-
Сборка и подключение — Vite-сборка в
local/dist/, подключение черезAssetManagerтолько на нужных страницах
| Сложность задачи | Ориентировочный срок |
|---|---|
| Один типовой модал с формой | 1-2 дня |
| Система из 3-5 связанных модалей | 3-5 дней |
| Многошаговая форма с валидацией и интеграцией CRM | 5-10 дней |
Конечный срок зависит от количества состояний, сложности интеграции с Битрикс и требований к анимациям.







