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

Наша компанія займається розробкою, підтримкою та обслуговуванням рішень на Бітрікс та Бітрікс24 будь-якої складності. Від простих односторінкових сайтів до складних інтернет-магазинів, CRM систем з інтеграцією 1С та телефонії. Досвід розробників підтверджено сертифікатами від вендора.
Пропоновані послуги
Показано 1 з 1 послугУсі 1626 послуг
Розробка системи компонентів на Vue.js для 1С-Бітрікс
Середня
~1-2 тижні
Часті питання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851
  • 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
    585
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Розробка на базі 1С Підприємство для компанії МИРСАНБЕЛ
    751
  • image_crm_dolbimby_434_0.webp
    Розробка сайту на CRM Бітрікс24 для компанії DOLBIMBY
    657
  • image_crm_technotorgcomplex_453_0.webp
    Розробка на базі Бітрікс24 для компанії ТЕХНОТОРГКОМПЛЕКС
    989

Розробка системи компонентів на 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 тижні

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