Ну вот наш курс и подошёл к завершению.
Надеюсь, вам было интересно поработать с Vesp и вы узнали для себя что-то новое и даже интересное.
В этой заключительной заметке я хочу сделать краткий обзор PHP и JS библиотек, которые я использую в своих проектах. Возможно, они сэкономят вам время при разработке.
Список состоит только из решений, которые я лично использую постоянно, практически в каждом проекте. Он выкристаллизовался за довольно большой промежуток времени и почти не меняется.
Начнём с box/spout - это очень шустрая библиотека для работы с XLSX, ODS и CSV. Я наткнулся на неё, когда искал экономичное решение для экспорта больших объёмов информации, и она с ними отлично справилась.
У меня есть готовые решения по экспорту для контроллеров, постараюсь позже поделиться уже за рамками курса. Стоит отметить, что проект больше не развивается, так что если вам не хватит функционала - нужно искать другую либу, тут обновлений не будет.
ramsey/uuid я использую для удобной генерации uuid
моделей, когда мне не нужно, чтобы пользователи могли перебирать данные из контроллеров по id
. например, на bezumkin.ru все картинки выводятся именно по uuid
, подбором ничего не открыть.
Обратите внимание, что uuid
бывает нескольких видов, если вам нужен именно случайный uuid
- то это 4я версия.
peppeocchi/php-cron-scheduler уже используется в Vesp по умолчанию, но я про него ничего не рассказывал. Там всё просто - прописываете в crontab
сервера файл core/cli/cron.php
и добавляете скрипты для выполнения, рядом лежит пример для удаления старых сессий.
phpmailer/phpmailer для отправки почты, вместе с pelago/emogrifier, который разбирает стили письма из шапки и вставляет в теги, чтобы все почтовые клиенты их нормально понимали.
А для шаблонов писем я использую, понятное дело fenom/fenom.
Если будет интерес, могу написать подробнее про подключение подобных библиотек к проекту.
Еще вам может понадобиться установить doctrine/dbal, если вы будете писать миграции, которые меняют тип колонок в таблицах методом ->change()
, например со string
на text
. У меня такое иногда случается.
Этого набора библиотек мне хватает для реализации 90% проектов. В остальных случаях нужно подключить какой-нибудь сервис для оплаты или что-то подобное. Но всё это делается по одной схеме - ищем подходящую библиотеку, читаем документацию, подключаем.
При выборе JS решения для Vesp я советую руководствоваться следующими правилами:
vue-meta уже интегрирован в Nuxt и доступен на всех страницах для выставления title
и прочей работы с мета-тегами. Именно этот модуль мы используем в админке для указания названий страниц.
Я не уделял на курсе внимания SEO оптимизации, но думаю вы и сами легко с этим разберётесь, потому что модуль позволяет всё настроить в лучшем виде - просто читайте документацию.
@nuxtjs/sitemap - генерация карты сайта. Отлично работает как со статическими сайтами, так и с динамическими маршрутами на тысячи страниц. Просто пишете функцию с запросом в API для получения этих страниц, и модуль генерирует по ним карту.
Пример с bezumkin.ru:
Config.sitemap = {
hostname: env.SITE_URL,
gzip: true,
exclude: ['/admin/**', '/user/**', '/service/**'],
routes: async () => {
const routes = []
const [{data: sections}, {data: topics}] = await Promise.all([
axios.get(env.API_URL + 'web/sections', {params: {sort: 'views_count', dir: 'desc'}}),
axios.get(env.API_URL + 'web/topics', {params: {sort: 'id', dir: 'desc'}}),
])
sections.rows.forEach((section) => {
routes.push({
url: '/sections/' + section.alias,
lastmod: section.updated_at || section.created_at,
})
})
topics.rows.forEach((topic) => {
routes.push({
url: (topic.section.lessons ? '/lessons/' : '/sections/') + topic.section.alias + '/' + topic.id,
lastmod: topic.updated_at || topic.created_at,
})
})
return routes
},
}
@nuxtjs/pwa поможет вам превратить ваш сайт в PWA, который можно устанавливать на телефон. Всё работает хорошо по умолчанию, нужно только указать свою иконку. Этот модуль уже подключен по умолчанию в Vesp.
@nuxtjs/svg позволяет легко импортировать SVG
файлы в любом виде, хоть голым текстом, хоть компонентом для вставки в шаблон. Просто посмотрите примеры в документации, это крайне полезный модуль для вёрстки.
@nuxtjs/markdownit позволит вам удобно загружать на страницы какие-то статичные документы, вроде пользовательского соглашения в формате Markdown. Понятное дело, работать можно не только с файлами, а рендерить вообще любой markdown
в html
через функцию $md.render()
.
vuedraggable вы уже знаете, просто напоминаю, что он позволяет легко сортировать перетаскиванием любые данные в ваших компонентах.
glightbox я использую для вывода увеличенных картинок при клике, например прямо здесь - на bezumkin.ru.
Напоследок упомяну еще 2 библиотеки, которые уже подключены в Vesp, но со ссылками на их интеграцию в проект, возможно будет интересно почитать.
bootstrap-vue/nuxt - лучшая интеграция Bootstrap 4 с 85+ компонентами на все случаи жизни. Лично я очень ценю возможность самостоятельного подключения и модификации Bootstrap в стили проекта, с импортом только нужного, что облегчает размер собранного проекта.
Там есть и ленивая загрузка картинок, и прогресс-бары и еще много чего еще - просто внимательно посмотрите документацию, она очень подробная.
К сожалению, на Vue 3 и Bootstrap 5 они переходить не торопятся, поэтому я уже присматриваю другие решения на будущее.
@fortawesome/vue-fontawesome - мои любимые иконки, которые постоянно развиваются и обновляются. В последнее время, наверное, даже слишком бурно, что иногда приводит к небольшим проблемам. В любом случае, богаче набора иконок так удобно интегрированных в Nuxt найти не получится.
На этом у меня пока всё, надеюсь информации этого курса вам будет достаточно, чтобы погрузиться в Vesp и понять для себя, нравится ли вам логика его работы и стоит ли уделять время изучению используемых технологий.
Лично я уже не могу представить себе работу с MODX или другими системами, которые рендерят HTML на сервере, без прогрессивных фронтенд фреймворков.
Благодаря отделению бэкенда от фронтенда мои руки стали полностью развязаны. Не нужно думать о том, какие данные пришли от юзера, где может быть попытка взлома, какие переменные доступны в шаблоне, а какие нет. Просто считаем все запросы и ответы в API публичными - то есть, от хакера, и обрабатываем соответственно.
Завершение курса означает только окончание написания новых заметок, но я продолжаю отвечать на ваши вопросы в комментариях!
Проект VespShop скорее всего будет понемногу развиваться и после курса, если мне будут заказывать создание интернет-магазинов и при этом процессе получится разрабатывать какие-то универсальные штуки для общего пользования.
Буду очень рад вашим отзывам о курсе в комментариях. Что понравилось, что не очень, о чём еще нужно написать в будущем за рамками курса.
В курсе все достаточно разложено по полочкам и доходчиво объяснено, в этом смысле все хорошо. Спасибо!
Что хотелось бы рассмотреть в дальнейшем - построение чего-либо древовидного, например комменты или многоуровневое меню. И возможно я прошу многого, но хотелось бы понимать как пишутся умные фильтры, по типу mSearch2, подробно не надо, но хотелось бы понимать общий принцип. В интернете много посвящено статей обычным фильтрам, а вот по умным как-то вообще ничего не находил.
Ну а так, я думаю все что касается обычной разработки сайтов и что не было рассмотрено тут, так или иначе будет интересно.
Насчёт древовидного - это всё делается через рекурсивный вызов одного компонента, никакой уникальности именно для Vesp нет. Принцип ровно тот же, что и в MODX, да и вообще в любой системе.
Например, комментарии на bezumkin.ru:
id
иparent_id
parent_id
дерево комментариевCommentsTree:
<template> <div id="topic-comments" ...> <comments-thread :comments="commentsTree" :topic="topic" ... /> <comments-form v-if="..." /> </div> </template>
CommentsThread:
<template> <div class="comments-thread"> <div v-for="comment in comments" ...> ... <comments-form v-if="showForm(comment)" ... /> <comments-thread v-if="comment.children.length" :comments="comment.children" :topic="topic" ... /> </div> </div> </template>
Нужно будет прописать логику вывода формы комментирования в нужном месте, и методы сохранения, редактирования и удаления комментриев. Лично я всё это держу в родительском компоненте
CommentsTree
, а действия из вложенныхCommentsThread
передаю событиямиthis.$emit('action', data)
Примерно такая же логка будет и с любыми другими рекурсивными данными, вроде вложенного меню.
Постараюсь написать про это заметку, как будет время.
Ну да, вроде как понятнее. В остальных системах принцип понятен, тут больше возникал вопрос реализации этого на vue, так как с ним редко доводилось работать. Надо пробовать. Спасибо!
А подскажите пожалуйста, как для @nuxtjs/sitemap в конфиге правильно импортировать axios?
Всё как обычно, в самом начале файла конфига
import axios from 'axios'
Курс замечательный, проделана колоссальная работа, узнал много новых технологий, спасибо Василию за труды и то, что он делится ценным опытом, всем рекомендую!!!
Хотелось бы статейку про правильную интеграцию JS в VESP.
Понятно, что во всем можно разобраться самому, но хочется здесь каких-то рекомендаций, чтобы в будущем было больше порядка.
Занимаясь этим вопросом наткнулся на статью: Как правильно зарегистрировать плагин в nuxt.js . То есть может быть проблемой правильного добавление плагина Vue в Nuxt.
Да и по ванильному JS, хочется каких-то рекомендаций, например в каком случае js код нужно переделывать в Vue плагин.
Ура!
Модули Nuxt ставятся парой строк, там вообще никаких вопросов нет. Большинство популярных компонентов Vue про Nuxt или в курсе, или имеют issues про это на Github.
А вот модули JS надо смотреть в каждом конкретном случае. Проблемы обычно бывают если JS рассчитывает на работу в браузере, а ты его используешь при серверном рендере, но это довольно легко решается.
В общем, давай так - как будет конкретный вопрос по подключению чего-либо, просто спроси тут, я отвечу.
Ок, спасибо!
Василий добрый день!
Не подскажешь в чем может быть проблема с установкой Nuxt плагина в Vesp?
Устанавливаю плагин на голую сборку Nuxt - все работает, делаю все тоже самое на Vesp вижу такую заглушку.
В консоли ошибок нет.
Вот сам плагин https://github.com/oleksiikhr/vue-stripe-menu/tree/vue2
Думаю, может в nuxt.config.js плагин как-то не так подключаю. Подключил так:
Vesp расширяет и возвращает конфиг из
@vesp/frontend
. То есть, должно быть так:import {Config, findEnv, loadEnv} from '@vesp/frontend' // ... всякие другие настройки Config.plugins = ['@/src/site/plugins/menu'] // ... return Config
А если ты делаешь
export default {...}
, то просто игнорируешь все настройки по умолчанию и возвращаешь только 1 плагин, без остальных настроек - и тогда система не может даже найти директорию с исходниками, потому и просить её создать.Супер! Спасибо, заработало!!
Василий, добрый день!
У нас есть файлы scss, только не пойму, как они обрабатываются, ведь node-sass, sass-loader не установлены?
Очень даже установлены - просто их тянет зависимостями сам Vesp.
Круто! Спасибо.
А для чего нужна папка vendor?
vesp-shop.test/core/vendor
А там PHP зависимости от Composer!
Спасибо, понял!!
Только сейчас заметил, что у тебя путь от корня сайта.
Такого быть не должно, директория
core
находится на одном уровне сwww
- корнем сайта. Ты видимо случайно в нейcomposer
как-то запустил или что-то такое.Вот правильная www - там только
api.php
и никаких директорий.Василий, спасибо! Это у меня так папка называется vesp-shop.test в которой на одном уровне папки www и core, а в core находится папка vendor.
А, тогда все верно!
Василий, добрый день!
Обратил внимание, что на странице http://s30069.h10.modhost.pro/category-81/product-1 дважды идёт ссылка на один и тот же скрипт.
В начале:
<link rel="preload" href="/_nuxt/62f66e0.js" as="script">
В конце:
<script src="/_nuxt/62f66e0.js" defer></script>
Можешь объяснить в чем фишка?
Это 2 разных тега:
link
в шапке - это предварительная загрузка (preload)script
в footer уже обычный тег с javascriptНесмотря на 2 тега, физически файл грузится только 1 раз.
Стоит отметить, что мы этими вопросами вообще не заведуем и не управляем - Nuxt сам собирает файлы, минимизирует и подключает оптимальным способом.
Василий, спасибо большое, за разъяснение!
Скажи пожалуйста, на сколько сложно и с какими трудностями можно столкнуться, если на Vesp натянуть вот такой шаблон https://pixinvent.com/demo/vuexy-vuejs-admin-dashboard-template/landing/ ?
Не знаю, судя по Composition API оно вообще для Vue 3. Да и Laravel там, наверное, не просто так.
Ты лучше скажи, зачем оно вообще тебе нужно?
Это же админка, чтобы что-то админить - а Vesp по умолчанию админить нечего, кроме юзеров. Всё нужно писать, а в процессе написания заодно и админку сделаешь на Bootstrap.
В общем, я ни разу ничего подобного не использовал и зачем оно нужно не в курсе.
Василий, спасибо!
1. Я изучал Bootstrap, когда он только появился, но использовать его не стал, так как тогда он тянул все стили своей библиотеки даже те которые я не использовал на сайте. Сейчас я вижу, что в VESP можно конфигурировать в файле nuxt.config.js, подключать только те модули из Bootstrap которые будут использоваться, а вся библиотека Bootstrap не тянется?
Я делаю малостраничные сайты на JS и Webpack, а в верстке использую методологию БЭМ. Vue и Nuxt стал изучать с появлением твоего курса по Vesp.
2. Вопрос: Как я понял главная проблема установки плагинов Vue под Nuxt это SSR, а если Nuxt будет работать в SPA режиме, то такой проблемы не будет и можно с минимальными изменениями использовать плагины Vue в Nuxt?
Шаблон, ссылку на который я дал выше можно скачать в ознакомительных целях, набрав в поиске vuexy download. Хочу подсмотреть там идеи в плане UI/UX дизайна, ведь они тоже используют bootstrap-vue, только там Vue без Nuxt. Там есть версия под Vue 3, но так же есть и под Vue 2.
Вот список плагинов которые они используют:
3. Вопрос: Василий, почему они используют "bootstrap": "4.6.0" и "bootstrap-vue": "2.21.1", разве одного bootstrap-vue не достаточно? И вообще, я много где вижу подобное, например: "chart.js": "2.9.4" и "vue-chartjs": "3.5.1"?
2. Это никакая не главная проблема, да и вообще не проблема. Просто не все JS компоненты в курсе, что они могут быть запущены без браузера, на сервере - и могут быть не готовы к подобному.
Например, какой-нибудь lightbox при инициализации проверяет ширину экрана. Но на сервере
Поэтому такой компонент на сервере просто не нужно запускать, и нет проблем.
3. Ты путаешь https://getbootstrap.com и https://bootstrap-vue.org
Первый - собственно SASS\CSS стили и правила оформления, а второй - это набор компонентов, который реализует разный функционал на Vue, используя стили Bootstrap. Типа у открытой модалочки одни классы, а у закрытой другие - и меняет их Vue.
Нужные стили я подключаю в
assets/scss/index.scss
, а компоненты Bootstrap Vue вnuxt.config.js
, чтобы работал tree shakingЕсли я вдруг забуду подключить стили - компоненты всё равно работать будут, просто не будет оформления.
Точно так же стили Bootstrap используются и в React у компонентов React Bootstrap.
Ну и насчёт Сhart.js ты уже догадался, что это оригинальная библиотека и её адаптация для Vue. Как правило адаптации пишут сторонние разработчики, но вот например у Sortable.JS, который мы использовали - куча поддерживаемых фреймворков от основной команды.
Василий, спасибо за такой развернутый ответ!
Вроде бы во всем более-менее разобрался начинаю делать первый свой сайт на VESP. Сайт на заказ, но не сложный, думаю все получится, заодно прокачаюсь в современных технологиях веб-разработки )).
Василий, добрый день!
А как увидеть весь список иконок в наличии, которые я могу применить из пакета?
Смотри на их сайте, только обрати внимание, что мы используем те, которые solid и free.
https://fontawesome.com/search?o=r&m=free&s=solid
Ок, спасибо!!! А то я на их сайте растерялся, много у них там всего. А в @fortawesome/free-solid-svg-icons все svg в js файлах и их не посмотреть.
Василий, не подскажешь, в чем может быть проблема? В breadcrumbs.vue меняю
<fa icon="home" />
на<fa icon="list" />
и получаю ошибку:В @fortawesome/vue-fontawesome файлы faList.js и faList.d.ts присутствуют.
Так надо ж подключить в конфиге nuxt.
Импортируются только явно указанные иконки, для уменьшения размера итогового приложения.
Василий, спасибо тебе большое! Обалдеть, как круто!
Василий, добрый день!
А у нас возможно к иконкам применить их другие свойства: (regular, light, thin, duotone)?
Например, как у них к корзине, код для Vue:
<font-awesome-icon icon="fa-light fa-cart-shopping" />
, разные способы попробовал, так и не получилось.Конечно! Нужно только:
<fa :icon="['fal', 'cart-shopping']" />
Могут быть fal (light), far (regular), fad (duo), fas (solid) и fab (brands)
Супер!! Спасибо, большое!
Василий, я правильно понял?:
Только так я могу еще установить брендовые иконки:
а вот таких пакетов нет:
Или типы устанавливаются, как-то иначе?
И так установить не выходит:
Они доступны только за деньги, я же дал ссылку на документацию.
Спасибо, разобрался!
Василий, добрый день!
У тебя
fontawesome-free-solid
вшит в VESP, а я устанавливаю:затем правлю конфиг site/nuxt.config.js
Не работает, или нужно создавать плагин?
И еще, admin/nuxt.config.js по другому написано:
Как правильно писать в конфиге?
Это откуда взялось? Там строка, а не массив, ничего менять не нужно - посмотри документацию.
Это просто функции объединения для объектов и массивов из библиотеки lodash, чтобы добавить свои настройки к тем, которые уже есть в стандартном Vesp.
Спасибо, что-то она мне не нагуглилась.
Ясно, спасибо!
Василий, добрый день!
Подскажи, пожалуйста, как применить markdownit?
yarn add @nuxtjs/markdownit @github/markdown-toolbar-element
и
Что еще нужно добавить или отнять, чтобы в админке появилась рабочая панель редактора?
И еще, как сделать отформатированный текст из базы данных, сейчас он у меня выводится со ** звездочками ** а не жирный?
Сделал то же самое в site/nuxt.config.js , что и в admin/nuxt.config.js но результата нет.
Не знаю, ни разу этим компонентом не пользовался. Но могу предположить, что навешивать его нужно на сырой
<textarea
, а не на vue компонент<b-form-textarea
Вся фишка Markdown именно в том, что текст в БД хранится без HTML, а рендерится перед выводом на экран. Вот тебе его и нужно пропустить через markdownit или что-то подобное. У него в примерах есть такое, попробуй:
Василий, спасибо большое, все заработало!!
Скажи, пожалуйста, если ты не используешь
@github/markdown-toolbar-element
, то как выводить кнопки?А я себе свой редактор написал с помощью textarea-editor, где могу прописать любую логику при нажатиях на кнопки и шорткаты.
Спасибо Василий, понял!!
Василий, добрый день!
Подскажи, пожалуйста, а как ты подключаешь glightbox?
Попытался создать плагин, не удалось подключить.
Сделал ссылки из nuxt.config.js
Все работает, но это не по фэншую.
И еще, до сих пор окончательно не разобрался с путями, как например мне из конфига обратиться к файлу который лежит в пакете?
Config.head.script = [{src: ' ~glightbox/dist/js/glightbox.js', body: true}]
- так не работает?Ты всё еще не перестроился мысленно на Webpack. Не нужно подключать готовый скрипт как на обычном сайте, нужно его импортировать в свой код.
import GLightbox from 'glightbox' import 'glightbox/dist/css/glightbox.min.css' export default { data() { return { files: [ // ... ], }, // ... methods: { onShowImage(e) { const elements = [] let startAt = 0 const thumbnails = e.currentTarget.closest('.images') if (thumbnails) { const links = thumbnails.querySelectorAll('a') links.forEach((a) => { elements.push({href: a.href, type: 'image'}) }) startAt = elements.findIndex((i) => i.href === e.currentTarget.href) } else { elements.push({href: e.currentTarget.href, type: 'image'}) } GLightbox({elements, startAt}).open() }, }, }
И навесить обработчик на ссылки с картинками:
<template> <div class="images"> <a v-for="file in files" :key="file.id" :href="$image(file)" @click.prevent="onShowImage"> <img :src="$image(file, {w: 200, h: 150, crop: 'fit'})" alt="" /> </a> </div> </template>
Код неточный, могут быть ошибки и очепятки.
Василий, спасибо большое!!
Василий, добрый день!
Уже перепробовал все что смог придумать, и уже думаю что nuxt с glightbox не совместимы.
Сделал сборку nuxt + glightbox + vesp-frontend:
https://codesandbox.io/s/vesp-frontend-ewqn1w?file=/pages/index.vue
Там glightbox работает, но подключение без импорта.
Глянь, пожалуйста, может я что-то не понимаю, хотя другие пакеты импортируются нормально, а этот никак не хочет (((.
Как-то проморгал твой комментарий, сорян.
Проблема в том, что ты запускаешь Nuxt в режиме серверного рендера, а плагины типа lightbox на сервере не работают. При импорте подобных дополнений на сервере будет ошибка
window is not defined
, что логично - никакогоwindow
там нет.Поэтому нужно вынести подключение Glightbox в клиентский плагин, то есть плагин с суффиксом
.client
.import GLightbox from 'glightbox' import 'glightbox/dist/css/glightbox.min.css' export default (ctx, inject) => { inject('glightbox', (params ={}) => { return new GLightbox(params) }) }
Затем полключить его в
nuxt.config.js
:Config.plugins = ['@/plugins/glightbox.client.js']
И дальше можно использовать
this.$glightbox
на всех страницах:mounted() { this.$glightbox({ selector: '.glightbox', touchNavigation: true, loop: true, autoplayVideos: true, }) },
Василий спасибо большое! Очень полезный ответ от тебя, ведь ошибка: "Window is not defined" достаточно часто встречается, а теперь понятно откуда она берется.
Подключил вот так:
Config.plugins = [{ src: "@/plugins/glightbox.js", mode: "client" }];
Так как:
Да, я забыл суффикс в своем примере - так что ты все правильно сделал.
Пример поправил.
Ок, спасибо!
Здравствуйте, Василий! Не могли бы вы поделиться примерами работы с почтой? Подключение библиотек, оформления письма и тд.
Я использую PHPMailer для работы с почтой, шаблоны оформляю на Fenom. Делаю один основной, а разные варианты писем его просто расширяют.
Постараюсь написать заметку про это, с готовым кодом - но обещать не могу, занят на работе.
Готово! https://bezumkin.ru/lessons/vesp/3107
Спасибо большое!