Блог разработчика 1С-Битрикс

Vue.js в Битриксе: интеграция современного фронтенда в классическую CMS

Цель статьи — дать рабочий подход: после прочтения вы сможете добавить динамический фронтенд на Битрикс (динамические формы, фильтры каталога, корзина, личный кабинет) на реальном проекте — без магии и за рамками hello world.

Vue.js в Битриксе

1. Зачем использовать Vue.js в проектах на Битрикс?

Ключевые плюсы:

  • SPA-опыт там, где это уместно. Фильтры каталога, корзина, личный кабинет — всё реагирует мгновенно, без перезагрузок. UX растёт, конверсия часто — тоже.
  • Отделение логики от шаблонов. В Битриксе бизнес-логика часто «просачивается» в template.php. Vue помогает держать состояние и UI-логику в компонентах, а сервер — в компонентах/контроллерах Bitrix.
  • Предсказуемость и тестируемость. Состояние — в одном месте (Composition API), данные приходят из понятных точек (AJAX/REST/runComponentAction).
  • Инкрементальное внедрение. Не обязательно переписывать сайт в SPA. Можно встраивать маленькие островки Vue в существующие страницы («интеграция Vue с Битрикс» без революции).

2. Особенности архитектуры Битрикс: куда встраивать Vue

Где чаще всего живут «островки» Vue.js в Битрикс:

  • Публичная часть
    • В шаблоне сайта (/local/templates/<site>/header.php, footer.php) — подключение ассетов.
    • В шаблонах стандартных компонентов (/local/components/.../templates/.default/template.php) — создаём корневой контейнер и монтируем приложение.
  • Компоненты
    • Сервер подготавливает данные (arResult) и рендерит базовую разметку.
    • Vue берёт готовые данные и дальше управляет UI/состоянием.
  • AJAX-обработчики
    • Современный путь — методы компонентов в режиме Controllerable + BX.ajax.runComponentAction.
    • Либо контроллеры \Bitrix\Main\Engine\Controller с runAction.
  • Админка
    • Кастомные страницы и настройки модуля, мини-SPA в админ-интерфейсе. (Подходы те же, только про SEO думать меньше.)

3. Подготовка среды: подключение Vue и сборка ассетов

Вариант A. Подключение через CDN (быстрый старт)

В шаблоне сайта (например, header.php) подключаем Vue 3:

<?php
use Bitrix\Main\Page\Asset;
Asset::getInstance()->addString('<script src="CDN_URL_TO_VUE3" defer></script>');
// Пример: прод-версия Vue 3 с ESM или IIFE — на ваше усмотрение.
// В проде лучше зафиксировать версию.
        

Плюсы: просто, быстро. Минусы: контроль и кеширование хуже, HMR в разработке нет.

Вариант B. Сборка через Vite/Webpack (рекомендуется для проекта)

Структура (пример):

/local/assets/vue-app/        # результат сборки (prod)
/local/assets/vue-app/manifest.json
/local/js/vue-src/            # исходники (dev)
  main.ts
  components/
  ...
        

Помощник для подключения манифеста Vite (prod) и HMR (dev):

<?php
function includeVite(string $entry = 'src/main.ts'): void
{
    $isDev = getenv('VITE_DEV') === '1';
    if ($isDev) {
        echo '<script type="module" src="http://localhost:5173/@vite/client"></script>';
        echo '<script type="module" src="http://localhost:5173/' . htmlspecialchars($entry) . '"></script>';
        return;
    }
    $manifestPath = $_SERVER['DOCUMENT_ROOT'] . '/local/assets/vue-app/manifest.json';
    if (!is_file($manifestPath)) return;
    $manifest = json_decode(file_get_contents($manifestPath), true);
    $item = $manifest[$entry] ?? null;
    if (!$item) return;
    foreach (($item['css'] ?? []) as $css) {
        echo '<link rel="stylesheet" href="/local/assets/vue-app/' . htmlspecialchars($css) . '">';
    }
    echo '<script type="module" src="/local/assets/vue-app/' . htmlspecialchars($item['file']) . '"></script>';
}
        

В header.php достаточно сделать includeVite();.

Так мы получаем HMR в dev и минифицированные ассеты с хешами в prod — «правильный» динамический фронтенд на Битрикс.

4. Передача данных из Битрикс во Vue

Четыре рабочих способа, которые легко тестировать:

  1. Глобальная переменная (быстро и понятно)
    <?php
    use Bitrix\Main\Web\Json;
    ?>
    <script>
      window.__APP_PROPS__ = <?= Json::encode($arResult['JS_DATA'] ?? []) ?>;
    </script>
                    
  2. data-* атрибуты у корневого div (удобно, если несколько инстансов на странице)
    <div id="vue-filter"
         data-props='<?= htmlspecialcharsbx(Json::encode($arResult['JS_DATA'] ?? [])) ?>'></div>
                    
  3. REST/контроллер — Vue сам запрашивает данные после монтирования (меньше SSR-данных).
  4. AJAX-компонентыBX.ajax.runComponentAction к вашему компоненту.

Дополнительно: строки переводов удобно передавать через BX.message().

5. Пример интеграции: динамический фильтр товаров внутри шаблона компонента

Разберём минимально жизнеспособный пример «островка» Vue — фильтр каталога с серверной реализацией на компоненте.

Файловая структура

/local/components/my/catalog.filter/
  .description.php
  class.php
  /templates/.default/
    template.php
    result_modifier.php
        

class.php (серверная логика + AJAX-действие)

<?php
use Bitrix\Main\Engine\Contract\Controllerable;
use Bitrix\Main\Engine\ActionFilter;
use Bitrix\Main\Loader;
use Bitrix\Main\Context;
class MyCatalogFilterComponent extends CBitrixComponent implements Controllerable
{
    public function configureActions(): array
    {
        return [
            'apply' => [
                'prefilters' => [
                    new ActionFilter\HttpMethod([ActionFilter\HttpMethod::METHOD_POST]),
                    new ActionFilter\Csrf(),
                ],
                'postfilters' => [],
            ],
        ];
    }
    public function applyAction(array $filters = []): array
    {
        // Валидация входящих данных
        $priceFrom = isset($filters['priceFrom']) ? (int)$filters['priceFrom'] : null;
        $priceTo   = isset($filters['priceTo']) ? (int)$filters['priceTo'] : null;
        Loader::includeModule('iblock');
        // Пример: выборка из ИБ (упрощённо)
        $arFilter = ['IBLOCK_ID' => (int)$this->arParams['IBLOCK_ID'], 'ACTIVE' => 'Y'];
        if ($priceFrom !== null) { $arFilter['>=PROPERTY_PRICE'] = $priceFrom; }
        if ($priceTo !== null)   { $arFilter['<=PROPERTY_PRICE'] = $priceTo; }
        $items = [];
        $res = \CIBlockElement::GetList(['SORT' => 'ASC'], $arFilter, false, ['nTopCount' => 20], ['ID','NAME','DETAIL_PAGE_URL','PROPERTY_PRICE']);
        while ($el = $res->GetNext()) {
            $items[] = [
                'id'    => (int)$el['ID'],
                'name'  => $el['~NAME'],
                'url'   => $el['~DETAIL_PAGE_URL'],
                'price' => (int)$el['PROPERTY_PRICE_VALUE'],
            ];
        }
        return ['items' => $items];
    }
    public function executeComponent()
    {
        $this->arResult['JS_DATA'] = [
            'iblockId' => (int)$this->arParams['IBLOCK_ID'],
            'initial'  => ['priceFrom' => null, 'priceTo' => null],
        ];
        $this->includeComponentTemplate();
    }
}
        

Обратите внимание: компонент реализует Controllerable, поэтому на фронте используем BX.ajax.runComponentAction.

result_modifier.php (готовим данные к фронту)

<?php
if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED !== true) die();
use Bitrix\Main\Web\Json;
// Можно дополнительно нормализовать данные
$arResult['JS_DATA'] = $arResult['JS_DATA'] ?? [];
        

template.php (корневой контейнер + монтирование Vue)

<?php if(!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED!==true) die();
use Bitrix\Main\Web\Json;
$this->setFrameMode(true); // дружим с композитным режимом
// Обёртка динамической области (если включён композит)
// $frame = new \Bitrix\Main\Page\FrameHelper('vue_filter');
// $frame->begin();
?>
<div id="vue-filter"
     data-props='<?= htmlspecialcharsbx(Json::encode($arResult["JS_DATA"])) ?>'></div>
<?php
// Подключаем ассеты Vue (см. раздел про Vite/CDN)
includeVite('src/main.ts'); // или Asset::getInstance()->addString(...) для CDN
// $frame->end();
?>
<script>
  // Если вы подключаете Vue из CDN IIFE — получите глобал `Vue`.
  // Если через Vite/Webpack ESM — импорт будет в /src/main.ts.
</script>
        

src/main.ts (Vue 3, Composition API)

import { createApp, ref, onMounted } from 'vue';
function parseProps(el: HTMLElement) {
  try {
    return JSON.parse(el.dataset.props || '{}');
  } catch {
    return {};
  }
}
const App = {
  name: 'CatalogFilter',
  setup() {
    const root = document.getElementById('vue-filter') as HTMLElement;
    const props = parseProps(root);
    const priceFrom = ref<number|null>(props.initial?.priceFrom ?? null);
    const priceTo   = ref<number|null>(props.initial?.priceTo ?? null);
    const loading   = ref(false);
    const items     = ref<any[]>([]);
    const error     = ref<string|null>(null);
    const apply = async () => {
      loading.value = true;
      error.value = null;
      try {
        const response = await BX.ajax.runComponentAction(
          'my:catalog.filter',
          'apply',
          {
            mode: 'class',
            data: {
              filters: { priceFrom: priceFrom.value, priceTo: priceTo.value }
            }
          }
        );
        items.value = response.data.items;
      } catch (e: any) {
        error.value = 'Ошибка загрузки';
      } finally {
        loading.value = false;
      }
    };
    onMounted(() => {
      apply(); // загрузка начальных данных
    });
    return { priceFrom, priceTo, loading, items, error, apply };
  },
  template: `
    <div class="b-filter">
      <div class="b-filter__row">
        <label>Цена от: <input type="number" v-model.number="priceFrom" min="0"></label>
        <label>до: <input type="number" v-model.number="priceTo" min="0"></label>
        <button :disabled="loading" @click="apply">Применить</button>
      </div>
      <div v-if="error" class="b-filter__error">{{ error }}</div>
      <div v-if="loading">Загрузка…</div>
      <ul class="b-filter__list" v-if="!loading && items.length">
        <li v-for="it in items" :key="it.id">
          <a :href="it.url">{{ it.name }}</a> — {{ it.price }} ₽
        </li>
      </ul>
      <div v-if="!loading && !items.length">Ничего не найдено</div>
    </div>
  `
};
const app = createApp(App);
app.mount('#vue-filter');
        

Готово: это интеграция Vue с Битрикс в реальном компоненте. Сервер решает бизнес-логику и безопасность, фронт — UX.

6. Работа с формами и AJAX: отправка данных во входные точки Битрикс

Вариант 1. BX.ajax.runComponentAction (как в примере выше)

  • Автоматически подставляет sessid.
  • Ясная маршрутизация: vendor:component, метод, mode: 'class'.

Вариант 2. Контроллеры \Bitrix\Main\Engine\Controller

PHP (контроллер модуля/проекта):

<?php
namespace My\Project\Controller;
use Bitrix\Main\Engine\Controller;
use Bitrix\Main\Engine\ActionFilter;
class Forms extends Controller
{
    public function getDefaultPreFilters(): array
    {
        return [
            new ActionFilter\HttpMethod([ActionFilter\HttpMethod::METHOD_POST]),
            new ActionFilter\Csrf(),
        ];
    }
    public function feedbackAction(array $payload): array
    {
        // Валидация, сохранение, почтовое событие и т.д.
        return ['ok' => true];
    }
}
        

Vue (fetch напрямую):

const send = async (payload: any) => {
  const res = await fetch('/bitrix/services/main/ajax.php?action=my:project.Forms.feedback', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ payload, sessid: BX.bitrix_sessid() }),
    credentials: 'same-origin',
  });
  const data = await res.json();
  if (!data || data.status !== 'success') throw new Error('Request failed');
  return data;
};
        

Советы по формам

  • Валидируйте на фронте и на бэке. На фронте — UX, на бэке — безопасность.
  • CSRF: используйте ActionFilter\Csrf() и BX.bitrix_sessid().
  • Ошибки: возвращайте осмысленные коды/сообщения; на фронте показывайте пользователю понятные тексты.
  • Файлы: для загрузок используйте FormData, не забудьте про ограничения PHP/Битрикс и проверку MIME.

7. Советы и подводные камни

Кеширование и композит:

  • В шаблонах включайте <?php $this->setFrameMode(true); ?>.
  • Если страница в композитном режиме, заверните Vue-островок в динамическую область (FrameHelper) либо инициализируйте Vue после получения динамических данных.
  • Для блоков, которые часто меняются (корзина, лайки), не кладите динамику в жёсткий кеш компонента; разделяйте: «скелет» — кешируется, «данные» — AJAX.

SEO (публичная часть):

  • Если критичный контент (названия товаров, цены) нужен роботу — рендерите базовую разметку сервером, а Vue улучшает интерактивность (прогрессивное улучшение).
  • Для фильтров думайте о чистых URL и серверной выдаче по ним; Vue — для UX, а не вместо серверной страницы.
  • Добавьте noscript с базовым контентом, если уместно.

Совместимость с Bitrix API и JS-ядром:

  • Не ломайте BX-события. Если компонент Битрикса что-то подгружает через AJAX, Vue-инстанс может отвалиться. Решение — слушать события BX и пере-монтировать при динамической подгрузке (или использовать MutationObserver).
  • Глобальные имена. Избегайте коллизий в window; используйте неймспейсы, модули.
  • Локализация. BX.message() отлично подходит для строк, которыми питается Vue.

Инициализация на динамическом контенте:

  • Если часть DOM прилетает позже (например, пагинация сетки), подписывайтесь на события (например, грид может эмитить кастомные события) и повторно монтируйте/обновляйте Vue-компонент.
  • Перед повторным монтированием вызывайте app.unmount() у предыдущего инстанса, чтобы избежать утечек.

Производительность:

  • Дробите приложение на мини-виджеты вместо одного большого.
  • Используйте ленивую загрузку компонентов (dynamic import) в сборке.
  • Следите за количеством реактивных источников; вычисляемые значения (computed) вместо «ручных» вотчеров.

8. Когда Vue — оправданное решение, а когда — избыточное?

Оправдано:

  • Динамический фильтр каталога, быстрый поиск/сортировка без релоуда.
  • Корзина/мини-корзина, сравнение товаров, избранное.
  • Кабинет пользователя: заказы, адреса, формы, статусы, история — всё живёт в состоянии.
  • Сложные формы (много шагов, валидация, зависимые поля).

Избыточно:

  • Простые статические блоки, где нужна только пара «показать/скрыть».
  • Одноразовые лендинги без интерактива.
  • Страницы, критичные для SEO и полностью сервер-рендерные (там лучше сначала навести порядок в шаблонах компонентов).

Правило простое: если есть состояние и много пользовательских действий — Vue окупится. Если нет — сделайте минимальный JS или используйте стандартные возможности компонентов.

Заключение

Интеграция Vue.js в Битрикс — это не «или-или», а инкрементальная модернизация. Начните с маленького «островка» (фильтр, форма, мини-корзина), наладьте поток данных (передача arResult → Vue, AJAX-действия), приручите кеш/композит — и дальше масштабируйте. Такой подход даёт современный UX и сохраняет сильные стороны Битрикса: готовые модули, компоненты, админку и понятную серверную модель.

Если коротко — Vue.js в Битрикс отлично работает, если вы:

  1. держите серверную логику на стороне компонентов/контроллеров,
  2. отдаёте Vue чистые данные и
  3. уважаете кеш/SEO.

Тогда интеграция Vue с Битрикс превращается из эксперимента в стабильный производственный паттерн. Удачных релизов!

Теги: Vue.js, Битрикс, интеграция Vue с Битрикс, динамический фронтенд, SPA, фильтры каталога, корзина, личный кабинет, AJAX, Composition API, Bitrix, CMS, фронтенд-разработка


Валерий Макеев
27.10.2025 11:14
Этот Vue-компонент отображает корзину товаров с возможностью  динамического изменения количества товаров через AJAX-запросы к  компоненту Битрикса, с мгновенным пересчетом общей суммы.
Код
<?php
// /local/components/my/cart/templates/.default/template.php
if(!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED!==true) die();

use Bitrix\Main\Web\Json;
$this->setFrameMode(true);
?>
<div id="vue-cart" data-props='<?= Json::encode($arResult['CART_DATA']) ?>'></div>

<script>
document.addEventListener('DOMContentLoaded', function() {
    const { createApp } = window.Vue;
    
    createApp({
        data() {
            return {
                items: [],
                loading: false
            }
        },
        computed: {
            total() {
                return this.items.reduce((sum, item) => sum + (item.PRICE * item.QUANTITY), 0);
            }
        },
        mounted() {
            const props = JSON.parse(document.getElementById('vue-cart').dataset.props);
            this.items = props.items || [];
        },
        methods: {
            async updateQuantity(itemId, newQuantity) {
                this.loading = true;
                try {
                    const result = await BX.ajax.runComponentAction('my:cart', 'update', {
                        mode: 'class',
                        data: { itemId, quantity: newQuantity }
                    });
                    this.items = result.data.cart;
                } catch (error) {
                    console.error('Ошибка обновления:', error);
                }
                this.loading = false;
            }
        },
        template: `
            <div class="cart">
                <div v-if="loading" class="loading">Обновление...</div>
                <div class="cart-item" v-for="item in items" :key="item.ID">
                    <span>{{ item.NAME }}</span>
                    <input 
                        type="number" 
                        :value="item.QUANTITY" 
                        @change="updateQuantity(item.ID, $event.target.value)"
                        min="1">
                    <span>{{ item.PRICE * item.QUANTITY }} ₽</span>
                </div>
                <div class="cart-total">Итого: {{ total }} ₽</div>
            </div>
        `
    }).mount('#vue-cart');
});
</script>

Стоимость услуг по разработке и сопровождению сайтов на 1C-Битрикс

Разработка интернет-магазина с готовой версткой

от 4 недель

от 90 000 рублей

* указана минимальная стоимость. Стоимость выбранной лицензии «1С-Битрикс» оплачивается отдельно.

Разработка корпоративного сайта

от 7 дней

от 40 000 рублей

Разработка сайта без системы оплаты заказов через корзину

* стоимость зависит от наличия верстки, использования готового решения и т.д.

Модули и компоненты для «1С-Битрикс»

оценка производится на основе предоставленного Технического Задания

от 20 000 рублей
Разработка дополнительных модулей для 1С-Битрикс, расширение функционала, внедрение любых решений, требующихся для выполнения ваших бизнес-задач.

* стоимость зависит от конкретной задачи, ее объема и сложности выполняемых работ.