Хабрахабр

Опыт разработки SPA на VueJS + Nuxt

Наша компания занимается преимущественно разработкой интернет-магазинов и мы хотим поделиться своим опытом разработки проекта на связке VueJS + Nuxt + Laravel.
В статье пойдет речь про то, как мы решили реализовать интернет-магазин как SPA: как мы к этому пришли, трудности, легкости.
Сайт, про который пойдет речь, представляет из себя почти классический интернет-магазин с каталогом, фильтрами, поиском, корзиной, личным кабинетом т.е. почти все, что может быть в магазине. Проект имеет разную логику, ценообразование и отображение для юридических и физических лиц.

Почему SPA

До текущего проекта наш опыт разработки одностраничных приложений заключался только в нескольких внутренних проектах. И во многом SPA для нас оставалось в каком-то смысле темной лошадкой.
Не было четкого понимания проблем, связанных с ростом проекта, его seo-продвижением и стабильностью, когда как опыт и уже давно отлаженные процессы в создании обычных сайтах не вызывали никаких вопросов в решении этих задач.

Нашими разработчиками было принято решение собрать прототип нескольких страниц проекта и посмотреть, какие возникнут трудности при каждом из подходов. Выбор подхода вызвал в нашей компании достаточно жаркие споры, обе чаши весов с аргументами были наполнены и решение давалось очень сложно. Прототипы помогли показать, что управление состоянием сайта (каталог, корзина, оформление заказа и т.д.) намного более комфортно и вызывает меньше проблем именно в SPA версии. Этот подход помог нам с итоговым решением. Также стали более понятны проблемы с которыми мы можем столкнуться и это сподвигло к дальнейшим действиям. Скорость разработки и взаимодействия между верстальщиками и программистами значительно увеличилась благодаря тому, что не нужно переносить верстку, достаточно просто добавлять логику в уже готовые компоненты. Перед нами стал выбор технологий.

В twitter и на medium ни утихают споры, что все-таки лучше, vue или react. За окном лето 2017. Разработчики так же разделились на два лагеря, каждый со своими аргументами. Наш офис этот тренд не обошел стороной. С другой стороны каждый фреймворк предоставляет нам возможность создавать однофайловые компоненты и для обоих есть уже достаточно стабильные библиотеки с набором всех нам нужных функций (ssr, управление глобальным состоянием, роутинг, управление meta-данными). До этого каждый из нас уже работал с обеими технологиями.
Кому-то стал ближе jsx, кто-то предпочитает более привычный html или pug, кто-то считает что иммутабельность помогает лучше следить и управлять состоянием приложения, кому-то это кажется избыточным усложнением. Nuxt на момент выбора был еще в beta-версии, но достаточно стабилен. Для react это nextjs, а для vue — nuxtjs. процесс разработки у нас был построен таким образом, что изначально у нас идет верстка, а затем уже построение backend части и перенос сверстанных страниц во frontend, выбор фреймворка был достаточно прост. Т.к. решено было параллельно верстать сайт и запускать api. Нами был выбран vue и nuxtjs, т.к. Нашим верстальщикам был ближе подход создания привычного им html. При таком подходе удобно верстать сразу компоненты и в них уже добавлять логику.

Немножко о backend

В плане серверных решений и в целом выбора технологий для построения backend мы пошли более привычным путем. Языком был выбран php, для которого мы используем фреймворк laravel. Это все крутится на nginx. В качестве решения для базы данных у нас mysql.

Начало разработки, используемые пакеты и проблемы

Nuxt предоставляет полностью удовлетворяющие нас пакеты для управления состоянием приложения (vuex) и роутинга (vue-router). Поэтому начинать собирать проект и прикручивать к компонентам логику можно было начинать сразу, а далее по мере надобности искать нужные нам пакеты. В первую очередь, конечно же, понадобилось решение для общения с backend частью. Для этого был выбран, стандартный уже для всех, axios, и обертка над ним nuxt-axios-module. Так же сразу помогаем проекту не потеряться в окружениях и запускать в каждом окружении с нужной конфигурацией — выбираем dotenv и обертку nuxt-dotenv-module. Для начала разработки этого достаточно и процесс верстки начался.

“Где мой slick-slider, я хочу jquery” было слышно из верстальщицкого конца комнаты. Первая пауза случилась, когда нужно было добавлять в верстку слайдер изображений. Но практически все тянули за собой зависимость в виде jquery, которую не хотелось добавлять в готовый бандл, тем самым увеличивая его размер. Быстрый обзор готовых решений выявил несколько подходящих нам слайдеров. В итоге выбор пал на awesome-swiper, который полностью соответствовал нашим требованиям и даже чуть больше. Какие-то пакеты не поддерживали серверный рендеринг, что тоже было важно для нас. “Это и все, мне больше ничего не нужно делать? После того как слайдер был прикручен, наши верстальщики еще долгое время оставались в недоумении. Просто указать список изображений и это работает?”

Тут повезло больше, т.к. Далее встал выбор компонента для выбора дат. Оставалось только немного его стилизовать. обертка для любимого нашими верстальщиками flatpickr нашлась быстро.

Но, т.к. В нескольких местах на сайте присутствует карта. Тем не менее на момент разработки, да и сейчас, решений которые идеально покрывают все наши потребности нет. нам не нужно была идеальная детализация и проработанность карты, выбор между сервисами не стоял. Пакет имеет достаточно большой размер и тянет за собой много ненужного нам, но свои задачи решает хорошо. Исходя из всех плюсов и минусов был выбран google maps и обертка vue2-google-maps.

Пользователю нужно помогать вводить телефон, так как вариантов формата слишком много, да и работать потом в будущем с данными введенными в едином формате проще. В некоторых формах у нас присутствуют поля для ввода телефона. Хотелось использовать уже привычную text-mask, и тут нам повезло, у них было уже решение для vue — vue-text-mask. Поэтому нужна маска.

Оставалось только отслеживать клики вне компонента, в чем на помог vue-click-outside. Эти пакеты покрыли практически все наши требования. Для работы с датами используем moment. Быструю прокрутку вверх страницы мы реализовали с помощью vue-backtotop.

Итоговый размер bundle и откуда взялся 1 мегабайт

Стоит учитывать, что важным критерием при выборе пакетов являлся их вес.
В середине проекта мы решили провести анализ итогового проекта и посмотреть размеры сборки. Результаты наc мягко говоря удивили. Размер банда app.js составлял чуть большее 950kb gzip. Команда npm run analyze вывела нам красивый график с размером всех модулей, из которого мы поняли, что некоторые модули тянут за собой ненужные нам зависимости в виде jquery, lodash и т.д. От этих пакетов пришлось отказаться и найти им альтернативу. На текущий момент размер всего бандла составляет 480kb gzip.

Следите за зависимостями и периодически проверяйте размер вашего приложения.

Первоначальная загрузка страница и данные, получаемые по api

Nuxt предоставляет удобную возможность наполнить store данными на серверной стороне до загрузки клиента. Для этого используется action nuxtServerInit. У нас это выглядит так:

Так как сервер отдает все полученные данные на клиент для первоначальной отрисовки, размер html может быть слишком большим. Так как категории и некоторые другие сущности у нас используются сразу в нескольких компонентах, нам было удобнее получить их сразу и положить в store.
Но тут возникает проблема с размером json, который вы получаете. Размер json составлял более 2mb. Мы с этим столкнулись, когда в категориях начали еще передавать ненужные нам на всех страницах изображения, описание и другие поля принадлежащие каждой категории. К счастью это легко поправить, убрав ненужные поля из данных, которые отдает сервер.

Утечки памяти

Спустя некоторое время работы приложения на нашем тестовом сервере мы начали наблюдать неестественный рост потребления памяти. pm2 занимал до 90% всей памяти сервера и приложение периодически падало. На github странице nuxt уже висело несколько issue с такой же проблемой https://github.com/nuxt/nuxt.js/issues/805.
Проблема возникала когда мы в методе asyncData наших страниц делали несколько реквестов.

К счастью, эту проблему разработчики nuxt достаточно быстро решили, и на текущий момент процесс потребляет около 40mb памяти.

Интересные проблемы и их решения

Статьи с компонентами
В панели управления сайтом есть возможность добавлять статьи и вставлять в содержание статьи различные блоки, например блок с товаром.

HTML который приходит с сервера выглядит примерно так:

Vue нам предоставляет широкие возможности рендеринга компонента с помощью метода render. $product-4 указывает на то, что на месте этого указателя должен находится компонент Product.vue с идентификатором 4. Далее разбиваем весь html на дерево. Сначала ищем все упоминания указателей на компоненты в пришедшем html и получаем по api данные, нужные для отображения этого компонента. И затем собираем обратно html заменяя указатели на уже готовые компоненты. В этом нам помогла библиотека himalaya.

… А больше сил писать статью не хватило) Статью начинали писать летом 2017 по ходу разработки проекта, а на дворе уже лето 2018, проект запущен, а статья не выпущена.
Поэтому публикуем то, что насобирали, но у нас еще много интересных тем, наблюдений.
Если будет интересно — пишите, ставьте лайки) Ну и о чем было бы интересно еще услышать, что упустили.

Особых проблем с ним не замечено, а скорость работы сайта впечатляет и выгодно выделяет его на фоне других магазинов. Хочу сказать, что проект работает уже около 4 месяцев.

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть