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

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

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, 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 записів. Хоча localStorage має ліміт 5-10 МБ, типовий розмір масиву не перевищує 1-2 КБ. Ось реалізація:

// 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 хвилин, щоб не перевантажувати сервер при повторних відвідуваннях. Такий підхід зменшує запити на 80%:

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. Batch-запит працює в 4–6 разів швидше за N+1. Такий підхід прискорює завантаження блоку в 3–5 разів, а блокові метрики (LCP, FID) не страждають. Наприклад, час завантаження блоку з 8 товарами зменшується з 1000 мс до 200 мс. Кешування з React Query краще за повторні запити в 3-5 разів.

Підхід Час завантаження (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 — завантаження блоку гальмує.
  • Не очищають історію при вході — дані гостя змішуються з даними користувача.
  • Синхронізують історію занадто часто — надлишкове навантаження на сервер.

Що входить в роботу

  1. Аналіз поточної архітектури та місць розміщення блоку.
  2. Реалізація хука useRecentlyViewed з localStorage.
  3. API-ендпоінти для серверної синхронізації (Laravel).
  4. Batch-завантаження даних з кешуванням на клієнті.
  5. UI-компонент блоку (карусель) з кнопкою очищення.
  6. Інструкція з деплою та тестування.

Наш досвід — понад 50 проектів з історією переглядів. Гарантуємо коректну роботу на всіх етапах. Зв'яжіться з нами, щоб обговорити ваш проект — оцінимо його за день. Замовте реалізацію блоку «Нещодавно переглянуті» і отримайте приріст конверсії без складних інтеграцій.