Розробка модальних вікон і 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. Свайп-жести через 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 днів |
Кінцевий термін залежить від кількості станів, складності інтеграції з Бітрікс та вимог до анімацій.







