Интернет-магазин на vue.js + nuxt.js + apisful

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

  • посетители смогут фильтровать товары по категории
  • будут загружать только изображения, подходящие размеру и плотности их экрана
  • будут добавлять товары в корзину и оформлять заказы
  • мы будем получать email-уведомления о новых заказах

Большие туториалы с кучей кода никто не читает, поэтому все исходники лежат на github, а в этой статье мы их комментируем, объясняя потенциально непонятные моменты.

Все используемые в этом материале сервисы бесплатны.

Исходники здесь Живое демо тут

Данные и бэкенд

В рамках туториала, вероятно, было бы достаточно сложить объекты товаров в массив, но мы сразу хотим сделать так, чтобы этим всем можно было пользоваться при создании реального магазина. REST API и интерфейс администрирования для нас сгенерирует Apisful - там буквально пара кликов, магия и все готово.

[click on the GIF to pause]
  1. Создаем аккаунт здесь или авторизуемся тут.
  2. Жмем create new project
  3. Вводим название проекта и обязательно выбираем шаблон E-Commerce - тогда все создастся само и нам не придется делать лишних кликов.
  4. Жмем save

Как результат имеем следующие коллекции (это как таблицы в базе данных):

  • Categories - туда добавляем категории товаров, которые у нас будут появляться: штаны, маски для дайвинга и вот это вот все.
  • Products - это базовый товар: футболка c v-образным вырезом, например.
  • Product Variants - это вариации базового товара. Например, футболка c v-образным вырезом оливкового цвета.

Суть Products и Product Variants в том, что все футболки c v-образным вырезом стоят одинаково и находятся в одной категории, но футболки фиолетового цвета остались только в размере S и М, тогда как оливковые есть во всех размерах - характеристики размера мы выносим в Product Variants. Это позволит нам изменять список доступных размеров при выборе цвета футболки на сайте. Вот так:

На этом бэкенд пока откладываем и движемся дальше

Vue.js + Nuxt.js

Создаем проект

Сам по себе vue.js хоть и очень классный, но занимается только одной конкретной проблемой - слоем отображения. Нам же нужно еще:

  • обрабатывать переходы на страницы - в этом нам поможет vue router
  • писать веб компоненты - для этого есть vue loader
  • иметь локальный сервис, в который смогут вносить изменения все компоненты сайта (для создания корзины заказа) - vuex знает в этом толк

Это все отдельные библиотеки, которые нужно собрать вместе в дополнение к vue.js. Собирать сами мы, разумеется, ничего не хотим, поэтому воспользуемся nuxt.js - это фреймворк, который уже собрал все необходимые vue библиотеки вместе и позволит нам просто взять и начать кодить.

Создать nuxt.js проект невероятно просто

yarn create nuxt-app <project-name>
# или
npx create-nuxt-app <project-name>
# или
npm init nuxt-app <project-name>

После вызова одной из команд выше, nuxt.js начнет задавать нам всякие каверзные вопросы (а мы на них ответим):

  • как назвать проект (на ваше усмотрение)
  • js мы хотим или ts (в этом руководстве используем js)
  • какой пакетный менеджер будем использовать (будем yarn)
  • какой css фреймворк желаем (никакого нам не надо)
  • и какие модули нам установить (только axios)
  • как насчет линтера (тут выбирайте на свой вкус)
  • тестировать будем? (пока нет)
  • какую сборку собирать (Single Page App)
  • как будем деплоить (Static/JAMStack hosting)

После этой непродолжительной беседы, nuxt.js сделает выводы и как результат у нас появится папка с проектом. Вот так она выглядит

vue-cli-basic-files-tree

Там есть все, что нам необходимо и по ходу туториала многим мы воспользуемся. Остается только один момент: мы пишем стили на sass, а значит нужно установить пакеты, которые этот самый sass будут компилировать в css (автоматически, конечно):

yarn add --dev node-sass sass-loader

Страницы и компоненты

Все страницы находятся в директории pages. Стоит создать в папке pages файл с расширением .vue и nuxt.js сразу же сгенерирует для него соответствующий route. О роутах подробнее можно почитать здесь.

Файл pages/index.vue является главной страницей сайта.
Файл pages/categories/_category.vue соответствует url в духе /categories/t-shirts-Xmw1kx5.

Эти две страницы (главная и категории) выглядят одинаково с точки зрения UI, отличается только список товаров. Поэтому мы создали компонент components/products-list/CommonProductsListPage.vue, который проделывает всю работу по отображению элементов страницы.

В коде главной страницы и страницы категории мы используем этот компонент, передавая в него функцию выборки товаров.

В компоненты стоит выносить все то, что вы собираетесь переиспользовать или логику чего вы хотите вынести отдельно. Вот такие компоненты есть у нас в этом проекте

список компонентов проекта

Выборка товаров

Apisful автоматически генерирует REST API к нашей схеме данных. То есть если у нас есть коллекция Products, то сможем получить список всех товаров запросом на url

https://api.apisful.com/v1/collections/products/

Подробнее с Apisful REST API можно ознакомиться здесь.

Для работы с апи мы создали директорию api. В ней файл api/index.js с базовыми настройками, а также файлы для работы с каждой коллекцией.

Шаблоны страниц

На примере главной страницы и страницы категории мы показали как можно вынести повторяющиеся элементы интерфейса (или логики) в компонент.

Но как быть с теми элементами страницы, которые повторяются на каждой (или почти каждой) странице? Можно подключать один и тот же элемент на каждой странице, а можно воспользоваться layouts.

Layout - это шаблон страницы, в который встраивается ее содержимое. В нашем случае в layout/default.vue находится шаблон, который оборачивает всю страницу в <div class="container"> и содержит шапку сайта (логотип и ссылку на корзину).

Шаблонов может быть больше одного. Дефолтный шаблон применяется ко всем страницам автоматически. Если странице нужен другой шаблон - например layouts/blog.vue, то в компоненте это нужно указать вот так

<script>
export default {
  layout: 'blog'
}
</script>

Vuex на примере корзины

Нередко бывает так, что на клиенте нам необходимо централизированное хранилище, к которому можно получить доступ из любого компонента.

В нашем случае мы используем такое хранилище для работы с корзиной:

Из pages/products/_product.vue мы добавляем товар в корзину.
В components/cart/CartProductsList.vue выводим список товаров и позволяем изменять количество.
А в components/CommonNavbar.vue показываем сколько товаров в корзине.

Логика работы такого хранилища находится в store/cart.js и использует Vuex. Vuex парой абзацев объяснить трудно, имеет смысл зайти на сайт и прям вникнуть. Если вам кажется, что все слишком сложно и можно жить без vuex, то, скорее всего, так и есть и пока изучение vuex можно отложить.

Плагины

Плагины позволяют расширить функциональность nuxt.js. Используя плагины можно подключать как сторонние библиотеки, так и небольшие кусочки своего кода.

Мы, например, создали файл plugins/filters.js, в котором объявили глобальный фильтр, форматирующий цену.
Чтобы он заработал, нужно в файле nuxt.config.js найти массив plugins и дописать в него путь к плагину. Вот так

вот здесь регистрировать плагины

О фильтрах во vue.js можно почитать тут, а о nuxt.js плагинах здесь.

Assets и static

Эти две папки предназначены для статических файлов: изображения, иконки, стили...

Разница между ними в том, что содержимое assets пройдет через webpack - минифицируется, изменит название и претерпит другие модификации. Поэтому к таким файлам нужно обращаться через ~/assets/path/to/file, на этапе компиляции такие пути заменятся на нужные.
Эту папку мы используем практически для всей статики.

Все то, что находится в static без изменений перенесется в корневую директорию сайта при сборке.
Это хорошо для таких файлов как favicon.ico, robots.txt и тому подобных.

Уведомления о новых заказах

Когда пользователь нажимает кнопку "Place an order", мы сохраняем заказ в коллекции Orders. Как мы это делаем можно увидеть в файле api/order.js.

Сами заказы можно увидеть в админке, вот:

Оформленные заказы

Для того, чтобы получать уведомления о новых заказах, мы воспользуемся тригерами. Создать тригер проще простого:

  1. В меню слева в разделе Triggers нажмем Add new
  2. Введем название тригера - New order (или любое другое)
  3. И настроим логику тригера - Send email notification when new record added in collection Orders
  4. Жмем Save

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

Заключение

Теперь мы, я надеюсь, понимаем как пользоваться nuxt'ом, зачем он нужен, как бесплатно и в пару кликов получить бэкенд для своего проекта.

Подписывайтесь и вы не пропустите следующие умопомрачительные туториалы.