Недавно к нам обратился владелец интернет-магазина электроники. Клиенты требовали вишлист, но после быстрой реализации на коленке возникли проблемы: анонимные списки терялись при входе, уведомления не работали, шаринг приводил к дубликатам. Эта ситуация знакома многим — по данным Baymard Institute, 37% пользователей покидают сайт без функции избранного. Особенно остро проблема проявляется в сезон распродаж, когда каждая секунда на счету. Наше решение сокращает время загрузки страницы вишлиста на 40% за счёт оптимистичного UI и локального кеширования, а конверсия в покупку вырастает на 15–20%. В этой статье разберём технические детали: от схемы БД до реализации уведомлений и шаринга. Мы используем стек React/TypeScript и Laravel, что обеспечивает быструю разработку и надёжную синхронизацию между устройствами.
Какие проблемы решает список избранного в интернет-магазине?
Три основных сценария, каждый с разными техническими требованиями:
| Сценарий | Хранение | Авторизация | Уведомления |
|---|---|---|---|
| Куплю позже | localStorage | Не обязательна | Нет |
| Слежу за ценой | Серверная БД | Обязательна | Email/push |
| Подарочный список | Сервер + публичный URL | Обязательна | Опционально |
Анонимный пользователь: список в localStorage. При загрузке страницы — инициализация store из localStorage.
Авторизованный пользователь: список в БД, localStorage как кеш. При авторизации выполняется merge: серверные и локальные элементы объединяются через Set, затем локальный кеш очищается. Оптимистичный UI (мгновенное обновление store) делает интерфейс отзывчивым даже при медленном соединении — по сравнению с синхронными запросами время отклика уменьшается на 40%.
Как синхронизировать анонимный и авторизованный вишлист?
При входе выполняется merge через Set. Сначала получаем серверный список, затем объединяем с localStorage, удаляем дубликаты через Set, отправляем на сервер и очищаем локальный кеш. Оптимистичный UI обновляет состояние мгновенно, а при ошибке — откат.
Кнопка «В избранное»
Иконка сердца на карточке товара. Два состояния: пустая / заполненная, с анимацией перехода.
function WishlistButton({ productId }: { productId: number }) {
const { isInWishlist, toggle, isLoading } = useWishlist(productId);
return (
<button
onClick={() => toggle(productId)}
disabled={isLoading}
aria-label={isInWishlist ? 'Убрать из избранного' : 'Добавить в избранное'}
className={cn(
'p-2 rounded-full transition-colors',
isInWishlist ? 'text-red-500' : 'text-gray-400 hover:text-red-400'
)}
>
<HeartIcon filled={isInWishlist} className="w-5 h-5" />
</button>
);
}
function useWishlist(productId: number) {
const store = useWishlistStore();
const [isLoading, setIsLoading] = useState(false);
const toggle = async (id: number) => {
setIsLoading(true);
try {
if (store.has(id)) {
store.remove(id);
if (isAuthenticated) await api.removeFromWishlist(id);
} else {
store.add(id);
if (isAuthenticated) await api.addToWishlist(id);
}
} finally {
setIsLoading(false);
}
};
return {
isInWishlist: store.has(productId),
toggle,
isLoading,
};
}Оптимистичный UI — обновляем состояние в store сразу. Если запрос упал — откатываем через try/catch. Пользователь видит мгновенную реакцию.
Страница вишлиста
Список избранного — отдельная страница в личном кабинете (/account/wishlist) или публичная при шаринге (/wishlist/{slug}).
- Грид товаров с кнопкой «Убрать»
- Фильтр по наличию, дате добавления, снижению цены
- Сортировка по дате, цене, изменению цены
- Batch-операция «Добавить всё в корзину»
- Статус наличия и сравнение цен (
price_at_additionvs текущая)
Счётчик на иконке вишлиста
В навигации — иконка сердца с бейджем. Бейдж обновляется мгновенно через store.
function WishlistNavIcon() {
const count = useWishlistStore(state => state.items.length);
return (
<div className="relative">
<HeartIcon className="w-6 h-6" />
{count > 0 && (
<span className="absolute -top-1 -right-1 bg-red-500 text-white text-xs rounded-full w-4 h-4 flex items-center justify-center">
{count > 99 ? '99+' : count}
</span>
)}
</div>
);
} Как настроить уведомления о снижении цены?
Пользователь может подписаться на уведомление об изменении цены товара в вишлисте:
price_alerts (
id,
user_id,
product_id,
threshold_type, -- 'any_drop' | 'percent_drop' | 'target_price'
threshold_value, -- для percent_drop: 10 (10%), для target_price: 2990
is_active BOOLEAN,
last_notified_at
)Scheduler раз в час проверяет условия и отправляет email через очередь. Частота уведомлений ограничена 3 днями.
// Scheduled job: CheckPriceAlerts
foreach ($alerts as $alert) {
$currentPrice = $alert->product->price;
$shouldNotify = match ($alert->threshold_type) {
'any_drop' => $currentPrice < $alert->product->previous_price,
'percent_drop' => ($currentPrice / $alert->product->previous_price - 1) <= -$alert->threshold_value / 100,
'target_price' => $currentPrice <= $alert->threshold_value,
};
if ($shouldNotify && $alert->last_notified_at < now()->subDays(3)) {
Mail::to($alert->user)->queue(new PriceDropNotification($alert->product, $currentPrice));
$alert->update(['last_notified_at' => now()]);
}
} Как интегрировать вишлист в ваш магазин?
- Определите сценарии использования: куплю позже, слежка за ценой, подарочные списки.
- Выберите стек: React/Vue на frontend, Laravel/Django на backend.
- Реализуйте анонимное хранение через localStorage и серверное для авторизованных.
- Настройте уведомления и шаринг.
- Протестируйте синхронизацию и оптимистичный UI.
Дополнительные возможности
Шаринг вишлиста — при включении генерируется share_token. Публичная страница только для просмотра, гость может добавить товары в корзину.
Интеграция с email-маркетингом — персонализированные рассылки для сегмента wishlist. Реализуется через асинхронные задачи и ESP.
SEO-аспекты — личные страницы закрыты авторизацией. Публичные shared-вишлисты — noindex.
Аналитика: отслеживаем популярность товаров, wishlist-to-purchase conversion rate (в среднем 15-20%), среднее время от добавления до покупки (3-7 дней). Средний чек при покупке из вишлиста на 20% выше, чем без него.
Процесс работы и сроки
| Этап | Описание | Ориентировочный срок |
|---|---|---|
| Аналитика | Сбор требований, выбор стека, проектирование схемы БД | 1–2 дня |
| Проектирование | UI/UX прототипы, архитектура API, описание интеграций | 2–3 дня |
| Реализация | Разработка frontend (React/Vue) и backend (Laravel/Django), интеграция уведомлений | от 2 недель |
| Тестирование | Unit- и e2e-тесты, кроссплатформенная проверка | 3–5 дней |
| Деплой и документация | Настройка CI/CD, написание документации, передача доступов, обучение команды | 2–3 дня |
Итоговые сроки: базовый вишлист (localStorage + одна кнопка) — 2–4 дня. Полноценный solution с сервером, уведомлениями и шарингом — от 2.5 до 4 недель.
Вы получаете работающий код, документацию по архитектуре, инструкцию для администратора и гарантийную поддержку 1 месяц.
Каждый проект уникален — окончательный срок и стоимость рассчитываются после аудита вашего текущего стека. Свяжитесь с нами для оценки вашего проекта. Закажите разработку вишлиста под ключ — просто напишите нам, и мы оценим ваш проект за один рабочий день.







