Розробка фронтенду інтернет-магазину на Vue.js для 1С-Бітрікс
Типова ситуація: дизайнер намалював каталог з анімованими фільтрами, миттєвим оновленням кошика та переходами між сторінками без перезавантаження. Фронтенд-розробник дивиться на шаблони bitrix:catalog.section та bitrix:sale.order.ajax — і розуміє, що вписати це в стандартну компонентну модель Бітрікс неможливо без костилів. Тут появляється headless-підхід: Бітрікс залишається бекендом, а весь інтерфейс живе на Vue.js.
Це не «модний стек заради моди». Headless виправданий, коли стандартні шаблони Бітрікс не дозволяють реалізувати потрібний UX, коли фронтенд-команда працює автономно від backend-розробників, або коли один API обслуговує сайт, мобільний додаток та кіоски в офлайн-точках.
Архітектура: як розділяються шари
У класичному Бітрікс-магазині PHP-компонент робить вибірку, передає масив в template.php, там же підключається CSS і JS. У headless-схемі все інакше:
-
Бітрікс працює як API-сервер. Каталог, ціни, залишки, кошик, оформлення, авторизація — все через REST API (модуль
rest) або кастомні контролери на основі\Bitrix\Main\Engine\Controller. - Vue.js / Nuxt.js — окремий додаток. Рендерить інтерфейс, керує маршрутизацією, станом, формами.
-
Nginx проксує:
/api/*йде в Бітрікс, інше — на статику Vue або Node.js (при SSR).
Розгортання фронтенду та бекенду незалежне. Фронтенд-розробник пушить у свій репозиторій, CI збирає бандл, розкатує на CDN або Node-сервер. Backend-розробник оновлює Бітрікс окремо. Контракт між ними — API-специфікація.
REST API Бітрікс: що працює, а що доведеться дописувати
Модуль rest надає методи для основних сутностей магазину.
Каталог:
-
catalog.product.list— товари з фільтрацією за властивостями, розділом, ціною -
catalog.product.get— детальна карточка -
catalog.product.offer.list— торгові пропозиції (SKU) -
catalog.section.list— дерево категорій -
catalog.price.list— ціни за типом
Кошик:
-
sale.basket.addItem,sale.basket.updateItem,sale.basket.deleteItem,sale.basket.getItems
Замовлення:
-
sale.order.add,sale.order.get,sale.order.list -
sale.shipment.getDeliveryServices,sale.paySystem.getList
На папері все покрито. На практиці починаються нюанси.
catalog.product.list не повертає довільні властивості інфоблока. Потрібно додатково запитувати через catalog.product.getFieldsByFilter або писати свій endpoint. Фасетна фільтрація — підрахунок кількості товарів за кожним значенням фільтра, як у стандартному smart_filter — в REST API відсутня. Розрахунок вартості доставки за змістом кошика — ще один метод, якого немає з коробки.
Рішення — кастомні REST-методи. Реєструються через \CRestServer::onRestServiceBuildDescription() або через \Bitrix\Main\Engine\Controller з анотацією @restMethod. На стороні Бітрікс кастомний контролер виконує вибірку й повертає JSON:
-
/api/catalog/filter— товари + фасети (кількість за значеннями фільтра) -
/api/cart/calculate— перерахунок кошика з урахуванням правил кошика, знижок та промокодів -
/api/checkout/submit— оформлення замовлення одним запитом
Фасетний індекс — окрема історія. Бітрікс зберігає перерахункові фасети в таблиці b_catalog_smart_filter. При headless-підході потрібно або використовувати цю таблицю прямо через ORM, або будувати фасети на льоту. Перший варіант швидший, але привʼязує до внутрішньої структури Бітрікс. Другий — повільніше на великих каталогах (50 000+ товарів), але передбачуваніший.
Компонентна архітектура Vue-додатка
Структура фронтенду для інтернет-магазину:
src/
├── pages/
│ ├── CatalogPage.vue # список товарів з фільтрами
│ ├── ProductPage.vue # карточка товара
│ ├── CartPage.vue # кошик
│ ├── CheckoutPage.vue # оформлення
│ └── AccountPage.vue # особистий кабінет
├── components/
│ ├── catalog/
│ │ ├── ProductCard.vue
│ │ ├── FilterPanel.vue
│ │ └── FacetCounter.vue
│ ├── cart/
│ │ ├── CartItem.vue
│ │ └── CartSummary.vue
│ └── ui/ # переиспользуємі елементи
├── stores/
│ ├── catalogStore.ts # Pinia: товари, фільтри, пагінація
│ ├── cartStore.ts # кошик, синхронізація з API
│ ├── userStore.ts # авторизація, токен
│ └── checkoutStore.ts # оформлення замовлення
├── api/
│ ├── catalog.ts # обгортки над API каталогу
│ ├── cart.ts
│ └── auth.ts
└── composables/
├── useProductFilter.ts # логіка фільтрації
└── useInfiniteScroll.ts # нескінченна прокрутка
Pinia керує станом. cartStore — найнетривіальніший: при додаванні товара потрібно миттєво оновити UI (optimistic update), відправити запит до API, отримати відповідь з актуальною ціною (Бітрікс міг застосувати знижку або списати залишок) та синхронізувати локальний стан з сервером. Для неавторизованих користувачів кошик живе в localStorage і мігрує на сервер після входу.
Vue Router з lazy-loading: кожна сторінка — окремий chunk. Переходи між категоріями не перезавантажують додаток, а фільтри записуються в query-параметри URL для можливості поділитися посиланням.
SSR: без нього в e-commerce не можна обійтись
Vue SPA рендерить на клієнті. Пошуковий робот бачить пустий <div id="app"></div>. Для інтернет-магазину, де карточки товарів і категорії мають індексуватися, це вирок.
Nuxt.js з SSR — основний варіант. Node.js-сервер рендерить Vue-компоненти в HTML, дані з Бітрікс API запитуються через useFetch() або useAsyncData(). Клієнт отримує готовий HTML, після гідратації додаток працює як SPA.
Nuxt.js з ISR (Incremental Static Regeneration) — гібрид. Сторінки каталогу кешуються й оновлюються за TTL або за вебхуком з Бітрікс при змінені товара. Nuxt 3 підтримує routeRules з swr (stale-while-revalidate):
// nuxt.config.ts
routeRules: {
'/catalog/**': { swr: 3600 }, // кеш на годину
'/product/**': { swr: 600 }, // кеш на 10 хвилин
'/cart': { ssr: false }, // кошик — тільки клієнт
'/checkout': { ssr: false },
}
Для каталогів з 50 000+ товарів SSR переважніше повної статичної генерації — nuxt generate для такого обсягу займе години.
Мета-теги — окрема задача. У Бітрікс шаблони SEO настроюються у властивостях інфоблока (шаблони вигляду {=this.Name} купити в Мінську). При headless-підході ці шаблони потрібно віддавати через API та застосовувати у Nuxt через useHead() або useSeoMeta().
Авторизація
Два підходи:
OAuth 2.0 через модуль rest: фронтенд перенаправляє на /oauth/authorize/, користувач логується на стороні Бітрікс, отримує code, обмінює на access_token. Стандартний flow, але UX страждає — редирект на інший домен.
Кастомний JWT-endpoint: /api/auth/login приймає логін/пароль, Бітрікс перевіряє через CUser::Login(), створює сесію й повертає JWT. Фронтенд зберігає токен в httpOnly cookie (не в localStorage — інакше XSS-уразливість). Refresh-токен продовжує сесію без повторного введення пароля. Простіше в реалізації, UX краще.
Що втрачається при headless
- Візуальний редактор — не працює. Контент керується через адмінку Бітрікс, фронтенд забирає дані по API.
- Композитний кеш — не застосовний. Кешування на стороні Nuxt (ISR) або CDN.
-
Стандартні компоненти —
bitrix:catalog.section,bitrix:sale.order.ajaxне використовуються. Вся логіка відображення на Vue. - Обмін з 1С — працює без змін, це серверна сторона.
Терміни за масштабом проекту
| Масштаб | Що входить | Термін |
|---|---|---|
| MVP-каталог | Листинг, карточка товара, фільтри, SSR | 1–2 тижні |
| Магазин без особистого кабінету | + кошик, оформлення, оплата | 3–4 тижні |
| Повнофункціональний магазин | + ОК, історія замовлень, вподобане, порівняння | 5–8 тижнів |
| B2B-портал | + типи цін за групами, персональні каталоги, швидке замовлення | 8–12 тижнів |
Headless на Бітрікс — компроміс. Сучасний фронтенд та гібкість в обмін на втрату частини екосистеми та збільшення вартості підтримки. Підхід виправданий для проектів з високими вимогами до інтерфейсу, виділеною фронтенд-командою та планами на мультиплатформність.







