Продолжение рассказа про разработку pet-проекта о кафе и коворкингах на солнечном Кипре. «Рабочие места» для цифровых кочевников ヽ(。_°)ノ
В первой части я рассказал про REST API микросервис, теперь - про фронтэнд-сайт.
Всё удалось, код проекта открыт, велкам в пул-реквесты. Адрес сайта - в конце статьи, чтобы меньше походило на рекламу.
Архитектура
Сайт реализован в виде PWA на Vue 3 Composition API и UI-фреймворке Vuetify. Оба инструмента хорошо подходят для быстрого старта проекта с нуля и содержат меньше избыточного кода, нежели предыдущие версии.
Основная часть сайта - это карта Google Maps в always free tier, на которую выводятся данные из REST API микросервиса, а также панель с фильтрами и списком "рабочих мест".
"Входной" компонент src/components/Home.vue:
управляет отображением разделов для мобильной и десктопной версии;
управляет данными из API, которые получает из src/composable/api.js;
обрабатывает их computed фильтрами из src/components/Filters.vue;
передаёт отфильтрованные данные в компонент src/components/Places.vue для отображения списка мест;
обрабатывает ошибку 404;
устанавливает мета-теги через компонент src/components/PlaceHead.vue.
Отдельной карточки "рабочего места" в проекте нет, пока достаточно простой прокрутки списка мест к нужному месту:
watch(
() => props.selectedPlaceId,
id => {
scrollToPlace(id)
}
)
Фильтры "зашиты" в код компонента src/components/Filters.vue и дополнительно стилизованы для более компактного вида.
Для работы с мета-тегами используется пакет vueuse/head.
Для отображения карты используется библиотека fawmi/vue-google-maps версии 0.9.72. В последующих версиях автор поломал карту и не выложил исходный код на GitHub (однако эти версии доступны через npm).
Также на карте можно отобразить своё местоположение и увидеть ближайшие интересные места.
Большие и маленькие фотографии мест выводятся каруселью через swiper/vue и пару своих компонентов.
Градиентный цветной рейтинг мест рассчитывается простым методом в src/composable/colors.js в цветовом пространстве hsl. В зависимости от значения рейтинга, изменяется только hue, a saturation и lightness - константы.
Для исправления возможных неточностей на карточках мест предусмотрена кнопка Complain - всё по-серьёзному :-)
Простой роутер src/router/index.js на основе vue-router позволяет пока не использовать store (при текущей функциональности проекта) и помогает в обработке 404 ошибок.
Большинство компонентов подключаются асинхронно динамически для ускорения отображения страниц, например, const Navigation = defineAsyncComponent(() => import("./Navigation.vue"))
Часть шаблона завёрнута в <KeepAlive> для кеширования компонентов при открытии/закрытии панели. Однако подружить их с Suspense для отображения заглушек не удалось :-(
Вместо дефолтных шрифтовых иконок в Vuetify, в проекте используются индивидуально импортируемые svg-иконки из пакета mdi/js от https://materialdesignicons.com. Экономия около 1 Мб в финальном бандле.
Для сборки проекта используется Vite и немного магии в vite.config.js для оптимизации итогового кода: минификации CSS и HTML, создания облегчённой версии Sentry.
PWA собирается посредством vite-pwa/vite-plugin-pwa, а его параметры также задаются в vite.config.js. В целом, реализация PWA не была самоцелью проекта, но с её помощью удалось реализовать хорошее кеширование всех частей проекта и повторное открытие сайта получилось очень быстрым.
Про Vuetify
В целом, Vuetify понравился больше, чем Quazar https://quasar.dev/ и другие UI-фреймворки. Но у него есть свои недостатки:
Сложность тонкой визуальной кастомизации компонентов. Vuetify рассчитан на Material Design и, например, просто так убрать отступы между чекбоксами или сделать мобильные кнопки меньше размером, не получится. В некоторых случаях придётся использовать :deep или заворачивать компоненты в дополнительные div'ы, что приводит к снижению оценки Google PageSpeed из-за большой глубины DOM.
Невозможность удалить неиспользуемые стили. Сборку можно очень хорошо облегчить, индивидуально импортируя компоненты и настраивая стили в SASS, но некоторую часть общих неиспользуемых стилей не получится удалить через PurgeCSS и аналогов из-за динамических названий классов.
Мало возможностей в некоторых компонентах, например, в слайдере.
Но, повторюсь, этот фреймворк достаточно хорош для многих проектов.
Деплой
Та же платформа Fly.io с управляемыми microVM Firecracker. Тут всё гораздо проще, чем при размещении API микросервиса и достаточно Dockerfile'а из нескольких строк и настройки заголовков в config/headerConfig.json:
FROM pierrezemb/gostatic
COPY docker/config/headerConfig.json /config/
COPY dist/ /srv/http/
ENTRYPOINT ["/goStatic", "-fallback", "/index.html"]
CI/CD
Тут чуть сложнее: сначала Github Action собирает билд при помощи Vite, а затем flyctl делает из него контейнер и деплоит на продакшн vm. Все секреты хранятся в GitHub production environment.
Мониторинг
Тот же Sentry и Honeybadger.
На этом этапе сайт работает, размещён в продакшн-окружении и доступен всем пользователям. Проект выполнен :)
Репозиторий фронтэнда, сайт https://workplaces.cy/