ФиксИнтернет-магазин на 1С-Битрикс под ключ за 30 дней по фиксированной цене

Современный фронтенд на 1С-Битрикс: Vue, React и сборка

Современный фронтенд на 1С-Битрикс: Vue и React поверх серверных компонентов, сборка, взаимодействие с бэкендом, SSR и SEO

«Давайте перепишем сайт на Vue» — фраза, после которой у опытного разработчика 1С-Битрикс сводит скулы. Не потому, что Vue или React плохи, а потому, что за ней часто стоит подмена: магазин с исправно работающим серверным рендерингом собираются превратить в клиентский SPA — и заодно потерять SEO, скорость и половину преимуществ платформы. При этом обратная крайность не лучше: городить умный фильтр, корзину и конфигуратор на «голом» jQuery, склеивая состояние строками в DOM, — путь к неподдерживаемому коду. Истина, как обычно, посередине, и она вполне конкретна.

Эта статья — трезвый разбор, как совместить сильные стороны серверного Битрикса с современным фронтендом. Разберём, где Vue и React реально оправданы (интерактивные части: фильтр, корзина, конфигуратор, личный кабинет, дашборд), а где они избыточны (простые контентные страницы — не надо). Покажем, как подключать точечные виджеты в контейнеры внутри штатных компонентов, как отдавать им данные из $arResult и получать через ajax-действия D7 и REST, как настроить сборку на Webpack или Vite, что делать с CSRF-токеном, SSR и гидратацией, чтобы не обрушить SEO и Core Web Vitals. Материал — выжимка из наших проектов по кастомной разработке на 1С-Битрикс.

Коротко

  • Битрикс — серверный движок: для каталога, карточек и контента серверный рендеринг быстрее и лучше для SEO, чем клиентский SPA.
  • Vue и React оправданы точечно — на интерактивных участках: умный фильтр, корзина, конфигуратор, личный кабинет, внутренние дашборды.
  • Правильная модель — виджеты в контейнерах: пустой div в шаблоне компонента, в него монтируется собранный бандл; остальное остаётся серверным.
  • Данные — стартовые из $arResult (в data-атрибут), изменяемые — через ajax-действия контроллеров D7 (внутри сайта) или REST (внешний клиент), с CSRF-токеном.
  • Сборка — Vite или Webpack через npm; исходники в репозитории, хешированные бандлы в папке шаблона.
  • Простой интерактив (табы, аккордеоны, валидация) — оставляйте на vanilla JS; фреймворк тащат только ради настоящего состояния.

Зачем магазину на Битриксе современный фронтенд

Ожидания пользователей выросли: мгновенный отклик фильтра без перезагрузки, корзина, которая пересчитывается на лету, конфигуратор товара с живым превью, личный кабинет, где всё меняется без перезагрузки страниц. Это интерфейсы, к которым людей приучили маркетплейсы, и на классическом «отправил форму — перезагрузил страницу» их не собрать. Вот здесь современный фронтенд — Vue, React и их экосистема — становится не модой, а инструментом под конкретную задачу.

Но у Битрикса своя природа: это в первую очередь серверный движок, который силён именно в отдаче готового HTML, кэше, композите и интеграции с 1С. Ошибка — воспринимать «современный фронтенд» как замену этому. Правильнее — как дополнительный слой поверх сильной серверной основы, включаемый ровно там, где интерактивность оправдывает свою цену. Задача разработчика — провести границу: что остаётся серверным, а что достаётся фреймворку.

Именно об этой границе и пойдёт речь. Мы не будем агитировать ни за «всё на React», ни за «никаких фреймворков». Мы разберём инженерный вопрос: как встроить Vue или React в проект на Битриксе так, чтобы получить выгоду в интерактиве и не заплатить за неё SEO, скоростью и поддерживаемостью. Начнём с того, как вообще устроен рендеринг в Битриксе по умолчанию — без этого невозможно понять, куда встраивается новый слой.

Классический серверный рендеринг Битрикса

По умолчанию Битрикс рендерит страницы на сервере. Схема простая и проверенная: приходит запрос, ядро собирает страницу из шаблона сайта и компонентов, компоненты выбирают данные и отрисовывают HTML своими шаблонами (template.php), и браузер получает уже готовую разметку. Пользователь и поисковый робот видят контент сразу — ничего не нужно «дорисовывать» в браузере. Как устроены эти шаблоны и компоненты, мы подробно разбирали в материале про шаблоны и компоненты 1С-Битрикс.

Интерактив в этой модели традиционно добавляли двумя способами. Первый — обычные ссылки и формы с перезагрузкой страницы: нажали «применить фильтр» — сервер собрал новую страницу. Второй — точечный AJAX на jQuery или ванильном JavaScript: отправили запрос, получили кусок HTML или JSON, подставили в DOM. Для многих задач этого хватает и сегодня, и недооценивать серверную модель — ошибка.

Сильные стороны серверного рендеринга Битрикса конкретны:

Слабое место у этой модели одно, но существенное: сложный интерактив с богатым состоянием собирать вручную тяжело. Когда на странице десятки взаимозависимых элементов, которые меняются без перезагрузки, императивный код на jQuery превращается в клубок. Вот эту нишу и закрывают Vue и React — и только её.

Серверный рендер vs серверная страница + виджеты Классика: всё с сервера HTML шапки и меню каталог, карточка — готовый HTML индексируется сразу интерактив — точечный AJAX/vanilla подвал Гибрид: сервер + виджеты HTML шапки и меню каталог серверный HTML Vue-фильтр виджет React-корзина SEO-текст подвал
Схема: слева — всё рисует сервер; справа — серверная страница остаётся основой, а фреймворк «оживляет» только отдельные интерактивные контейнеры (фильтр, корзина).

Где серверного рендеринга достаточно

Прежде чем звать фреймворк, полезно честно перечислить, где он не нужен вовсе. На типичном магазине это большинство страниц. Их достаточно рендерить на сервере, и любые попытки «осовременить» их клиентским приложением дают минус к скорости и SEO без плюса к пользе.

Общий признак «серверной» страницы — состояние либо отсутствует, либо тривиально (открыть/закрыть, показать/скрыть). Когда пользователь в основном читает и переходит по ссылкам, а не манипулирует сложным интерфейсом, серверный рендеринг оптимален по всем осям: скорость, SEO, простота, стоимость поддержки. Тащить сюда Vue или React — значит платить за то, чем не пользуешься.

Принцип экономии сложности: начинайте с серверного рендеринга по умолчанию и добавляйте клиентский фреймворк только там, где без него интерфейс объективно не собрать чисто. Не «где можно применить Vue», а «где без Vue будет плохо». Это разворачивает мышление в правильную сторону.

Что добавляет Vue и React: слой интерактива

Ценность Vue и React — не в «модности», а в конкретной инженерной модели: декларативный UI, управляемый состоянием. Вы описываете, как интерфейс должен выглядеть при данном состоянии, а фреймворк сам обновляет DOM, когда состояние меняется. Больше не нужно вручную находить элементы и синхронизировать их — вы меняете данные, а представление подстраивается.

Именно это решает главную боль сложного интерактива на «голом» JS. Представьте умный фильтр: пользователь щёлкает чекбоксы, меняет диапазон цены, и одновременно должны обновиться список товаров, счётчик найденного, активные метки выбранных значений, доступность других чекбоксов, URL. На jQuery это десятки императивных обработчиков, которые тянут друг за друга и легко рассыпаются. На Vue или React это одно реактивное состояние, от которого автоматически зависит весь UI.

Что конкретно даёт этот слой:

Ключевая мысль: этот слой ценен ровно там, где есть богатое изменяемое состояние. Нет состояния — нет и выгоды, только накладные расходы на бандл и сборку. Поэтому дальше — самый практический вопрос: на каких именно участках магазина состояние достаточно сложное, чтобы фреймворк окупился.

Где Vue и React реально оправданы

Есть узнаваемый набор участков интернет-магазина, где интерактивность настолько богата, что фреймворк окупается почти всегда. Это места, где пользователь активно манипулирует интерфейсом, а состояние меняется часто и взаимозависимо.

Умный фильтр каталога

Мгновенный пересчёт товаров при изменении условий, живой счётчик найденного, активные метки, синхронизация с URL, доступность значений в зависимости от выборки. Много взаимозависимого состояния — классический кандидат на Vue/React-виджет. При этом сам список товаров можно оставлять серверным и обновлять его через запрос, а фреймворк держит только панель фильтра и её логику.

Корзина и мини-корзина

Добавление и удаление без перезагрузки, изменение количества, пересчёт сумм и скидок, промокоды, синхронизация мини-корзины в шапке с состоянием на странице. Состояние живёт на нескольких участках страницы и должно быть согласованным — реактивная модель здесь заметно чище императивной.

Конфигуратор товара

Выбор опций, комплектаций, цвета, размера с живым пересчётом цены, доступности и превью. Взаимозависимые опции (одна ограничивает другие), динамическая цена — то место, где декларативный UI экономит массу кода и ошибок.

Личный кабинет и dashboard

Списки заказов с фильтрами, статусы, повтор заказа, редактирование профиля, внутренние панели для B2B-клиентов и менеджеров, аналитические дашборды. Это по сути мини-приложения внутри сайта, где SPA-подход внутри одного раздела уместен: контент за авторизацией, SEO не требуется, а интерактивность высока.

УчастокПочему фреймворк оправданSEO-критичность
Умный фильтрМного взаимозависимого состояния, живой пересчётСредняя (контент товаров — серверный)
Корзина / мини-корзинаСогласованное состояние на нескольких участкахНизкая (персональное)
КонфигураторВзаимозависимые опции, динамическая цена и превьюСредняя
Личный кабинетМини-приложение за авторизациейНет (закрыто от индексации)
Внутренний dashboardСложные таблицы, фильтры, живые данныеНет

Обратите внимание на колонку SEO: чем ниже требования к индексации, тем свободнее можно применять клиентский рендер. Личный кабинет и дашборд закрыты авторизацией — там фреймворк почти без ограничений. Фильтр и конфигуратор стоят на индексируемых страницах — там держим серверный контент, а виджет отвечает только за интерактив.

Где фреймворк избыточен

Зеркальная половина честности — назвать места, где Vue или React принесут больше вреда, чем пользы. Соблазн «раз уж подключили фреймворк, давайте везде» велик, но именно он рождает раздутые бандлы и медленные страницы.

Общее правило-антипод: если на участке нет разделяемого изменяемого состояния и сложной синхронизации UI, фреймворк избыточен. Он добавляет вес бандла, шаг сборки и когнитивную нагрузку, ничего не давая взамен. Хуже того — превращение индексируемого контента в клиентский рендер напрямую бьёт по SEO. Поэтому «где не надо» так же важно проектировать, как «где надо».

Таблица: нужен фреймворк или нет

Соберём критерий выбора в одну таблицу — по ней удобно принимать решение на этапе проектирования конкретного блока. Смотрите не на «модно/немодно», а на характер состояния и требования к SEO.

Признак блокаСерверный рендер / vanillaVue / React-виджет
СостояниеОтсутствует или тривиальноеБогатое, изменяемое, взаимозависимое
Частота перерисовокРедко, по действиюЧасто, реактивно
Синхронизация UIОдин-два элементаМного связанных элементов
SEO-важность контентаВысокая — нужен серверный HTMLНизкая или контент дублируется серверно
ПримерыКарточка, раздел, статья, форма, табыФильтр, корзина, конфигуратор, ЛК, dashboard
Стоимость поддержкиНиже: нет сборки и рантаймаВыше: сборка, версии, компетенции

Если признаки блока в основном в левой колонке — оставляйте серверный рендеринг, при необходимости добавив немного ванильного JS. Если преобладает правая — это кандидат на виджет. Пограничные случаи решайте в пользу простоты: дешевле потом «поднять» блок до фреймворка, чем годами тащить лишнюю сложность там, где она не окупилась.

Три способа подключить современный фронтенд

Технически интегрировать Vue или React с Битриксом можно на трёх уровнях глубины. Они отличаются степенью связанности с платформой и радикальностью перестройки. Для большинства проектов оптимален первый — самый лёгкий.

СпособСутьКогда выбирать
Точечные виджетыФреймворк монтируется в контейнеры внутри штатных компонентов; страница остаётся сервернойПочти всегда: фильтр, корзина, конфигуратор на обычном сайте
SPA-разделЦелый раздел (личный кабинет, B2B-панель) — клиентское приложение за авторизациейВнутренние интерфейсы, где SEO не нужен, а интерактив высок
HeadlessФронтенд полностью отделён, Битрикс — только бэкенд и источник данных через APIКрайняя форма: сильная команда, готовность к SSR и своей инфраструктуре

Разница принципиальна. Точечные виджеты сохраняют всё, чем силён Битрикс: серверный HTML, кэш, композит, SEO. Вы просто добавляете островки интерактива. SPA-раздел изолирует клиентское приложение в зоне, где индексация не нужна, — компромисс без риска для витрины. Headless же переворачивает всё: фронтенд становится самостоятельным приложением, а Битрикс — «головой без тела», отдающей данные. Это мощно, но дорого и требовательно; о нём — отдельный раздел ниже.

Дальше разберём первый способ детально, потому что он покрывает подавляющее большинство реальных задач магазина и при этом наименее рискован.

Точечные виджеты: монтируем в контейнер компонента

Идея точечного виджета проста: шаблон компонента Битрикса выводит обычный серверный HTML, а внутри него оставляет пустой контейнер — div с известным id или data-атрибутом. На клиенте собранный бандл находит этот контейнер и монтирует в него Vue- или React-приложение. Всё, что вне контейнера, остаётся серверным и продолжает работать как раньше.

В шаблоне компонента (например, в template.php кастомного каталога) это выглядит так:

<?php if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED !== true) die(); ?>

<!-- Серверный HTML остаётся, виджет монтируется в пустой контейнер -->
<div class="catalog-layout">
  <div id="smart-filter-app"
       data-section="<?= htmlspecialcharsbx($arParams['SECTION_CODE']) ?>"></div>

  <div class="catalog-list">
    <?php // серверный список товаров — индексируется как обычно ?>
    <?php foreach ($arResult['ITEMS'] as $item): ?>
      <!-- карточка товара, отрисованная сервером -->
    <?php endforeach; ?>
  </div>
</div>

Скрипт виджета подключают штатно — через API ядра, а не тегом <script> в середине вёрстки, чтобы не ломать объединение ресурсов и композит:

<?php
// В template.php или result_modifier.php шаблона компонента
$this->addExternalJs($templateFolder . '/dist/smart-filter.[hash].js');
$this->addExternalCss($templateFolder . '/dist/smart-filter.[hash].css');

Сам бандл на Vue монтируется в контейнер, когда DOM готов. Точка входа виджета минимальна:

// src/smart-filter/main.js — точка входа виджета
import { createApp } from 'vue';
import SmartFilter from './SmartFilter.vue';

const el = document.getElementById('smart-filter-app');
if (el) {
  createApp(SmartFilter, {
    section: el.dataset.section,
  }).mount(el);
}

Аналогично устроен React-виджет — вместо createApp().mount() вызывается createRoot(el).render(...). Ключевой принцип один: виджет знает только про свой контейнер и ничего не ломает вокруг. На странице может быть несколько независимых виджетов (фильтр, мини-корзина, конфигуратор) — каждый в своём контейнере, со своим бандлом, не мешая ни друг другу, ни серверному рендеру.

Vue-виджет монтируется в контейнер компонента Компонент Битрикса (сервер) template.php → серверный HTML список товаров, контент <div id="smart-filter-app"> пустой контейнер data-section=... addExternalJs(bundle) Бандл виджета createApp(SmartFilter) .mount('#smart-filter-app') реактивный UI фильтра только внутри контейнера mount
Схема: компонент отдаёт серверный HTML с пустым контейнером и подключает бандл; фреймворк монтирует реактивное приложение только внутрь этого контейнера, не трогая остальную страницу.

Данные во фронтенд: из $arResult

Виджету нужны данные. Первый и часто лучший способ передать их — прямо из $arResult, ведь компонент уже выбрал всё нужное на сервере. Если стартовое состояние известно на момент рендера страницы, не стоит заставлять браузер делать лишний запрос: отдайте данные сразу, чтобы виджет монтировался уже с наполнением. Это быстрее и не даёт «мигания» пустотой, что важно и для восприятия, и для метрик скорости.

Технически данные сериализуют в JSON и кладут в data-атрибут контейнера или в отдельный тег. Обязательно экранируйте вывод — это данные в HTML-атрибуте:

<?php
use Bitrix\Main\Web\Json;
// В result_modifier.php готовим компактный набор для виджета
$arResult['FILTER_JSON'] = Json::encode([
    'sectionId' => (int)$arResult['SECTION']['ID'],
    'properties' => $arResult['FILTER_PROPERTIES'], // диапазоны, значения
    'selected'  => $arResult['SELECTED_VALUES'],
]);
?>

<!-- в template.php -->
<div id="smart-filter-app"
     data-state="<?= htmlspecialcharsbx($arResult['FILTER_JSON']) ?>"></div>

Во фронтенде виджет читает это состояние при монтировании:

const el = document.getElementById('smart-filter-app');
const initialState = JSON.parse(el.dataset.state || '{}');

createApp(SmartFilter, { initialState }).mount(el);

На практике модели комбинируют: стартовое состояние — из $arResult (быстро, без запроса, дружелюбно к SEO), а дальнейшие изменения — запросами (пользователь щёлкнул фильтр — виджет запросил пересчёт). Так вы получаете лучшее из двух миров: мгновенный первый показ с данными и живой интерактив дальше. Важно не переусердствовать с объёмом данных в атрибуте: кладите только то, что нужно виджету на старте, а не весь массив компонента, иначе раздуете HTML.

Тонкость с кэшем: данные из $arResult кэшируются вместе с компонентом — это хорошо для общих данных фильтра. Но персональное (содержимое корзины, имя пользователя) в кэш попадать не должно: его отдают в некэшируемой части или в динамической области композита. Иначе виджет корзины покажет всем данные одного пользователя.

Взаимодействие с бэкендом: ajax-действия D7

Как только пользователь начинает менять состояние — применяет фильтр, добавляет товар в корзину, листает страницы, — виджету нужно общаться с сервером. Внутри того же сайта самый короткий и правильный путь — ajax-действия контроллеров D7. Это встроенный механизм ядра: вы описываете класс-контроллер с методами-действиями, а фронтенд дёргает их по служебному адресу.

Контроллер наследуется от базового класса ядра и объявляет действия. Ядро само разберёт параметры, проверит сессию и CSRF, вернёт JSON:

<?php
// local/php_interface/lib/Controller/Catalog.php (упрощённо)
namespace Vendor\Shop\Controller;

use Bitrix\Main\Engine\Controller;
use Bitrix\Main\Engine\ActionFilter;

class Catalog extends Controller
{
    // Разрешаем неавторизованным читать фильтр, но защищаем изменяющие действия
    public function configureActions()
    {
        return [
            'filter' => [
                'prefilters' => [ new ActionFilter\HttpMethod([ActionFilter\HttpMethod::METHOD_POST]) ],
            ],
        ];
    }

    // Действие: пересчёт фильтра. Вернёт JSON автоматически
    public function filterAction(int $sectionId, array $values): array
    {
        $result = \Vendor\Shop\Filter::apply($sectionId, $values);
        return [
            'count' => $result['COUNT'],
            'items' => $result['ITEMS'],   // или HTML списка
            'facets' => $result['FACETS'], // доступные значения
        ];
    }
}

Фронтенд вызывает действие по служебному эндпоинту ядра, передавая полное имя контроллера, имя действия и CSRF-токен:

async function applyFilter(sectionId, values, sessid) {
  const body = new FormData();
  body.append('sectionId', sectionId);
  body.append('sessid', sessid); // CSRF-токен Битрикса
  Object.entries(values).forEach(([k, v]) => body.append(`values[${k}]`, v));

  const res = await fetch(
    '/bitrix/services/main/ajax.php?action=vendor:shop.Catalog.filter',
    { method: 'POST', body, headers: { 'X-Requested-With': 'XMLHttpRequest' } }
  );
  const json = await res.json();
  return json.data; // ядро оборачивает ответ в { status, data, errors }
}

Плюсы этого пути очевидны: не нужно поднимать отдельный API, авторизация уже работает через сессию пользователя, ядро само проверяет CSRF и оборачивает ответ в единый формат { status, data, errors }. Для виджетов на той же странице того же сайта это способ по умолчанию. Полноценный REST нужен в другой ситуации — о ней следующий раздел.

Взаимодействие через REST

Когда клиент внешний или обособленный — мобильное приложение, отдельный headless-фронтенд на своём домене, интеграция со сторонней системой — вместо ajax-действий используют полноценный REST. Разница принципиальна: ajax-действия завязаны на сессию того же сайта, а REST — самостоятельный интерфейс с собственной аутентификацией по токенам (OAuth), пригодный для любого клиента откуда угодно.

В экосистеме Битрикса REST реализуют либо через модуль REST и приложения (rest-приложения с OAuth-токенами), либо собственными эндпоинтами на базе того же D7 — маршрутами, которые отдают и принимают JSON и проверяют токен доступа. Для headless-витрины обычно строят продуманный слой API: каталог, разделы, товар, корзина, оформление заказа — каждый ресурс с чёткими методами. Мы подробно разбираем проектирование такого слоя в отдельном материале про REST API в 1С-Битрикс, а построение самого API под ключ — это наша услуга разработки REST API для 1С-Битрикс.

КритерийAjax-действия D7REST
Кто клиентВиджет на той же странице того же сайтаВнешний: приложение, headless, интеграция
АутентификацияСессия пользователя + CSRF (sessid)Токены доступа (OAuth)
Порог входаНизкий: контроллер + fetchВыше: проектирование API, версии, документация
ФорматJSON, обёртка ядра { status, data, errors }JSON, свой контракт ресурсов
Когда выбиратьПо умолчанию для виджетов сайтаОбособленный фронтенд, headless, мобильные

Практический вывод: не поднимайте REST ради виджета на своей же странице — это лишняя работа и лишняя поверхность безопасности. Ajax-действия закрывают этот сценарий проще и надёжнее. REST резервируйте для случаев, когда клиент действительно внешний и сессии сайта ему недоступны. Выбор правильного механизма взаимодействия экономит недели и снижает риски.

Фронтенд ↔ бэкенд: ajax-действия и REST Виджет на странице Vue / React fetch + sessid Внешний клиент мобильное / headless OAuth-токен ajax.php?action=… CSRF + сессия REST-эндпоинт токен доступа Бэкенд Битрикса контроллеры D7 данные, права, 1С
Схема: виджет на странице ходит на бэкенд через ajax-действия контроллера D7 (сессия + CSRF), а внешний клиент — через REST с токеном доступа; в обоих случаях за ними работает то же ядро Битрикса.

CSRF-токен и безопасность запросов

Любой изменяющий запрос к Битриксу должен нести CSRF-токен — это защита от подделки запросов. В Битриксе токен называется bitrix_sessid, и без него ajax-действия, меняющие данные, отклоняются. Игнорировать это нельзя: и потому что не заработает, и потому что это реальная мера безопасности.

Токен генерирует сервер, и его нужно передать во фронтенд. Правильный способ — вывести его на клиент так, чтобы он был свежим на каждый запрос (то есть не в кэшируемой части шаблона, а в некэшируемой области или в подвале):

<?php // в подвале или в некэшируемой области — токен персональный ?>
<script>
  window.SHOP_SESSID = '<?= htmlspecialcharsbx(bitrix_sessid()) ?>';
</script>

Во фронтенде токен подставляют в каждый POST-запрос — параметром sessid и/или заголовком, вместе с X-Requested-With:

async function postAction(action, payload) {
  const body = new FormData();
  body.append('sessid', window.SHOP_SESSID);
  Object.entries(payload).forEach(([k, v]) => body.append(k, v));

  return fetch(`/bitrix/services/main/ajax.php?action=${action}`, {
    method: 'POST',
    body,
    headers: {
      'X-Requested-With': 'XMLHttpRequest',
      'X-Bitrix-Csrf-Token': window.SHOP_SESSID,
    },
  }).then(r => r.json());
}

Несколько правил безопасности, о которых легко забыть:

Сборка: Webpack, Vite и npm

Как только появляется Vue или React с однофайловыми компонентами и import, нужен шаг сборки: исходники превращаются в готовые бандлы, которые понимает браузер. Управляет зависимостями npm, а собирает Webpack или Vite. Для точечных виджетов задача, по сути, одна — собрать несколько независимых бандлов и положить их в папку шаблона.

Vite обычно приятнее в разработке: быстрый старт, мгновенный HMR (горячая замена модулей), лаконичная конфигурация и удобный library-режим для сборки отдельных виджетов. Webpack — зрелый и предсказуемый, с огромной экосистемой; он часто уже используется в проекте или в механике расширений самого ядра. Для наших задач разница не драматична — важнее дисциплина организации, а не выбор инструмента.

Минимальная сборка виджетов на Vite в режиме библиотеки может выглядеть так:

// vite.config.js — сборка независимых виджетов
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';

export default defineConfig({
  plugins: [vue()],
  build: {
    outDir: 'local/templates/main/dist',
    rollupOptions: {
      input: {
        'smart-filter': 'src/smart-filter/main.js',
        'cart':        'src/cart/main.js',
        'configurator':'src/configurator/main.js',
      },
      output: {
        entryFileNames: '[name].[hash].js',   // хеш для сброса кэша
        assetFileNames: '[name].[hash].[ext]',
      },
    },
  },
});

Скрипты в package.json дают команде понятные точки входа — разработку и продакшен-сборку:

{
  "scripts": {
    "dev":   "vite",
    "build": "vite build",
    "watch": "vite build --watch"
  }
}

Практический критерий выбора прост: начинаете с нуля и собираете набор виджетов — берите Vite; встраиваетесь в существующий пайплайн или в механику расширений ядра — оставайтесь на том, что уже есть. Не тратьте недели на спор об инструменте: и Webpack, и Vite решают задачу. Гораздо важнее — как вы храните исходники и бандлы, и об этом дальше.

Пайплайн сборки: исходники → бандлы src/ (репозиторий) *.vue / *.jsx / *.ts package.json node_modules — не в git npm run build Vite / Webpack минификация, хеши dist/ в шаблоне filter.[hash].js cart.[hash].js addExternalJs() В git — исходники и собранные бандлы; node_modules — нет хеш в имени файла сбрасывает кэш браузера при обновлении
Схема сборки: исходники в репозитории через npm и Vite/Webpack превращаются в хешированные бандлы в папке шаблона, которые Битрикс подключает штатным addExternalJs.

Где хранить исходники и бандлы

Организация файлов — то, что отличает поддерживаемый проект от «свалки». Правило простое: исходники и собранные бандлы — в репозитории, зависимости — нет. Разберём по пунктам.

Где именно собирать — тоже вопрос дисциплины. Идеально — на этапе CI/CD: пуш в репозиторий запускает сборку, готовые бандлы попадают на сервер вместе с кодом. Тогда на боевом сервере не нужен Node.js, а версия фронтенда всегда соответствует версии бэкенда. Как выстроить такой конвейер, мы разбираем в связке с общей темой деплоя и окружений. Ключевое здесь — воспроизводимость: любой разработчик по npm ci && npm run build получает идентичный результат, а на прод едет ровно то, что собрано и проверено.

Антипаттерн: собирать бандлы вручную на боевом сервере «по-быстрому» и не хранить исходники в репозитории. Через полгода никто не сможет пересобрать виджет: исходники утеряны, версия зависимостей неизвестна, правки идут прямо в минифицированный код. Исходники в git и воспроизводимая сборка — не бюрократия, а страховка проекта.

State-менеджмент: когда он нужен

Вокруг стейт-менеджеров (Pinia для Vue, Redux/Zustand для React) много шума, и новички тащат их в каждый виджет. На деле для большинства точечных виджетов отдельный стор не нужен: локального состояния компонента и передачи данных через входные параметры и события вполне достаточно. Фильтр, конфигуратор, небольшая форма прекрасно живут на встроенной реактивности без внешнего стора.

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

СитуацияРешение
Один изолированный виджетЛокальный стейт компонента, без стора
Данные из родителя в дочернийВходные параметры (props) и события
Состояние на 2–3 связанных элементаНебольшой общий объект/composable
Состояние делят независимые виджеты (корзина)Стор: Pinia / Redux / Zustand
Большое SPA-приложение (личный кабинет)Стор + маршрутизация внутри приложения

Ориентир: вводите стор, когда реально появляется разделяемое состояние, а не заранее «на будущее». Преждевременный стейт-менеджер усложняет простой виджет и повышает порог входа для команды. Начинайте с локального состояния — и поднимайте до стора ровно тогда, когда синхронизация вручную становится болью. Это тот же принцип экономии сложности, что и с выбором самого фреймворка.

TypeScript: когда оправдан

TypeScript добавляет статическую типизацию поверх JavaScript — и вместе с ней шаг компиляции и определённый порог входа. Стоит ли он того на проекте Битрикса? Зависит от масштаба фронтенда и состава команды, и ответ не одинаков для всех.

TypeScript оправдан, когда фронтенд-часть большая и живёт долго: сложный личный кабинет, B2B-панель, дашборды, headless-витрина. Здесь типы окупаются — они ловят ошибки на этапе сборки, документируют контракты данных (особенно ответы API), заметно облегчают рефакторинг и передачу кода между разработчиками. Чем больше кодовая база и чем дольше её поддерживать, тем сильнее выгода.

TypeScript избыточен, когда виджетов немного и они небольшие: пара сотен строк на фильтр и корзину. Настройка типов, декларации, борьба с типами сторонних библиотек могут съесть больше времени, чем сэкономят. Для маленьких изолированных виджетов современный JS с аккуратной структурой — разумный выбор.

Практика: если решаете внедрять TypeScript, делайте это с самого начала фронтенд-части — «дотипизировать» большой готовый JS-проект дороже, чем писать на TS сразу. И типизируйте в первую очередь границы: ответы ajax-действий и REST, входные параметры виджетов. Именно там типы дают максимум пользы за минимум усилий.

SSR, гидратация и риск для SEO

Здесь — главный подводный камень современного фронтенда, о который разбиваются коммерческие проекты. Классический SPA отдаёт браузеру почти пустой HTML и наполняет страницу через JavaScript уже на клиенте. Для пользователя с быстрым устройством это терпимо. Для поискового робота — проблема: контент, который появляется только после исполнения JS, индексируется хуже и позже. А для магазина каждая недоиндексированная страница — это потерянный трафик и деньги.

Отсюда три понятия, которые нужно различать:

Ключевой вывод для Битрикса: не превращайте индексируемый контент в CSR. И здесь у платформы огромное преимущество — её серверный рендеринг уже отдаёт готовый HTML каталога и карточек. Если вы используете точечные виджеты (наш основной сценарий), проблема SSR почти не встаёт: контент рендерит сам Битрикс на сервере, а виджет добавляет интерактив поверх уже проиндексированного HTML. По сути, серверный рендер Битрикса играет роль «SSR для контента», а фреймворк — роль гидратации интерактивных островков.

Проблема SSR становится острой только в headless-архитектуре, где фронтенд обособлен и сам отвечает за рендер. Тогда без серверного рендеринга (SSR/пререндер на Node.js) SEO проседает, и это приходится решать отдельной инфраструктурой. Именно поэтому headless — крайняя форма, а не «улучшенная версия сайта».

SEO: пустой SPA vs серверный контент + гидратация Клиентский SPA (риск) HTML почти пустой <div id="app"></div> контент рисует JS в браузере робот видит пустоту → минус SEO Битрикс: сервер + гидратация серверный HTML контента каталог, карточка — сразу виджет гидратирует интерактив робот видит контент → SEO цел
Схема: клиентский SPA отдаёт роботу пустой HTML и теряет SEO; серверный рендер Битрикса отдаёт готовый контент сразу, а виджет лишь гидратирует интерактивные островки — индексация не страдает.

Как не потерять SEO при внедрении

Соберём практические правила, которые защищают SEO магазина при добавлении Vue или React. Они простые, но нарушение любого из них дорого обходится в выдаче.

  1. Индексируемый контент — только серверным HTML. Названия, описания, цены, характеристики товаров, тексты разделов должны быть в исходном HTML, который отдаёт Битрикс. Не переносите их в клиентский рендер.
  2. Виджеты — поверх контента, а не вместо него. Фильтр управляет выдачей, но сам список товаров пусть остаётся серверным. Виджет добавляет интерактив, не подменяя контент.
  3. Ссылки — настоящие <a href>. Навигация по каталогу, пагинация, переходы в разделы — обычные ссылки на ЧПУ, доступные роботу, а не только клиентские обработчики. Виджет может перехватывать клик для плавности, но URL должен быть реальным.
  4. Мета-теги ставит сервер. Title, description, canonical, микроразметку формирует Битрикс на сервере (в том числе в component_epilog.php), а не JS на клиенте.
  5. Проверяйте, что видит робот. Смотрите исходный HTML (view-source) и инструменты проверки индексации: контент должен присутствовать до исполнения JS.

Отдельно про фильтр — самое частое место риска. Умный фильтр на Vue легко «случайно» превратить в клиентский рендер списка, и тогда страницы категорий с параметрами перестают нормально индексироваться. Правильно: серверные страницы фильтрации (SEF-URL категорий и популярных комбинаций) отдаёт Битрикс, а виджет обеспечивает мгновенность внутри уже проиндексированной страницы. Как устроена SEO-дружественная фильтрация и её URL, мы разбираем в связке с каталогом — там это ключевая тема. Соблюдая эти правила, вы получаете живой интерфейс без жертв в поиске.

Производительность и размер бандла (CWV)

Второй по значимости риск после SEO — производительность. Каждый килобайт JavaScript, который вы отправляете пользователю, он оплачивает временем загрузки и отзывчивости. Фреймворк добавляет вес рантайма и вашего кода, и без контроля это напрямую бьёт по Core Web Vitals — метрикам, которые видит поиск и по которым его ранжирует. Подробно про сами метрики — в материале про Core Web Vitals для интернет-магазина.

Как современный фронтенд влияет на CWV и что с этим делать:

Конкретные приёмы контроля веса и скорости:

Главная мысль: гибридная модель (серверный контент + лёгкие виджеты) сама по себе дружелюбна к CWV — быстрый первый показ обеспечивает сервер, а интерактив догружается точечно. Ломается это только при небрежности: гигантский общий бандл, клиентский рендер контента, отсутствие резервирования места. Дисциплина по бандлам — прямое условие хороших метрик, а значит и позиций.

Headless как крайняя форма

Headless — самая радикальная модель современного фронтенда. Здесь Битрикс перестаёт отдавать страницы вовсе: он превращается в «голову без тела» — бэкенд и источник данных через API, — а всю отрисовку берёт на себя отдельное фронтенд-приложение (нередко на React/Next.js или Vue/Nuxt) на своей инфраструктуре. Витрина и админка полностью разделены.

Что headless даёт и чего требует:

Честный вывод: headless оправдан не «для современности», а когда есть конкретные причины — сильная фронтенд-команда, потребность в едином API для нескольких клиентов, действительно приложение-уровня интерфейс. Для большинства магазинов это избыточно и рискованно: точечные виджеты дают 90% выгоды современного фронтенда при доле сложности и без потери сильных сторон платформы. Мы подробно разбираем эту модель, её условия и подводные камни в отдельном материале про headless-коммерцию на 1С-Битрикс — там же критерии, когда к ней стоит переходить, а когда нет.

Ориентир по масштабу: точечные виджеты — «добавить интерактив там, где нужно». SPA-раздел — «сделать мини-приложение за авторизацией». Headless — «переписать способ, которым сайт устроен целиком». Это разные по цене и риску шаги; начинать почти всегда стоит с первого и повышать уровень только под доказанную потребность.

Когда остаться на vanilla JS

Не каждая интерактивность требует фреймворка — и это тоже инженерное решение, а не отсталость. Огромная часть «динамики» магазина закрывается современным ванильным JavaScript и штатными расширениями ядра, без единого килобайта Vue или React.

Оставайтесь на vanilla, когда интерактив локальный и без разделяемого состояния:

Современный JavaScript силён: fetch, модули, делегирование событий, IntersectionObserver, шаблонные строки — этого хватает на большинство задач «оживления» страницы. А штатные UI-расширения Битрикса дают готовые попапы, уведомления и элементы без сторонних зависимостей. Прежде чем тянуть фреймворк ради одного слайдера, спросите себя: есть ли здесь настоящее состояние? Если нет — vanilla будет и легче, и быстрее, и проще в поддержке. Экономия сложности почти всегда выигрывает вдолгую.

Порог входа и поддержка команды

Технический выбор — это ещё и организационное решение. Внедряя Vue или React, вы меняете не только код, но и требования к команде, к найму и к передаче проекта. Недооценивать это опасно: красиво собранный виджет, который через год некому поддерживать, — плохая инвестиция.

Что важно взвесить заранее:

Практический вывод: соотносите сложность фронтенда с реальными силами команды и горизонтом поддержки проекта. Для небольшой команды разумнее меньше виджетов и максимум серверного рендеринга; для сильной фронтенд-команды планка выше. Каждый добавленный слой сложности должен окупаться пользой для пользователя — иначе это техдолг, который вы сами себе создаёте. Зрелое решение — то, которое проект сможет нести годами, а не только эффектно выглядит на демо.

Частые ошибки

Ошибки при внедрении современного фронтенда на Битриксе повторяются от проекта к проекту. Зная их заранее, вы избежите дорогих переделок и потери позиций.

Чек-лист внедрения

Короткий список для сверки при проектировании и перед релизом виджетов на магазине:

Вывод

Современный фронтенд на 1С-Битрикс — это не выбор «серверный рендеринг или Vue/React», а грамотное сочетание. Битрикс силён серверной отдачей контента, кэшем, композитом и интеграцией с 1С — и для каталога, карточек, контентных страниц это оптимально по скорости и SEO. Фреймворк добавляют точечно, на участках с богатым изменяемым состоянием: умный фильтр, корзина, конфигуратор, личный кабинет, дашборд. Модель — виджеты в контейнерах внутри компонентов: серверный HTML остаётся основой, а Vue или React оживляют отдельные островки.

Практический стержень такой архитектуры складывается из нескольких дисциплин. Данные — стартовые из $arResult, изменяемые через ajax-действия контроллеров D7 (или REST для внешних клиентов), с обязательным CSRF-токеном. Сборка — Vite или Webpack через npm, исходники в репозитории, хешированные бандлы в папке шаблона, разбивка по виджетам ради Core Web Vitals. SEO защищено тем, что индексируемый контент рендерит сервер, а не клиент; headless остаётся крайней формой под доказанную потребность и сильную команду. И, наконец, простой интерактив честно оставляют на vanilla JS. Такой подход даёт живой современный интерфейс без жертв в поиске, скорости и поддерживаемости. Если нужен такой фронтенд под ваш проект, мы проектируем и внедряем его в рамках кастомной разработки на 1С-Битрикс и строим REST API для внешних клиентов. Другие материалы по архитектуре и производительности собраны в рубрике «Разработка и архитектура».

Частые вопросы

Нужен ли вообще Vue или React для магазина на 1С-Битрикс?

В большинстве случаев — нет, целиком не нужен, и это нормально. Битрикс изначально устроен как серверный движок: компоненты собирают HTML на сервере, отдают готовую страницу, а поиск и пользователь получают контент сразу. Для карточек, разделов каталога, статических и контентных страниц этого достаточно. Фреймворк оправдан точечно — на сложных интерактивных участках, где состояние меняется часто и без перезагрузки: умный фильтр с мгновенным пересчётом, корзина и мини-корзина, конфигуратор товара, личный кабинет и внутренние дашборды. Правильная стратегия — не переписывать сайт в SPA, а вставлять Vue- или React-виджеты в отдельные контейнеры внутри штатных компонентов, оставляя всё остальное на серверном рендеринге.

Как подключить Vue или React к странице компонента Битрикса?

Через точечное монтирование в контейнер. В шаблоне компонента (template.php) вы выводите пустой div с известным id или data-атрибутом, а собранный бандл виджета монтируется в него на клиенте. То есть Битрикс отдаёт серверный HTML, а фреймворк «оживляет» только свой участок. Скрипт бандла подключают штатным механизмом — addExternalJs из шаблона или через AddHeadString в подвале, а не тегом script посреди вёрстки. Такой подход не ломает объединение ресурсов, композит и кэш компонента: остальная страница остаётся обычной серверной, а виджет живёт в своей песочнице.

Откуда виджет берёт данные — из $arResult или отдельным запросом?

Оба способа рабочие, и выбор зависит от того, известны ли данные на момент рендера страницы. Если да — их отдают прямо из $arResult: сериализуют в JSON и кладут в data-атрибут контейнера или в отдельный островок JSON-LD-подобного тега. Виджет при монтировании читает эти данные и стартует уже с наполнением — без лишнего запроса и без «мигания» пустотой, что хорошо и для скорости, и для SEO. Если же данные зависят от действий пользователя (пересчёт фильтра, добавление в корзину, подгрузка следующей страницы), виджет получает их асинхронно — через ajax-действия контроллеров D7 или через REST. На практике комбинируют: стартовое состояние из $arResult, дальнейшие изменения — запросами.

Что такое ajax-действия контроллеров D7 и чем они отличаются от REST?

Ajax-действия (controller actions) — это встроенный в ядро Битрикса механизм: вы описываете класс-контроллер с методами-действиями, а фронтенд дёргает их по служебному адресу, передавая имя действия и параметры. Ядро само проверяет сессию и CSRF-токен, разбирает параметры и возвращает JSON. Это самый короткий путь «фронтенд ↔ бэкенд» внутри того же сайта: не нужно поднимать отдельный API, авторизация уже работает через сессию пользователя. REST — это полноценный внешний интерфейс с токенами доступа (OAuth), он нужен, когда клиент внешний: мобильное приложение, отдельный headless-фронтенд, интеграция со сторонней системой. Правило простое: виджет на той же странице того же сайта — ajax-действия; внешний или обособленный клиент — REST.

Правда ли, что SPA на Битриксе плохо индексируется поисковиками?

Да, это реальный риск, если весь контент рисуется только на клиенте. Классический SPA отдаёт почти пустой HTML и наполняет страницу через JavaScript уже в браузере. Поисковый робот такой контент видит хуже и позже: индексация JS-рендеринга идёт по остаточному принципу, а для коммерческих страниц каждая потеря в выдаче — это деньги. Поэтому на Битриксе не стоит превращать каталог и карточки в клиентский SPA. Безопасная модель — серверный рендеринг основного контента (его отдаёт сам Битрикс) плюс точечные виджеты на интерактивных участках. Если же архитектура headless и фронтенд обособлен, обязателен SSR или пререндер, иначе SEO проседает. Мы подробно разбираем это в разделе про SSR и гидратацию.

Vite или Webpack — что выбрать для сборки под Битрикс?

Оба подходят, потому что для точечных виджетов вам нужна, по сути, одна вещь — собрать несколько независимых бандлов и положить их в папку шаблона. Vite обычно приятнее в разработке: быстрый старт, мгновенный HMR, простая конфигурация, хороший вывод в library-режиме для отдельных виджетов. Webpack — зрелый и предсказуемый, у него огромная экосистема и он часто уже стоит в проектах и в самой платформе для её экстеншенов. Практический критерий: если начинаете с нуля и собираете набор виджетов — берите Vite; если встраиваетесь в существующий пайплайн или в механику расширений ядра — оставайтесь на том, что уже используется. Важнее не сам инструмент, а дисциплина: исходники в репозитории, собранные бандлы с хешем в имени, versioning и понятная команда сборки.

Как передать CSRF-токен во фронтенд-запрос к Битриксу?

Битрикс защищает изменяющие запросы session-based CSRF-токеном (bitrix_sessid). Для ajax-действий ядро ждёт этот токен и без него запрос отклоняется. Получить токен на клиенте можно из отданного сервером значения — например, вывести его в data-атрибут или в мета-строку в подвале — и передавать в каждом POST-запросе как параметр sessid или в заголовке. При обращении к action-эндпоинту ядра в fetch добавляют этот токен и заголовок X-Requested-With. Не встраивайте токен в кэшируемую часть шаблона: он персональный и должен приходить свежим на каждый запрос (для этого его берут в некэшируемой области или в динамической области композита). Для внешнего REST механика другая — там аутентификация идёт через OAuth-токен, а не через sessid.

Когда лучше остаться на vanilla JS вместо Vue или React?

Когда интерактив простой и локальный: показать или скрыть блок, табы, аккордеон, валидация формы, слайдер, кнопка «показать ещё», плавная прокрутка. Тащить ради этого фреймворк и бандл — значит грузить пользователю лишние килобайты и усложнять проект без выгоды. Современный ванильный JavaScript вместе со штатными расширениями ядра закрывает почти весь «обычный» интерактив магазина. Фреймворк начинает окупаться там, где появляется настоящее состояние: много взаимозависимых элементов, частые перерисовки, сложная синхронизация UI и данных — фильтр, корзина, конфигуратор, дашборд. Граница проста: нет разделяемого состояния и сложной реактивности — оставайтесь на vanilla; есть — берите фреймворк, но точечно.

Поделиться:

Нужен современный фронтенд на 1С-Битрикс?

Внедрим Vue/React-виджеты там, где они окупаются — фильтр, корзина, конфигуратор, личный кабинет, — поверх серверного Битрикса, без потери SEO и скорости. Настроим сборку, взаимодействие с бэкендом и Core Web Vitals. Рассчитаем работу по вашему проекту.

Игорь Воскресенский

Команда студии B2Bsite. С 2014 года разрабатываем, ускоряем и поддерживаем интернет-магазины на 1С-Битрикс: серверные компоненты и современный фронтенд на Vue и React, сборка и взаимодействие с бэкендом, REST API, Core Web Vitals и обмен с 1С для среднего и крупного бизнеса.

← Все статьи блога