Автор: Павел Волков, тимлид
Скорость загрузки сайта и правильное считывание страниц роботами поисковых систем — краеугольный камень для успешного интернет-магазина, за эти показатели борются разработчики. Оба фактора влияют на ранжирование в поисковой выдаче, а отдельно скорость предопределяет поведение пользователей. Алгоритм наилучшего быстродействия и прохождения индексации программисты закладывают в архитектуре ит-продукта.
Сейчас мы работаем над e-commerce платформой для большой сети розничных магазинов. Это площадка с более чем 20-ю тысячами товаров. Привычное для нас и клиента решение — магазин на базе Битрикса — не подходило для проекта. И мы обратили свое внимание на Nuxt.
Почему Nuxt, а не Vue?
Nuxt — фреймворк на основе Vue.js для создания универсальных приложений — предлагает принципиально другой подход к работе с frontend-ом сайта.
Nuxt поддерживает SSR, server side rendering. Эта технология рендеринга сайта увеличивает скорость загрузки и позволяет поисковикам корректно индексировать страницы:
Скорость. Во Vue.js весь рендеринг страницы происходит на стороне браузера. За расчет и отрисовку графических элементов отвечает клиентская сторона — локальный браузер компьютера, телефона, планшета. Пользователь увидит страницу сайта только после полной подгрузки скриптов. SSR же перекидывает рендеринг на сервер, а клиентской стороне отдает сначала HTML-страницу, она сразу отображается у пользователя в браузер, потом подгружает скрипты. То есть пользователь взаимодействует с контентом раньше, чем при разработке на Vue.js. Очевидно, что нагрузка на браузер меньше, скорость загрузки быстрее.
Seo. Для продвижения важнее всего первая загрузка сайта. Если поисковой робот зайдет на сервис на чистом Vue.js, то первым увидит пустой HTML-документ без контента. Потому что при начальной загрузке подключился только js скрипт, где записано все приложение, а уже потом скрипт динамически подгрузил сайт. Роботы не поняли, что это за ресурс, ушли, интернет-магазин остался без проиндексированных страниц. Рендеринг на сервере, как в Nuxt, при первой загрузке сразу формирует понятный HTML. Поисковики воспринимают страницу как обычный документ со всеми разделами, контентом, метаданными и корректно индексируют сайт.
Связка с Битриксом
В чем была главная загвоздка сделать интернет-магазин полностью на Битриксе? Страница загружалась полностью при любом обновлении. Для всех элементов формировались запросы и отправлялись в базу данных. В не зависимости должен поменяться футер, хедер или контент после пользовательского действия или нет — из базы данных прилетает HTML и меняет весь интерфейс. Да, поисковые роботы всегда смогут просканировать HTML и понять содержимое страницы, но скорость загрузки из-за постоянного обновления очень хромала. Можно кэшировать отдельные части, чтобы не делать запросы к базе данных. В теории это увеличит скорость. Но нам все равно придется получать HTML этих данных из кэша и отправлять их на front. Все операции требуют ресурсов.
Поэтому frontend мы отдали Nuxt-у, а Битрикс остался на backend-е. Он предлагает огромное количество готовых функций для интернет-магазинов: эквайринг, кассы, скидки, остатки, склады. Кастомная реализация таких инструментов возможна, допустим на Laravel, но требует больше времени. Поэтому Битрикс используем на backend-е и взаимодействуем с ним через REST API.
Финальный аргумент в пользу связки Nuxt+Битрикс — полное разделение frontend и backend разработки. Во-первых, программисты могут выбрать один вектор работы и полноценно развиваться в нем. Во-вторых — тестирование. Разграничение позволит проводить интеграционные или unit-тесты отдельно для front-а и back-а, единичных сервисов, что в Битрикс сделать крайне сложно и неудобно.
Сложности
Основной сложностью стала длительность первой загрузки страницы. Мы рассчитывали на скорость быстрее Битрикса, а вышло дольше. Почему?
Предположим, что для первой страницы нужно обработать три запроса:
список товаров по фильтру;
список фильтров для товаров;
список разделов.
Если бы интернет-магазин был полностью на Битрикс, то процесс передачи данных выглядел так. Битрикс загружает данные по первому запросу, выполняется РНР скрипт, который идет последовательно: инициализирует классы, кэширует значения. Обработка первого запроса будет долгой, предположим, 0,15 секунды: 0,12 — инициализация ядра, 0,3 — запрос. Второй и третий будет обработан быстрее, потому что есть кэш. Предположим по 0,3 секунды. В сумме получаем загрузку первой страницы за 0,21 секунды.
Для нашей связки Nuxt+Битрикс был план — реализовать REST API, опираясь на лучшие практики. Каждый блок на странице отправляет свой запрос к Битриксу. Но получить блоки мы могли тоже только по отдельности. На формирование страницы целиком уходило от трех до пяти запросов. То есть: первый запрос — Битрикс инициализирует ядро и кэширует переменные, отдает Nuxt-у, второй запрос — Битрикс инициализирует ядро и кэширует переменные, отдает Nuxt-у, третий запрос — Битрикс инициализирует ядро и кэширует переменные, отдает Nuxt-у и так далее.
Получается, что загрузка первой страницы в связке Битрикс+Nuxt занимала 0,45 секунды. Это была глобальная проблема. Долгая первая загрузка — плохая индексация роботами, быстрый отток пользователей.
Решение проблемы нашли в объединении. Мы организовали отдельный слой REST API, который группирует запросы. От Nuxt к back-у идут не отдельные просьбы “дай мне разделы по фильтру X”, “дай мне список товаров по фильтру X”, а одна — “дай мне страницу товаров, в которую входят товары и разделы по фильтру X”. Внутри основного REST API, которое обращается напрямую к Битриксу, базе данных, запросы разбиты в соответствии с лучшими практиками.
В итоге первая загрузка интернет-магазина происходит со скоростью Битрикса, а последующие намного быстрее.
Реактивное приложение
С первой загрузкой разобрались, а что с последующими? Архитектура, которую позволяет выстроить Nuxt, создает не просто интернет-магазин, а реактивное приложение. Это значит, что работа с дальнейшими загрузками страниц сводится к чистому Vue: обновляются части приложения, требующие изменения, только для них отправляются запросы на back.
Например, на сайте есть футер и хедер. Они меняются в крайнем случае при авторизации. Если вы читайте блог, листайте каталог, они статичны, а меняющийся контент обновляется через легкий JSON. В нем передаются только данные на замену, без HTML, js, то есть без верстки.
Не перезагружая страницу можно обновлять и отдельные блоки. Например, список товаров. По клику на раздел верхняя, нижняя и правая части страницы, сайдбар с меню каталога не меняются. Обновляются только конкретные данные товарной сетки: название, цена, скидка, картинка.
Оправдан ли такой подход?
Да. В итоге закрыты все задачи проекта:
разработчики разделены на бекэндеров и фронтэндеров;
доступно удобное и разнообразное тестирование;
скорость загрузки уменьшилась;
seo-критерии соблюдены.
Вместо компонентного подхода, мы используем свой. Он через REST API организует архитектуру, удобную для конкретного проекта:
Route: это маршрут, URL к которому можно обратиться разными http методами. В нем подключаются мидлвары валидации, авторизации, возможно некоторые изменения входных параметров. Дальше вызывается Controller.
Controller: группирует запросы к сервисам, выполняющие различные действия.
Service: здесь описывается вся логика, взаимодействие с базой данных, расчеты.
Это решение подходит для больших e-commerce проектов, с высокой нагрузкой. Nuxt делает разработку немного дольше и требует от программиста уровня выше джуниора. Битрикс же позволяет быстро сделать небольшой магазин, с небольшой нагрузкой силами джуниоров.
Перспектива
REST API упрощает масштабирование системы. Предположим, что клиент захочет запустить мобильное приложение, личный кабинет, промо-сайт отдельной марки. Для этих ресурсов не придется писать back заново, можно спокойно или с минимальными доработками подключиться к REST API интернет-магазина. Back становится полноценной базой данных для любых цифровых ресурсов клиента.
То же происходит и с редизайном. Не нужно лезть в Битрикс и разбирать весь проект, достаточно переписать и перезапустить часть Vue. Или со сменой технологий. Для перехода на новый стек не нужно трогать Nuxt, достаточно переделать REST API и перенаправить запросы. Это позволяет системе быть гибкой.