Разработка системы компонентов на Vue.js для 1С-Битрикс

Наша компания занимается разработкой, поддержкой и обслуживанием решений на Битрикс и Битрикс24 любой сложности. От простых одностраничных сайтов до сложных интернет магазинов, CRM систем с интеграцией 1С и телефонии. Опыт разработчиков подтвержден сертификатами от вендора.
Предлагаемые услуги
Показано 1 из 1 услугВсе 1626 услуг
Разработка системы компонентов на Vue.js для 1С-Битрикс
Средняя
~1-2 недели
Часто задаваемые вопросы
Наши компетенции:
Этапы разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1173
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    811
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Разработка на базе Битрикс, Битрикс24, 1С для компании Development of an Online Appointment Booking Widget for a Medical Center
    564
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Разработка на базе 1С Предприятие для компании МИРСАНБЕЛ
    745
  • image_crm_dolbimby_434_0.webp
    Разработка сайта на CRM Битрикс24 для компании DOLBIMBY
    655
  • image_crm_technotorgcomplex_453_0.webp
    Разработка на базе Битрикс24 для компании ТЕХНОТОРГКОМПЛЕКС
    976

Разработка системы компонентов на Vue.js для 1С-Битрикс

Один Vue-компонент на сайте — это интеграция. Система компонентов — это архитектура. Разница принципиальная: в системе компоненты делят общее состояние, общий дизайн-токен, единый механизм инициализации и взаимодействия с Битрикс-бэкендом. Без системного подхода через полгода на сайте появляются три версии корзины, два разных подхода к AJAX и дублирующийся код в каждом компоненте.

Что входит в систему компонентов

Типичная система Vue-компонентов для Битрикс-магазина:

  • Ядро: конфигурация приложения, Pinia stores, API-слой, утилиты
  • UI-компоненты: кнопки, инпуты, модалки, тосты — не зависят от бизнес-логики
  • Бизнес-компоненты: корзина, избранное, сравнение, отзывы, фильтр — используют stores и API
  • Точки монтирования: места в PHP-шаблоне, куда монтируются Vue-компоненты

Структура файлов

/local/js/vue/
├── app.ts                    # инициализация и монтирование
├── api/
│   ├── client.ts             # базовый HTTP-клиент
│   ├── cart.ts
│   ├── catalog.ts
│   └── wishlist.ts
├── stores/
│   ├── cartStore.ts
│   ├── wishlistStore.ts
│   ├── compareStore.ts
│   └── userStore.ts
├── components/
│   ├── ui/                   # дизайн-система
│   │   ├── Button.vue
│   │   ├── Modal.vue
│   │   ├── Toast.vue
│   │   ├── Spinner.vue
│   │   └── Badge.vue
│   ├── cart/
│   │   ├── CartButton.vue    # кнопка корзины в шапке
│   │   ├── CartDrawer.vue    # выдвижная панель корзины
│   │   └── CartItem.vue
│   ├── catalog/
│   │   ├── AddToCartBtn.vue
│   │   ├── WishlistBtn.vue
│   │   └── CompareBtn.vue
│   └── product/
│       ├── Reviews.vue
│       └── SizeAdvisor.vue
└── types/
    ├── cart.ts
    ├── product.ts
    └── user.ts

Механизм инициализации

Проблема с множеством Vue-компонентов на одной странице: нельзя создавать отдельное приложение (createApp) для каждого — они не будут делить Pinia stores, и состояние корзины в шапке будет отличаться от состояния в поп-апе.

Правильный подход: одно приложение, множество точек монтирования.

// app.ts
import { createApp, defineAsyncComponent } from 'vue'
import { createPinia }  from 'pinia'

// Регистрация компонентов для монтирования по data-атрибутам
const componentRegistry: Record<string, any> = {
    'cart-button':   defineAsyncComponent(() => import('./components/cart/CartButton.vue')),
    'add-to-cart':   defineAsyncComponent(() => import('./components/catalog/AddToCartBtn.vue')),
    'wishlist-btn':  defineAsyncComponent(() => import('./components/catalog/WishlistBtn.vue')),
    'compare-btn':   defineAsyncComponent(() => import('./components/catalog/CompareBtn.vue')),
    'reviews':       defineAsyncComponent(() => import('./components/product/Reviews.vue')),
    'size-advisor':  defineAsyncComponent(() => import('./components/product/SizeAdvisor.vue')),
}

// Создаём одно приложение с Pinia
const pinia = createPinia()

// Монтируем компоненты в каждый элемент с data-vue-component
document.querySelectorAll('[data-vue-component]').forEach((el) => {
    const name      = el.getAttribute('data-vue-component')!
    const Component = componentRegistry[name]
    if (!Component) return

    // Парсим props из data-атрибутов
    const props: Record<string, any> = {}
    for (const attr of el.attributes) {
        if (attr.name.startsWith('data-prop-')) {
            const propName = attr.name.replace('data-prop-', '').replace(/-./g, m => m[1].toUpperCase())
            props[propName] = JSON.parse(attr.value)
        }
    }

    const app = createApp(Component, props)
    app.use(pinia) // Один экземпляр Pinia — общее состояние
    app.mount(el)
})

В PHP-шаблоне:

<!-- Шапка: кнопка корзины -->
<div data-vue-component="cart-button"></div>

<!-- Карточка товара: кнопки добавления -->
<div data-vue-component="add-to-cart"
     data-prop-product-id="<?= $product['ID'] ?>"
     data-prop-price="<?= $product['PRICE'] ?>"></div>

<div data-vue-component="wishlist-btn"
     data-prop-product-id="<?= $product['ID'] ?>"></div>

Ключевой момент: один экземпляр pinia передаётся во все приложения. Это означает, что cartStore в шапке и cartStore на карточке товара — одно и то же хранилище, состояние синхронизировано.

API-слой: единый HTTP-клиент

// api/client.ts
const CSRF_TOKEN = (document.querySelector('meta[name="csrf-token"]') as HTMLMetaElement)?.content

async function request<T>(url: string, options: RequestInit = {}): Promise<T> {
    const res = await fetch(url, {
        ...options,
        headers: {
            'Content-Type': 'application/json',
            'X-Bitrix-Csrf-Token': CSRF_TOKEN,
            ...options.headers,
        },
    })

    if (!res.ok) throw new Error(`HTTP ${res.status}: ${await res.text()}`)
    const data = await res.json()
    if (data.errors?.length) throw new Error(data.errors[0].message)

    return data
}

export const apiGet  = <T>(url: string) => request<T>(url)
export const apiPost = <T>(url: string, body: unknown) =>
    request<T>(url, { method: 'POST', body: JSON.stringify(body) })

Все компоненты используют apiGet / apiPost — единая точка для добавления авторизационных заголовков, логирования ошибок, перехватчиков.

Дизайн-система: токены и UI-компоненты

Цвета, шрифты, отступы — через CSS-переменные, которые совпадают с переменными в PHP-шаблоне Битрикс:

/* Определяются в шаблоне Битрикс, используются Vue-компонентами */
:root {
    --color-primary:   #0052cc;
    --color-success:   #00875a;
    --color-danger:    #de350b;
    --spacing-sm:      8px;
    --spacing-md:      16px;
    --border-radius:   4px;
}

Vue-компонент Button.vue использует эти переменные — визуально совместим с остальным сайтом.

Ленивая загрузка и code splitting

defineAsyncComponent + Vite автоматически разбивает код на чанки. CartDrawer.vue загружается только когда пользователь нажимает кнопку корзины — не в начальном бандле. Это критично для производительности: начальная страница не загружает код компонентов, которые возможно никогда не откроются.

// Загрузка только при первом взаимодействии
const CartDrawer = defineAsyncComponent({
    loader: () => import('./components/cart/CartDrawer.vue'),
    loadingComponent: Spinner,
    delay: 200,
})

Тестирование системы

Юнит-тесты Pinia stores через Vitest:

// stores/cartStore.test.ts
import { setActivePinia, createPinia } from 'pinia'
import { useCartStore } from './cartStore'

describe('cartStore', () => {
    beforeEach(() => setActivePinia(createPinia()))

    it('добавляет товар в корзину', async () => {
        const store = useCartStore()
        await store.add(123, 1)
        expect(store.items).toHaveLength(1)
        expect(store.count).toBe(1)
    })
})

Компонентные тесты через Vue Test Utils + Vitest.

Сборка и интеграция с Битрикс

Vite настраивается для сборки в папку /local/js/vue/dist/. В шаблоне Битрикс подключается через:

// В header.php шаблона
\Bitrix\Main\Page\Asset::getInstance()->addJs('/local/js/vue/dist/app.js', true);
// Или через type=module для нативных ES-модулей
?>
<script type="module" src="/local/js/vue/dist/app.js"></script>

Сроки

Масштаб Что входит Срок
Базовая система (3–5 компонентов) Инициализация, Pinia, API-слой, UI-база 3–5 недель
Полноценная система + корзина, избранное, сравнение, отзывы 6–10 недель
+ Дизайн-система, тесты, CI + токены, Vitest, автосборка +2–3 недели

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