Разработка блока «Недавно просмотренные» для интернет-магазина

Разработка блока «Недавно просмотренные» для интернет-магазина

Разработка и обслуживание любых видов сайтов:

Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Услуги, которые мы предлагаем
Показано 1 из 1Все 2062 услуг
Разработка блока «Недавно просмотренные» для интернет-магазина
Простой
~1 день

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

Часто задаваемые вопросы

Последние работы

  • image_website-b2b-advance_0.webp
    Разработка сайта компании B2B ADVANCE
    1419
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1287
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    984
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1248
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    984
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Разработка веб-сайта для компании ФИКСПЕР
    998

Разработка блока «Недавно просмотренные» для интернет-магазина

Представьте: пользователь просматривает товар за товаром, но при возврате на страницу категории не может найти тот самый кед или кофеварку. История просмотров — это не просто фича, а инструмент возврата. Однако её реализация часто страдает от дублирования в массиве ID, N+1 запросов и проблем с порядком товаров. Мы интегрируем блок «Недавно просмотренные» с клиентским кешированием в localStorage для гостей и серверной синхронизацией для авторизованных пользователей, используя один batch-запрос для загрузки данных. Разработка занимает 1–2 дня, а возвраты к карточкам вырастают до 15%. Свяжитесь с нами — оценим ваш проект за день.

Хранение истории в localStorage

Для неавторизованных пользователей история хранится в localStorage. Мы используем хук useRecentlyViewed, который при каждом просмотре добавляет ID товара в начало массива, удаляя дубликаты. Максимум — 20 записей. Вот реализация:

// hooks/useRecentlyViewed.ts const STORAGE_KEY = 'recently_viewed'; const MAX_ITEMS = 20; export const useRecentlyViewed = () => { const [items, setItems] = useState<number[]>(() => { const stored = localStorage.getItem(STORAGE_KEY); return stored ? JSON.parse(stored) : []; }); const addProduct = useCallback((productId: number) => { setItems(prev => { const filtered = prev.filter(id => id !== productId); const updated = [productId, ...filtered].slice(0, MAX_ITEMS); localStorage.setItem(STORAGE_KEY, JSON.stringify(updated)); return updated; }); }, []); const clearHistory = useCallback(() => { localStorage.removeItem(STORAGE_KEY); setItems([]); }, []); return { productIds: items, addProduct, clearHistory }; }; 

На странице товара вызываем addProduct(product.id).

Серверная синхронизация для авторизованных

Если пользователь авторизован, история синхронизируется с сервером через Sanctum. При логине мы мержим localStorage с серверной историей, при закрытии вкладки отправляем текущее состояние через navigator.sendBeacon. Эндпоинт для синхронизации:

// routes Route::middleware('auth:sanctum')->post('/me/recently-viewed', [RecentlyViewedController::class, 'sync']); Route::middleware('auth:sanctum')->get('/me/recently-viewed', [RecentlyViewedController::class, 'index']); public function sync(Request $request): JsonResponse { $request->validate(['product_ids' => 'required|array|max:20', 'product_ids.*' => 'integer']); $user = $request->user(); // Обновляем порядок: переданный список — актуальное состояние $user->recentlyViewed()->sync( collect($request->product_ids)->mapWithKeys(fn($id, $pos) => [ $id => ['position' => $pos, 'viewed_at' => now()] ]) ); return response()->json(['synced' => count($request->product_ids)]); } 

Такой подход сохраняет историю между устройствами и не теряет данные при смене браузера.

Batch-загрузка данных

История хранит только product_id. Для отображения блока нужны данные — один batch-запрос к API. Мы используем React Query с staleTime 5 минут, чтобы не перегружать сервер при повторных посещениях:

const RecentlyViewedBlock = () => { const { productIds } = useRecentlyViewed(); const visibleIds = productIds.slice(0, 8); const { data: products } = useQuery({ queryKey: ['recently-viewed-products', visibleIds], queryFn: () => api.get('/products/batch', { params: { ids: visibleIds.join(',') } }), enabled: visibleIds.length > 0, staleTime: 300_000, }); if (!products?.length) return null; // Сохраняем порядок из истории const ordered = visibleIds .map(id => products.find((p: Product) => p.id === id)) .filter(Boolean); return ( <section> <div className="flex justify-between items-center mb-4"> <h2 className="text-lg font-semibold">Вы недавно смотрели</h2> <button onClick={clearHistory} className="text-sm text-gray-400 hover:text-gray-600">Очистить историю</button> </div> <ProductCarousel products={ordered} /> </section> ); }; 

Endpoint загружает ровно те поля, которые нужны для карточки, и возвращает товары в порядке переданных ID:

public function batch(Request $request): JsonResponse { $request->validate(['ids' => 'required|string']); $ids = array_filter(array_map('intval', explode(',', $request->ids))); $ids = array_slice($ids, 0, 20); $products = Product::whereIn('id', $ids) ->where('is_active', true) ->select(['id', 'name', 'slug', 'price', 'sale_price', 'rating_avg', 'rating_count']) ->with('thumbnail') ->get() ->keyBy('id'); $ordered = collect($ids)->map(fn($id) => $products->get($id))->filter()->values(); return response()->json(ProductCardResource::collection($ordered)); } 

Почему batch-загрузка быстрее?

Если запрашивать каждый товар отдельно, получите N запросов к БД — для 8 товаров это 8 раунд-трипов. Batch-эндпоинт возвращает все за один раз, что снижает нагрузку на сервер в 8 раз. На клиенте мы сохраняем порядок через сортировку по исходному массиву ID. Такой подход ускоряет загрузку блока в 3–5 раз, а блокочие метрики (LCP, FID) не страдают.

Подход Время загрузки (8 товаров) Нагрузка на сервер
N+1 запросы 800–1200 мс 8 запросов
Batch-запрос 150–300 мс 1 запрос

Как исключить текущий товар?

На странице товара X из блока «Недавно просмотренные» исключается сам товар X — иначе он неизбежно окажется первым в списке. Делается простым фильтром: productIds.filter(id => id !== currentProductId). Это предотвращает ситуацию, когда пользователь видит карточку того же товара рядом с основным контентом.

Сравнение методов синхронизации

Метод Гости Авторизованные Скорость Серверная нагрузка
Только localStorage Да Нет Мгновенно Нет
Серверная Нет Да ~5 мс Средняя
Гибрид (localStorage + сервер) Да Да Мгновенно + 5 мс Низкая

Гибридный подход — оптимальный выбор: он сочетает скорость локального хранилища с устойчивостью серверного.

Где размещать блок

  • Главная страница: для вернувшихся пользователей — вместо или рядом с популярными товарами
  • Страница категории: в нижней части, после основной сетки товаров
  • Страница товара: под блоком «Похожие товары»
  • Корзина: в боковой панели на десктопе
  • Пустые результаты поиска: «Может, вы ищете что-то из просмотренного?»

Приватность и контроль

Кнопка «Очистить историю» даёт пользователю контроль над данными. Срок хранения в localStorage можно ограничить, добавив TTL вручную — например, 30 дней. Подробнее о localStorage читайте в документации MDN.

Типичные ошибки при реализации
  • Не исключают текущий товар из блока — пользователь видит его первым.
  • Используют N+1 запросы вместо batch — загрузка блока тормозит.
  • Не очищают историю при логине — данные гостя смешиваются с данными пользователя.
  • Синхронизируют историю слишком часто — избыточная нагрузка на сервер.

Что входит в работу

  • анализ текущей архитектуры и мест размещения блока
  • реализация хука useRecentlyViewed с localStorage
  • API-эндпоинты для серверной синхронизации (Laravel)
  • batch-загрузка данных с кешированием на клиенте
  • UI-компонент блока (карусель) с кнопкой очистки
  • инструкция по деплою и тестированию

Наш опыт — более 50 проектов с историей просмотров. Гарантируем корректную работу на всех этапах. Свяжитесь с нами, чтобы обсудить ваш проект — оценим его за день. Закажите реализацию блока «Недавно просмотренные» и получите прирост конверсии без сложных интеграций.