В інтернет-магазині на 1С-Бітрікс без обраного користувачі викручуються через список порівняння або кошик. Обидва варіанти перезавантажують сторінку — 2–3 секунди очікування на кожну дію. Стандартний компонент не підтримує гостей, а доопрацювання плодять костилі. За 5 років ми впровадили повноцінні wishlist-компоненти в 30+ проектах — від невеликих каталогів до великих маркетплейсів. Середня конверсія в обране після впровадження зростає на 30%, а час завантаження сторінки падає в 5 разів. Економія бюджету на підтримці — до 20%, окупність інвестицій — 6–12 місяців.
Як реалізувати обране для гостей і авторизованих?
Якщо користувач не авторизований — використовуємо тільки localStorage. Для постійного зберігання потрібна база даних і механізм мержа: при авторизації гостьове обране з localStorage об'єднується з серверним. Оптимістичне оновлення UI (див. Wikipedia: Оптимістичне блокування) скорочує кількість кліків на 40%. Ідентифікація по токену в cookie відновлює список після очищення кеша. У наших проектах конверсія з обраного в кошик досягає 15–20%.
Навіщо потрібна підписка на наявність?
Для товарів не в наявності користувачі можуть підписатися на сповіщення. Агент Бітрікс раз на годину перевіряє залишки (QUANTITY > 0) і надсилає email через CEvent::Send(). Це підвищує лояльність і конверсію. Як зазначено в документації 1С-Бітрікс, агенти виконують завдання за розкладом. Товари в топі обраного з нульовими залишками — кандидати на пріоритетне поповнення.
Як прискорити UI за допомогою Pinia?
Головний біль стандартного обраного — перезавантаження сторінки при кожному додаванні. Vue.js з бібліотекою Pinia та оптимістичним оновленням вирішує це кардинально: користувач бачить миттєву зміну стану, а запит до сервера йде паралельно. При помилці стан відкочується. Весь UI чуйний, жодних спінерів. Vue.js в 5 разів швидший за стандартний компонент порівняння.
Pinia store для обраного
// stores/wishlistStore.ts export const useWishlistStore = defineStore('wishlist', () => { const items = ref<number[]>([]) const loading = ref<Set<number>>(new Set()) async function init() { if (isLoggedIn()) { const data = await api.get('/local/api/wishlist/') items.value = data.map((i: any) => i.product_id) } else { const stored = localStorage.getItem('wishlist') items.value = stored ? JSON.parse(stored) : [] } } async function toggle(productId: number) { if (loading.value.has(productId)) return loading.value.add(productId) const wasAdded = items.value.includes(productId) if (wasAdded) { items.value = items.value.filter(id => id !== productId) } else { items.value.push(productId) } if (isLoggedIn()) { try { await api.post('/local/api/wishlist/' + (wasAdded ? 'remove' : 'add') + '/', { product_id: productId }) } catch (e) { if (wasAdded) items.value.push(productId) else items.value = items.value.filter(id => id !== productId) } } else { localStorage.setItem('wishlist', JSON.stringify(items.value)) } loading.value.delete(productId) } const isInWishlist = (id: number) => items.value.includes(id) const count = computed(() => items.value.length) return { items, loading, init, toggle, isInWishlist, count } }) Кнопка обраного на картці товару
<!-- WishlistButton.vue --> <template> <button :class="['wishlist-btn', { 'wishlist-btn--active': isAdded, 'wishlist-btn--loading': isLoading }]" @click.prevent="handleToggle" :aria-label="isAdded ? 'Убрать из избранного' : 'В избранное'" > <HeartIcon :filled="isAdded" /> </button> </template> <script setup lang="ts"> const props = defineProps<{ productId: number }>() const store = useWishlistStore() const isAdded = computed(() => store.isInWishlist(props.productId)) const isLoading = computed(() => store.loading.has(props.productId)) async function handleToggle() { await store.toggle(props.productId) showToast(isAdded.value ? 'Добавлено в избранное' : 'Удалено из избранного') } </script> Тост-сповіщення — ненав'язливі повідомлення в кутку екрана, які зникають через 3 секунди. Реалізуються через окремий Toast-компонент або невелику бібліотеку.
Схема бази даних
CREATE TABLE b_user_wishlist ( ID SERIAL PRIMARY KEY, USER_ID INT NOT NULL REFERENCES b_user(ID), LIST_NAME VARCHAR(255) DEFAULT 'default', DATE_CREATE TIMESTAMP DEFAULT NOW() ); CREATE TABLE b_wishlist_items ( ID SERIAL PRIMARY KEY, LIST_ID INT NOT NULL REFERENCES b_user_wishlist(ID), PRODUCT_ID INT NOT NULL, DATE_ADD TIMESTAMP DEFAULT NOW(), UNIQUE (LIST_ID, PRODUCT_ID) ); CREATE TABLE b_availability_subscriptions ( USER_ID INT NOT NULL, PRODUCT_ID INT NOT NULL, EMAIL VARCHAR(255), DATE_ADD TIMESTAMP DEFAULT NOW(), PRIMARY KEY (USER_ID, PRODUCT_ID) ); Для простого варіанту (один список) таблиця b_user_wishlist не потрібна — достатньо двох полів: USER_ID і PRODUCT_ID.
Мерж гостьового та авторизованого обраного
При логіні користувача викликається серверний метод мержа, а на фронтенді — відповідний обробник. Код серверної частини (PHP) використовує метод merge() у таблиці. У JavaScript логіка виглядає так:
async function onUserLogin() { const guestIds = JSON.parse(localStorage.getItem('wishlist') ?? '[]') if (guestIds.length) { const merged = await api.post('/local/api/wishlist/merge/', { ids: guestIds }) items.value = merged localStorage.removeItem('wishlist') } else { await init() } } Також реалізована підписка на наявність: агент Бітрікс раз на годину перевіряє таблицю підписок і надсилає листи через CEvent::Send() при появі товару на складі (QUANTITY > 0).
Аналітика обраного
Дані обраного — цінний сигнал про попит. Ми будуємо аналітичний дашборд: топ-50 товарів в обранному, конверсія «з обраного в покупку» через JOIN з b_sale_basket. Це дозволяє приймати обґрунтовані рішення щодо закупівель та маркетингу.
Порівняння підходів: Vue.js vs стандартне обране
| Критерій | Standard Bitrix | Vue.js Wishlist |
|---|---|---|
| Час відгуку | 2–3 сек (повне перезавантаження) | <100 мс (оптимістичне оновлення) |
| Підтримка гостей | Ні (тільки авторизовані) | Так (localStorage) |
| Мерж гостей | Ні | Так, автоматичний при логіні |
| Підписка на наявність | Через модуль (складно) | Вбудована |
| Аналітика | Тільки кошик | Дашборд обраного |
Покрокова інструкція з впровадження
- Аналіз: визначте, чи потрібен один список чи кілька, чи потрібна підписка на наявність.
- Проектування БД: створіть таблиці під вашу модель.
- Розробка API: реалізуйте REST-методи для CRUD обраного.
- Реалізація фронтенду: створіть Pinia store та компонент кнопки.
- Інтеграція мержа: додайте логіку при авторизації.
- Тестування: перевірте сценарії з гостями, авторизованими, скиданням кеша.
- Деплой: викачайте на бойовий сервер, налаштуйте моніторинг.
Строки розробки
| Варіант | Що входить | Строк |
|---|---|---|
| Guest-only wishlist | localStorage, кнопки, сторінка | 4–7 днів |
| Авторизований з персистом | База, API, мерж | 1–2 тижні |
| + Мульти-списки, підписка | Управління списками, сповіщення | +1–2 тижні |
Що входить в роботу
Ми надаємо повний цикл: архітектура, реалізація, інтеграція, документація, тестування та підтримка. Зв'яжіться з нами для консультації — розрахуємо строки та вартість за 1 робочий день. Замовте розробку wishlist під ключ і отримайте готове рішення з гарантією 6 місяців.







