Реактивний компонент порівняння товарів на Vue.js для Бітрікс
Типова ситуація: користувач додає товар у порівняння, сторінка перезавантажується, лічильник змінюється. Через три кліки він іде на маркетплейс. Стандартний компонент порівняння 1С-Бітрікс (bitrix:catalog.compare.buy та bitrix:catalog.compare.result) працював прийнятно багато років тому, але сьогодні UX-очікування кардинально змінилися. Ми пропонуємо замінити його на сучасне Vue.js-рішення: додавання в порівняння миттєве, лічильник у шапці оновлюється без перезавантаження, таблиця вміє ховати однакові характеристики. Компонент розробляємо під ключ, з адаптивом і підтримкою.
Чому варто замінити стандартне порівняння на Vue.js?
Стандартний компонент використовує сесію PHP для зберігання списку — це означає перезавантаження при кожній дії. Vue.js-версія працює повністю на фронтенді: стан зберігається в Pinia-store, синхронізується з localStorage для гостей та з сервером для авторизованих. Це дає швидкість, чуйність та збереження вибору між сесіями. У результаті час додавання товару скорочується з 1-2 секунд до 50 мс, а конверсія в кошик зростає на 15–25%.
Як працює компонент?
Зберігання списку порівняння
Стандартний Бітрікс зберігає порівняння в сесії PHP. Ми використовуємо localStorage з синхронізацією на сервер для авторизованих користувачів — так список зберігається між сесіями та пристроями.
// stores/compareStore.ts (Pinia)
export const useCompareStore = defineStore('compare', {
state: () => ({
items: [] as number[], // product IDs
}),
actions: {
async add(productId: number) {
if (this.items.includes(productId)) return
if (this.items.length >= 4) {
alert('Можно сравнивать до 4 товаров одновременно')
return
}
this.items.push(productId)
localStorage.setItem('compare_items', JSON.stringify(this.items))
// Синхронизация с сервером для авторизованных
if (isLoggedIn()) {
await fetch('/local/api/compare/add/', {
method: 'POST',
body: JSON.stringify({ product_id: productId }),
})
}
},
remove(productId: number) {
this.items = this.items.filter(id => id !== productId)
localStorage.setItem('compare_items', JSON.stringify(this.items))
},
isInCompare: (state) => (productId: number) => state.items.includes(productId),
},
getters: {
count: (state) => state.items.length,
},
})
При ініціалізації додатку список завантажується з localStorage (для гостей) або з API (для авторизованих, з мержем між пристроями).
Кнопка «Порівняти» на картці товару
<!-- CompareButton.vue -->
<template>
<button
:class="['compare-btn', { 'compare-btn--active': isAdded }]"
@click="toggle"
:title="isAdded ? 'Убрать из сравнения' : 'Добавить к сравнению'"
>
<IconScale :filled="isAdded" />
<span>{{ isAdded ? 'В сравнении' : 'Сравнить' }}</span>
</button>
</template>
<script setup lang="ts">
const props = defineProps<{ productId: number }>()
const store = useCompareStore()
const isAdded = computed(() => store.isInCompare(props.productId))
function toggle() {
isAdded.value ? store.remove(props.productId) : store.add(props.productId)
}
</script>
Кнопка монтується на кожній картці товару через data-атрибут.
Лічильник у шапці сайту
<!-- CompareCounter.vue (монтируется в header) -->
<template>
<a href="/compare/" class="header-compare">
<IconScale />
<span class="badge" v-if="count > 0">{{ count }}</span>
</a>
</template>
<script setup lang="ts">
const store = useCompareStore()
const count = computed(() => store.count)
</script>
Лічильник реактивно оновлюється через Pinia — жодного глобального EventBus.
Таблиця порівняння з диф-фільтром
Найскладніша частина — таблиця. Дані завантажуються з сервера за списком ID, потім на фронті будується карта відмінностей. Кнопка «Показати лише відмінності» ховає рядки з однаковими значеннями.
<!-- CompareTable.vue (упрощённо) -->
<template>
<table class="compare-table">
<thead>
<tr>
<th>Характеристика</th>
<th v-for="p in products" :key="p.id">
<ProductCard :product="p" @remove="store.remove(p.id)" />
</th>
</tr>
</thead>
<tbody>
<tr
v-for="spec in visibleSpecs"
:key="spec"
:class="{ 'row--different': diffMap[spec] }"
>
<td class="spec-name">{{ specLabels[spec] }}</td>
<td v-for="p in products" :key="p.id">{{ p.specs[spec] ?? '—' }}</td>
</tr>
</tbody>
</table>
</template>
<script setup lang="ts">
const showOnlyDiff = ref(false)
const visibleSpecs = computed(() =>
showOnlyDiff.value
? Object.keys(diffMap.value).filter(k => diffMap.value[k])
: Object.keys(diffMap.value)
)
</script>
На мобільних перша колонка фіксується через CSS Grid з position: sticky.
Як інтегрувати компонент у шаблон?
Інтеграція складається з трьох кроків:
- Розмістити точки монтування в PHP-шаблонах:
data-compare-btnдля кнопок на картках,data-compare-counterдля лічильника в шапці,data-compare-tableдля сторінки порівняння. - Підключити зібраний бандл Vue.js через
RegisterModuleв епілог шаблону. - Налаштувати API-контролер для отримання даних товарів за списком ID (властивості з
COMPARE = 'Y').
Кейс: зростання конверсії на 20% після впровадження
Один із наших клієнтів — інтернет-магазин з каталогом ~5 000 товарів — зіткнувся з низькою конверсією на сторінці порівняння (всього 2% переходів у кошик). Стандартний компонент працював повільно: кожна дія викликала перезавантаження. Після впровадження Vue.js-компонента швидкість вибірки скоротилася на 70%, а частка користувачів, які додали товар у кошик зі сторінки порівняння, зросла до 22%. Ключовою фішкою стала кнопка «Показати лише відмінності» — клієнти швидше знаходили відмінності та приймали рішення.
Порівняння підходів: стандартний vs Vue.js
| Параметр | Стандартний компонент | Vue.js-компонент |
|---|---|---|
| Час додавання товару | ~1-2 сек (з перезавантаженням) | ~50 мс (без перезавантаження) |
| Збереження списку | Тільки сесія PHP | localStorage + серверна синхронізація |
| Адаптив на мобільних | Обмежений | CSS Grid з sticky-колонкою |
| Фільтр відмінностей | Немає | Є |
| Конверсія в кошик | ~2% | ~22% (за даними кейсу) |
Що входить у роботу
Ми віддаємо готовий компонент, який включає:
- Вихідний код на Vue.js 3 + Pinia + TypeScript.
- PHP-контролер для API порівняння (опціонально).
- Інструкцію з інтеграції в шаблон 1С-Бітрікс.
- Доступ до репозиторію з історією змін.
- Консультаційну підтримку протягом 2 тижнів після здачі.
Процес роботи та терміни
| Етап | Тривалість | Результат |
|---|---|---|
| Аналітика та проектування | 1–2 дні | ТЗ, макет інтеграції |
| Розробка компонента | 5–10 днів | Робочий прототип |
| Інтеграція та тестування | 2–3 дні | Готовий функціонал на стенді |
| Деплой та документування | 1 день | Передача коду та інструкцій |
Орієнтовні терміни: від 1 до 3 тижнів залежно від складності. Зв'яжіться з нами — ми оцінимо ваш проект безкоштовно.
Наш досвід
Ми займаємося Бітрікс-розробкою понад 5 років та реалізували понад 50 проектів, включаючи кастомні компоненти для інтернет-магазинів. Знаємо всі підводні камені інтеграції Vue.js з 1С-Бітрікс та гарантуємо якість. Отримайте консультацію — обговоримо деталі вашого магазину.
Технічні деталі (для розробників)
Стек: Vue.js 3, Pinia, TypeScript, Vite. Збірка — через Webpack або Vite, підключається як окремий entry-point у шаблоні. Компонент не конфліктує з іншими Vue-додатками на сторінці.
Властивості інфоблоку, що беруть участь у порівнянні, позначаються прапорцем COMPARE = 'Y' — це стандартний механізм Бітрікс, що не потребує додаткового коду.
Типові помилки та їх вирішення
- Забули про синхронізацію для авторизованих. Без неї список втрачається при очищенні кеша браузера.
- Не врахували ліміт на кількість товарів. Більше 4–5 товарів у порівнянні перевантажують інтерфейс — краще обмежити.
- Зробили таблицю без фіксованої колонки на мобілках. Користувач не бачить назви характеристик при горизонтальному скролі.
Всі ці проблеми ми вирішуємо на етапі розробки — зверніться до нас за консультацією.
Висновок
Порівняння товарів — важливий інструмент для покупців, які обирають серед схожих позицій. Правильно реалізоване порівняння допомагає прийняти рішення на сайті, а не піти на агрегатор. Наш компонент — це швидко, сучасно та адаптивно. Хочете покращити UX свого магазину? Зв'яжіться з нами — обговоримо деталі та підготуємо комерційну пропозицію.







