Главная/Блог/Каталог на 100 000 товаров: делаем быстрый интернет-магазин на Vue и Nuxt

Каталог на 100 000 товаров: делаем быстрый интернет-магазин на Vue и Nuxt

Каталог на 100 000 товаров: делаем быстрый интернет-магазин на Vue и Nuxt
Каталог — главная страница прибыли интернет-магазина: через него проходит 60–80% трафика и почти все транзакции. Но как только ассортимент переваливает за 50–100 тысяч SKU, наивная реализация «получили массив — отрисовали» начинает убивать конверсию: страница грузится по 5–8 секунд, смартфоны перегреваются, а поисковики понижают позиции. По данным Google, при увеличении времени загрузки с 1 до 3 секунд вероятность отказа растёт на 32%, а до 5 секунд — на 90%. Хорошая новость: Vue 3 и Nuxt 3 спокойно вытягивают каталог любого размера, если правильно применить три инструмента — пагинацию, виртуальный скролл и многоуровневое кэширование. Разберём каждый на практике, с цифрами.

Почему каталог на 100 000 товаров «тормозит»: три типовые ошибки

Проблема почти никогда не во Vue — она в архитектуре данных. Первая ошибка — отдавать клиенту весь список: ответ API на 100 000 позиций весит 30–50 МБ и блокирует главный поток браузера на несколько секунд. Вторая — рендерить всё в DOM: браузер создаёт десятки тысяч узлов, и на среднем смартфоне страница «замерзает» уже на 3–5 тысячах карточек. Третья — ходить в базу данных за каждым запросом без кэша: под нагрузкой в 200–300 одновременных пользователей MySQL отвечает за 1–2 секунды вместо 50 мс. Каждую из этих проблем решает свой инструмент, но максимальный эффект они дают только в связке.

Пагинация: классика, от которой нельзя отказываться

Пагинация — единственный способ отдать и пользователю, и поисковику понятную структуру каталога. Чтобы она работала на скорость и SEO, придерживайтесь схемы:
  1. Ограничьте страницу 24–48 товарами — этого достаточно для выбора и не раздувает вес HTML.
  2. Страницы категорий и пагинации до 3–4 уровня отдавайте через SSR — так они попадают в индекс Яндекса и Google.
  3. Настройте канонические URL и добавьте все страницы пагинации в sitemap.xml, иначе индексация упрётся в первые несколько страниц.
  4. Подгружайте следующую страницу заранее (prefetch), пока пользователь читает текущую, — переход будет мгновенным.
  5. Для мобильных добавьте кнопку «Показать ещё» поверх пагинации: она подгружает следующие 24 товара без перезагрузки, а в URL подставляется ?page=N.
Так вы получаете SEO-дружественную структуру и бесшовный пользовательский опыт одновременно.

«Бесконечный скролл» и «Показать ещё»: что это даёт конверсии

Кнопка «Показать ещё» в среднем повышает глубину просмотра каталога на 20–30%: пользователю не нужно целиться в мелкие цифры страниц. Но у бесконечной ленты есть обратная сторона — она скрывает футер с контактами, ломает кнопку «назад» и усложняет индексацию. Поэтому рабочая схема для крупных магазинов: пагинация как основа плюс подгрузка внутри страницы. Важно, чтобы каждая подгрузка отражалась в URL (page=2, page=3) — тогда ссылкой можно поделиться, а поисковик увидит все страницы. Если «хвост» ассортимента длинный, ограничьте ленту 5–10 подгрузками и мягко предлагайте уточнить фильтры.

Виртуальный скролл: 20 карточек в DOM вместо 10 000

Виртуальный скролл (windowing) рендерит только видимые на экране элементы плюс небольшой буфер — обычно 20–40 карточек вместо тысяч. В экосистеме Vue для этого есть vue-virtual-scroller и встроенный v-virtual-scroll в Vuetify: при прокрутке компонент пересчитывает смещения и подставляет нужные элементы, удерживая стабильные 60 fps даже на слабых устройствах. Сильные сценарии — результаты живого поиска, админ-панели, разделы «все товары магазина» и бесконечные подборки. Но для SEO-страниц категорий виртуальный скролл — не замена пагинации: краулер не станет скроллить 10 000 позиций. Оптимальная связка — SSR-страницы пагинации для поисковиков и виртуализированные списки внутри интерактивных интерфейсов.

Nuxt и SSR: чтобы магазин индексировался и быстро открывался

Для каталога с тысячами страниц SSR в Nuxt — не опция, а необходимость: Яндекс по-прежнему плохо индексирует чистые SPA, а Google делает это медленнее. Настройте routeRules с режимом swr или isr: HTML популярных категорий кэшируется и отдаётся за 30–80 мс, а обновляется в фоне. Обязательно урезайте payload: карточке в списке нужны 10–15 полей (цена, название, фото, наличие), а не все 100 из базы — используйте pick в useFetch. Ниже первого экрана включайте ленивую гидрацию, чтобы JavaScript не блокировал взаимодействие. В результате TTFB держится в пределах 200–400 мс, а LCP укладывается в рекомендованные Google 2,5 секунды.

Кэширование на трёх уровнях: ускорение в 10–50 раз

Кэш — самый дешёвый способ ускорения: правильно настроенные слои убирают 80–90% обращений к базе. Рабочая конфигурация:
  • CDN и HTTP-кэш: SSR-HTML категорий и карточек кэшируется на CDN с TTL 5–15 минут и политикой stale-while-revalidate — пользователь получает мгновенный ответ, обновление идёт в фоне.
  • Серверный кэш Redis: ответы API, счётчики фасетов и меню категорий хранятся в памяти — выборка занимает 1–5 мс против 100–300 мс у SQL-запроса со сложными JOIN.
  • Клиентский кэш: useFetch с ключами и getCachedData не запрашивает повторно уже полученные списки при переходах «назад-вперёд».
Ключевое — стратегия инвалидации: сбрасывайте кэш по событиям (изменилась цена, поступил товар, обновилась категория), а не по таймеру «на всякий случай». В наших проектах cache hit rate выше 90% — база данных практически не видит трафика каталога.

Фильтры и фасеты: второй по тяжести узел каталога

Пагинация ускоряет выдачу, но пользователь в каталоге на 100 000 приходит именно за фильтрами. Считать количество товаров по каждому сочетанию фильтров запросами к MySQL — путь к таймаутам: на миллионах строк такой запрос занимает от 500 мс до 2 секунд. Решение — специализированный поисковый движок (Elasticsearch, Meilisearch или Typesense), который отдаёт фасеты с готовыми счётчиками за 10–50 мс. Фильтры должны жить в URL (?brand=samsung&price=20000-50000) — это и шаринг ссылок, и посадочные SEO-страницы под запросы вида «купить смартфон 512 Гб». Добавьте debounce 300–500 мс на ввод и скелетоны вместо прыгающего лейаута — воспринимаемая скорость вырастет заметнее, чем от лишних 200 мс в API.

Чек-лист: целевые цифры быстрого каталога

Сверяйте результат с конкретными метриками, а не с ощущениями:
  • LCP < 2,5 с и INP < 200 мс — пороги Core Web Vitals, ниже которых Google не режет позиции.
  • TTFB < 200–400 мс на страницах категорий с учётом кэша SSR.
  • Ответ API со списком товаров < 200 мс, вес ответа < 100 КБ на страницу.
  • Не более 40 карточек в DOM до активации виртуализации.
  • Cache hit rate > 90% на операции чтения каталога.
  • Первый экран мобильного каталога — до 500 КБ JavaScript.
Проверяйте не в офисном Chrome на топовом MacBook, а в Lighthouse с эмуляцией среднего Android и сети 4G — именно там живут ваши покупатели.
Каталог на 100 000 товаров — это не проблема Vue и Nuxt, а вопрос архитектуры: пагинация с SSR даёт индексацию и быстрый первый экран, виртуальный скролл удерживает 60 fps в интерактивных списках, а трёхуровневый кэш снимает нагрузку с базы и режет TTFB в разы. Инструменты дополняют друг друга, и внедрять их лучше на этапе проектирования — доработка кэширования в работающем магазине занимает больше времени и денег. Если хотите узнать, сколько миллисекунд теряет ваш каталог, и получить план оптимизации с конкретными цифрами — напишите нам: разберём проект и покажем узкие места до того, как они начнут стоить вам продаж.

Нужна разработка?

Оставьте заявку — обсудим ваш проект и предложим решение