Разработка decoupled-фронтенда для 1С-Битрикс
Decoupled — это когда фронтенд физически отделён от Битрикса: живёт на своём сервере, деплоится независимо, использует свой стек (React, Vue, Next.js), а с Битриксом общается только через API. В отличие от чистого headless, decoupled может сохранять часть страниц на Битрикс-шаблонах — например, кабинет администратора или страницы, генерируемые CMS.
Когда нужен decoupled
Decoupled оправдан, когда текущий Битрикс-шаблон стал ограничением по производительности или возможностям UX, но переписывать всё сразу нет возможности. Типичные сценарии:
- Каталог товаров заменяется на React SPA, остальные страницы остаются на Битрикс-шаблоне
- Личный кабинет клиента переписывается на Vue, остальное — без изменений
- Мобильное приложение получает данные из Битрикса через API, веб-сайт остаётся на шаблоне
Это эволюционный подход: постепенно заменяете части сайта, не устраивая полный редизайн.
Паттерн «остров» — частичная интеграция фронтенда
Вместо полного отделения фронтенда вы встраиваете React-компоненты в существующий Битрикс-шаблон через точки монтирования:
// В шаблоне компонента каталога Битрикс
// /local/templates/main/components/bitrix/catalog/my_template/template.php
// Данные для React передаём через data-атрибут или глобальную переменную
$catalogData = json_encode($arResult['ITEMS']);
?>
<div id="react-catalog"
data-items="<?= htmlspecialchars($catalogData) ?>"
data-currency="RUB">
</div>
<script src="/local/js/dist/catalog.bundle.js"></script>
<script>
window.BitrixCatalog && window.BitrixCatalog.mount(
document.getElementById('react-catalog'),
<?= $catalogData ?>
);
</script>
// catalog.bundle.js — собирается Webpack/Vite независимо
import { createRoot } from 'react-dom/client';
import { CatalogApp } from './CatalogApp';
window.BitrixCatalog = {
mount(container, initialData) {
const root = createRoot(container);
root.render(<CatalogApp initialData={initialData} />);
}
};
Этот подход позволяет разрабатывать фронтенд на React с полноценным toolchain (TypeScript, hot reload, тесты) и при этом не трогать остальную часть Битрикс-сайта.
Конфигурация сборки (Vite)
// vite.config.js для decoupled-компонентов Битрикс
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
build: {
outDir: '../public/local/js/dist',
lib: {
entry: './src/index.tsx',
name: 'BitrixComponents',
formats: ['iife'], // IIFE для встройки в Битрикс-шаблон
fileName: 'components',
},
rollupOptions: {
external: [], // не экстернализируем зависимости
},
},
server: {
cors: true, // разрешаем CORS для dev-сервера
port: 3000,
},
});
В режиме разработки Vite dev-сервер запускается на localhost:3000, а Битрикс-сайт — на site.local. Обращения к API из dev-сервера проксируются через Vite proxy:
server: {
proxy: {
'/api': {
target: 'http://site.local',
changeOrigin: true,
}
}
}
Управление состоянием между Битрикс и React
Самая сложная часть decoupled — синхронизация состояния между Битрикс-частью и React-компонентами. Корзина — классический пример: иконка корзины в Битрикс-шапке должна показывать актуальное число товаров, добавленных через React-каталог.
Решение через кастомный Event Bus:
// shared/eventBus.js — доступен и в Битрикс-части, и в React
window.BitrixEventBus = {
listeners: {},
emit(event, data) {
(this.listeners[event] || []).forEach(cb => cb(data));
},
on(event, callback) {
(this.listeners[event] ||= []).push(callback);
}
};
// В React-компоненте при добавлении в корзину:
window.BitrixEventBus.emit('cart:updated', { count: newCount });
// В Битрикс-шаблоне (шапка):
window.BitrixEventBus.on('cart:updated', ({ count }) => {
document.querySelector('.cart-counter').textContent = count;
});
Деплой и CI/CD
Decoupled-фронтенд требует отдельного шага сборки в CI/CD. Простой pipeline для GitLab CI:
build-frontend:
stage: build
script:
- cd frontend
- npm ci
- npm run build
- rsync -avz dist/ server:/var/www/site/local/js/dist/
only:
- main
После деплоя фронтенда Битрикс-шаблоны автоматически подхватывают новую версию бандла. Версионируйте файлы по хешу содержимого (catalog.a1b2c3.js) чтобы избежать проблем с браузерным кэшем.
Decoupled — разумный компромисс между «всё переписать» и «оставить как есть». Позволяет модернизировать фронтенд постепенно, снижает риск и позволяет реально оценить отдачу от инвестиций в новый стек до полного перехода.







