Ваша форма обратной связи на Битрикс открывается в штатном BX.PopupWindow? При попытке добавить многошаговую логику или асинхронную подгрузку фотографий вы быстро упираетесь в ограничения встроенного рендеринга — DOM пересоздаётся при каждом открытии, события не наследуются, отладка превращается в квест. Разработчики часто пытаются расширить BX.PopupWindow через jQuery-манипуляции с контентом, но это ведёт к хрупкому коду и конфликтам с кэшированием компонентов.
Мы предпочитаем другой путь: изолированное Vue-приложение, которое монтируется в точке вызова и управляет всеми состояниями реактивно. Накопленный опыт — более 30 проектов с интеграцией Vue.js в Битрикс — от простых форм до сложных многошаговых корзин. Такой подход сокращает время разработки на 40% и упрощает поддержку.
Как устроена архитектура модального окна на Vue.js в Битрикс
Интеграция строится через точку монтирования в шаблоне компонента. Вместо глобальной инициализации приложения мы монтируем изолированный экземпляр на конкретный элемент:
// 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-* атрибуты или через глобальный объект window.BX_STATE, который формируется в result_modifier.php. Прямая PHP-интерполяция в JS-коде запрещена — она нарушает кэширование компонента. Структура Vue-компонента модального окна включает:
- ModalWrapper.vue — обёртка с оверлеем, управляет z-index, блокировкой скролла, обработкой клавиши Escape
- ModalContent.vue — слот для контента, анимации входа/выхода через Transition
- ModalTrigger.vue — кнопка-триггер, эмитит событие открытия через provide/inject или Pinia store
Для управления состоянием нескольких модалей на одной странице мы используем useModalStore на Pinia — стек открытых модалей, чтобы закрытие верхней не закрывало нижнюю.
Почему Vue.js лучше jQuery для модальных окон в Битрикс?
Сравнение ключевых параметров:
| Параметр | BX.PopupWindow + jQuery | Vue.js |
|---|---|---|
| Реактивное состояние | Нет (вручную через DOM) | Есть (data, computed) |
| Валидация форм | Внешняя библиотека | VeeValidate или computed |
| Анимации | CSS-классы вручную | Transition |
| Кэширование компонента | Работает плохо | Работает штатно (изоляция) |
| Время разработки формы | 3-5 дней | 1-2 дня |
Мы прошли путь от jQuery до Vue на десятках проектов — разница в производительности и времени поддержки очевидна.
Какие типичные сценарии мы решаем?
Форма обратной связи с валидацией. Поля валидируются реактивно через VeeValidate. После отправки 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 при каждом открытии. Мы поступили иначе: одна точка монтирования в







