7. Скафолдинг внешнего интерфейса
Перевод | Автор оригинала: Loris
ЭПИЗОД 7
2 МЕСЯЦА НАЗАД
ЧТЕНИЕ 14 МИН.
Хотя технически пользователи могут начать отправлять и читать твиты с помощью нашей программы, напрямую взаимодействуя с блокчейном, никому не нужен такой пользовательский опыт.
Мы хотим абстрагировать все это в приятный пользовательский интерфейс (UI), который напоминает то, с чем они знакомы. По этой причине мы создадим внешний клиент, используя VueJS.
Мы будем использовать VueJS, потому что: а) это мой любимый фреймворк JavaScript и б) он очень мало документирован в экосистеме Solana. Если вы лучше знакомы с другими фреймворками JavaScript, такими как React, вы все равно можете следить за ними, так как большинство концепций перекликаются между фреймворками.
Теперь фронтенд-разработка — это отдельный мир, и я легко мог бы часами подробно описывать, как создать пользовательский интерфейс, который мы получим в конце этого эпизода. Тем не менее, в центре внимания этой серии Солана, и я не хотел бы слишком отклоняться от нее. Существует множество руководств по разработке внешнего интерфейса — даже в этом блоге.
В то же время нам нужен пользовательский интерфейс, чтобы продолжить наше путешествие и создать наше децентрализованное приложение. Так вот в чем дело. В этом эпизоде я объясню, как начать работу с VueJS и установить все зависимости, которые нам понадобятся, чтобы вы могли сделать это самостоятельно. Затем, когда дело доходит до фактического дизайна и компонентов пользовательского интерфейса, я дам вам кучу файлов для копирования/вставки в разные места и кратко объясню, что они делают. Сначала компоненты будут содержать фиктивные данные, чтобы мы могли связать их с нашей программой Solana в следующих эпизодах.
Пристегните ремни безопасности, потому что мы собираемся двигаться быстро. Пойдем!
Установите интерфейс командной строки Vue.
Один из самых простых способов создать новое приложение VueJS — использовать его инструменты CLI.
Если они у вас еще не установлены, вы можете сделать это, запустив следующее.
npm install -g @vue/cli@5.0.0-rc.1
Обратите внимание, что мы явно запрашиваем версию 5, которая все еще является кандидатом на выпуск на момент написания, потому что мы хотим, чтобы наше приложение VueJS было связано с Webpack 5 вместо Webpack 4.
Вы можете проверить правильность установки инструментов VueJS CLI, запустив:
vue --version
Создайте новое приложение Vue
Теперь мы можем создать новое приложение VueJS, запустив vue create
, а затем каталог, который должен быть создан для него.
Мы хотим, чтобы наш внешний клиент находился в каталоге app, который в настоящее время является пустой папкой. Поэтому нам также нужно будет использовать параметр --force для его переопределения. Хорошо, давайте запустим это.
vue create app --force
Теперь вас должны попросить выбрать предустановку для вашего приложения. В этой серии мы будем использовать Vue 3, поэтому давайте выберем предустановку Default (Vue 3)
.
? Please pick a preset:
Default ([Vue 2] babel, eslint)
❯ Default (Vue 3) ([Vue 3] babel, eslint) # <- This one.
Manually select features
И вот так мы получили приложение VueJS 3 внутри нашего проекта.
Давайте cd
в него, так как мы будем работать внутри этого каталога до конца этого эпизода.
cd app
Установка библиотек Solana и Anchor
Далее давайте установим библиотеки JavaScript, предоставленные Solana и Anchor. Мы упоминали в предыдущем эпизоде, что они уже были включены в наш проект Anchor для наших тестов, но это другая среда со своими собственными зависимостями, поэтому нам нужно установить их явно.
Убедитесь, что находитесь в каталоге app
и запустите следующее.
npm install @solana/web3.js @project-serum/anchor
Настройка полифиллов node.js
Мир внешнего интерфейса полон причуд и ошибок, и вот одна из них, с которой я борюсь при создании этой серии.
Некоторые из библиотек JavaScript, которые мы будем использовать в нашем приложении, зависят от полифиллов Node.js.
Node.js — это, по сути, «JavaScript для серверов», и цель полифилов Node.js — перенести некоторые из его основных зависимостей в мир внешнего интерфейса. Таким образом, один и тот же код можно использовать на обеих сторонах.
Например, помните, как мы преобразовали строку в буфер, используя Buffer.from('some string')
? Нам не нужно было импортировать этот объект Buffer
, потому что это основная зависимость Node.js, которая была заполнена для нас.
В настоящее время мир внешнего интерфейса отказывается от объединения всех этих зависимостей Node.js по умолчанию. И это именно то, что сделал Webpack, когда выпустил версию 5. Вот очень хорошее объяснение [из их документации](https://webpack.js.org/blog/2020-10-10-webpack-5-release/#automatic- nodejs-полифиллы удалены):
В первые дни целью веб-пакета было разрешить запуск большинства модулей Node.js в браузере, но ландшафт модулей изменился, и теперь многие модули используются в основном для целей внешнего интерфейса. Webpack <= 4 поставляется с полифиллами для многих основных модулей Node.js, которые автоматически применяются, как только модуль использует любой из основных модулей (т. е. модуль шифрования).
Webpack 5 прекращает автоматическое заполнение этих основных модулей и фокусируется на модулях, совместимых с внешним интерфейсом. Наша цель — улучшить совместимость с веб-платформой, где основные модули Node.js недоступны.
Так что это приятное изменение, но, как я уже говорил ранее, существование некоторых наших зависимостей зависит от этих полифилов. Если мы ничего не сделаем, мы получим следующую ошибку при компиляции нашего интерфейса.
BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.
К счастью для нас, есть способ решить эту проблему, вернув нужные нам полифиллы и/или сказав Webpack, что они нам не нужны, чтобы он перестал жаловаться.
В нашем случае нам понадобится только полифил Buffer
, и мы можем отключить другие, которые в противном случае потерпели бы неудачу. Мы можем сделать это внутри нашего файла vue.config.js
, который содержит свойство configureWebpack
, позволяющее нам предоставлять дополнительные конфигурации Webpack.
const webpack = require('webpack')
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
configureWebpack: {
plugins: [
new webpack.ProvidePlugin({
Buffer: ['buffer', 'Buffer']
})
],
resolve: {
fallback: {
crypto: false,
fs: false,
assert: false,
process: false,
util: false,
path: false,
stream: false,
}
}
}
})
Круто! Теперь мы должны быть защищены от запутанных ошибок полифилла. 😌
Настройка ESLint
Пока мы настраиваем, давайте добавим пару вещей в наши конфигурации ESLint. Если вы не знакомы с ESLint, это линтер JavaScript, который наш редактор кода использует, чтобы предупредить нас об ошибках или коде, который не соответствует заданному стилю кода.
Поскольку мы будем использовать суперпричудливый тег <script setup>
в наших компонентах VueJS 3, нам нужно сообщить ESLint об этом, поэтому наш редактор кода не показывает много ошибок, когда код действительно действителен.
Здесь не нужно слишком беспокоиться о деталях, просто откройте package.json
вашего каталога app
и замените ваш объект eslintConfig
следующим.
"eslintConfig": {
"root": true,
"env": {
"node": true,
"vue/setup-compiler-macros": true
},
"extends": [
"plugin:vue/vue3-essential",
"eslint:recommended"
],
"parserOptions": {
"parser": "@babel/eslint-parser"
},
"rules": {
"vue/script-setup-uses-vars": "error"
}
},
Установка TailwindCSS
Для разработки пользовательского интерфейса я буду использовать свою любимую структуру CSS: TailwindCSS. Если вы не знакомы с ним, это сверхмощный служебный фреймворк, с которым очень приятно работать. Излишне говорить, что я очень рекомендую это.
Для его установки нам понадобятся следующие зависимости. Как обычно, обязательно запустите это в каталоге app
.
npm install tailwindcss@latest postcss@latest autoprefixer@latest
Затем нам нужно сгенерировать наш файл конфигурации Tailwind. Для этого просто запустите следующее.
npx tailwindcss init -p
Это сгенерировало файл tailwind.config.js в нашем каталоге app.
Обратите внимание, что мы использовали параметр -p
для создания файла postcss.config.js
. Это необходимо для того, чтобы Webpack мог распознать Tailwind как плагин PostCSS и, следовательно, скомпилировать наши конфигурации Tailwind.
Давайте сразу внесем небольшую корректировку в наш конфиг-файл Tailwind. Мы предоставим массив purge
, чтобы при компиляции для производства Tailwind мог удалить все служебные классы, которые не используются в предоставленных путях.
По сути, нам нужно указать, где находится наш HTML, который в нашем случае находится внутри любого файла JavaScript в папке src
или в общедоступном файле index.html
.
Итак, откройте файл tailwind.config.js и замените пустой массив purge следующими строками
module.exports = {
purge: [
'./public/index.html',
'./src/**/*.{vue,js,ts,jsx,tsx}',
],
// ...
}
Затем создайте новый файл в папке src
с именем main.css
и добавьте следующий код.
@tailwind base;
@tailwind components;
@tailwind utilities;
При компиляции эти три оператора Tailwind будут заменены множеством служебных классов, сгенерированных динамически.
Наконец, нам нужно импортировать этот новый файл CSS в наш файл main.js
, чтобы Webpack мог его подобрать.
Давайте импортируем его в начало этого файла и добавим несколько комментариев, чтобы разделить код на небольшие разделы.
// CSS.
import './main.css'
// Create the app.
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
We’re now fully ready to use TailwindCSS!
Установка Vue маршрутизатора
Далее нам нужна маршрутизация в нашем интерфейсе. Щелчок по новой странице должен отражаться в URL-адресе и наоборот. К счастью, нам не нужно реализовывать это с нуля, поскольку для этой цели мы можем использовать Vue Router.
Чтобы установить его, нам нужно запустить следующее. Обратите внимание, что нам нужно явно установить версию 4 Vue Router, так как эта версия совместима с Vue 3.
npm install vue-router@4
Далее давайте определим наши маршруты — то есть сопоставление между URL-адресами и компонентами VueJS.
Создайте новый файл в папке src с именем route.js и вставьте внутрь следующее.
export default [
{
name: 'Home',
path: '/',
component: require('@/components/PageHome').default,
},
{
name: 'Topics',
path: '/topics/:topic?',
component: require('@/components/PageTopics').default,
},
{
name: 'Users',
path: '/users/:author?',
component: require('@/components/PageUsers').default,
},
{
name: 'Profile',
path: '/profile',
component: require('@/components/PageProfile').default,
},
{
name: 'Tweet',
path: '/tweet/:tweet',
component: require('@/components/PageTweet').default,
},
{
name: 'NotFound',
path: '/:pathMatch(.*)*',
component: require('@/components/PageNotFound').default,
},
]
Это все страницы, которые содержит наше приложение, включая резервные URL-адреса, которые не существуют.
Если мы попытаемся скомпилировать наше внешнее приложение на этом этапе, используя npm run serve
, это не удастся, потому что все эти компоненты отсутствуют, но не волнуйтесь, мы добавим их все в следующем разделе.
Теперь, когда наши маршруты определены, мы можем импортировать и подключить плагин Vue Router к нашему приложению VueJS.
Откройте файл src/main.js
и обновите его следующим образом.
// CSS.
import './main.css'
// Routing.
import { createRouter, createWebHashHistory } from 'vue-router'
import routes from './routes'
const router = createRouter({
history: createWebHashHistory(),
routes,
})
// Create the app.
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).use(router).mount('#app')
Как видите, мы сначала создаем экземпляр маршрутизатора, предоставляя наши маршруты, а затем заставляем наше приложение VueJS «использовать» его в качестве подключаемого модуля.
Если вам интересно, метод createWebHashHistory добавляет ко всем путям префикс #, чтобы нам не нужно было позже настраивать какие-либо перенаправления на нашем сервере.
На данный момент наше приложение VueJS полностью настроено с помощью Vue Router и TailwindCSS. Все, что осталось сделать, это реализовать компоненты, которые составят пользовательский интерфейс нашего интерфейса.
Это означает, что если вы хотите создать свой собственный дизайн, вы можете остановиться здесь и реализовать компоненты, перечисленные в файле route.js, самостоятельно.
Тем не менее, я подготовил все это для вас, чтобы мы могли сосредоточиться на том, как интегрировать интерфейс с нашей программой Solana, а не тратить время на разработку пользовательского интерфейса.
Итак, пришло время для копирования/вставки!
Исходники
Хорошо, давайте сделаем это! Загрузите ZIP-файл ниже и распакуйте его, чтобы получить доступ ко всем файлам, которые составят наш пользовательский интерфейс.
Теперь, когда у вас есть все файлы, давайте переместим их в нужные папки.
Внутри папки src
:
- Удалите существующий компонент
App.vue
и замените его компонентом, представленным в ZIP-файле. - Удалите существующий каталог
components
и замените его на тот, который указан в ZIP-файле. - Добавьте каталоги
composables
иapi
из ZIP-файла.
Бум, интерфейс готов! 💥
На этом этапе вы сможете запустить npm run serve
и взглянуть на пользовательский интерфейс, открыв: http://localhost:8080/
.
npm run serve
# Outputs:
#
# App running at:
# - Local: http://localhost:8080/
# - Network: http://192.168.68.118:8080/
Хорошо, давайте немного осмотримся и объясним назначение всех этих файлов, которые мы только что добавили.
Объяснение компонентов
Начнем с компонентов. Помимо компонента App.vue, который находится в папке src, все остальные компоненты должны находиться в папке src/components.
Обратите внимание, что любой твит или любой подключенный кошелек в настоящее время имитируется поддельными данными, поэтому мы сможем узнать, как все подключить, в следующих эпизодах.
App.vue
: это основной компонент, который загружается при запуске нашего приложения. Он разрабатывает общий макет нашего приложения и делегирует все остальное Vue Router с помощью компонента<router-view>
. Любая страница, соответствующая текущему URL-адресу, будет отображаться там, где находится<router-view>
.PageHome.vue
: Домашняя страница. Он содержит форму для отправки твитов и список последних твитов от всех.PageNotFound.vue
: резервная страница 404. Он выводит сообщение об ошибке и предлагает вернуться на главную страницу.PageProfile.vue
: страница профиля подключенного пользователя/кошелька. Он отображает открытый ключ кошелька перед отображением формы твита и списка твитов, отправленных из этого кошелька.PageTopics.vue
: страница тем позволяет пользователям вводить тему и отображает все твиты, соответствующие ей. После ввода темы также отображается форма для отправки твитов с предварительно заполненной темой.PageTweet.vue
: на странице твита отображается только один твит. Открытый ключ твита предоставляется в URL-адресе, позволяющем нам получить учетную запись твита. Это полезно для пользователей, чтобы делиться твитами.PageUsers.vue
: аналогично странице тем, страница пользователей позволяет искать других пользователей, вводя их открытый ключ. При вводе действительного открытого ключа все твиты от этого пользователя будут извлекаться и отображаться на этой странице.TheSidebar.vue
: этот компонент используется в основном компонентеApp.vue
и создает боковую панель слева от приложения. Он использует компонент<router-link>
, чтобы легко генерировать URL-адреса Vue Router. Он также содержит кнопку для пользователей, чтобы подключить свои кошельки, но на данный момент эта кнопка ничего не делает.TweetCard.vue
: этот компонент отвечает за оформление одного твита. Он используется везде, где нам нужно отображать твиты.TweetForm.vue
: этот компонент создает форму, позволяющую пользователям отправлять твиты. Он содержит поле для содержания, поле для темы и небольшой обратный отсчет символов.TweetList.vue
: этот компонент использует компонентTweetCard.vue
для отображения не одного, а нескольких твитов.TweetSearch.vue
: этот компонент предлагает многократно используемую форму для поиска по критериям. Он используется на странице тем и на странице пользователей, поскольку нам нужно что-то искать на обеих этих страницах.
Объяснение файлов API
Помимо компонентов, ZIP-файл также содержит папку api
. Эта папка содержит по одному файлу для каждого типа взаимодействия, которое мы можем иметь с нашей программой. Технически нам не нужно извлекать эти взаимодействия в отдельные файлы, но это хороший способ сделать наши компоненты менее сложными и простыми в обслуживании.
На данный момент каждый из этих файлов определяет функцию, которая возвращает фиктивные данные.
fetch-tweets.js
: Предоставляет функцию, которая возвращает все твиты из нашей программы. В следующем выпуске мы немного изменим эту функцию, чтобы она могла фильтровать темы и пользователей.get-tweet.js
: предоставляет функцию, которая возвращает учетную запись твита из заданного открытого ключа.send-tweet.js
: Предоставляет функцию, которая отправляет инструкциюSendTweet
нашей программе со всей необходимой информацией.
Объяснение композиций
В этом ZIP-файле есть еще одна папка, которую нужно объяснить: composables.
В VueJS мы называем «составными» функциями, которые используют API композиции для расширения поведения компонента. Если вы знакомы с React, это можно сравнить с хуками React для VueJS.
Поскольку некоторым компонентам требовались дополнительные функциональные возможности, я позволил себе создать несколько составных элементов, чтобы компоненты было легче читать.
useAutoresizeTextarea.js
: этот компонуемый используется в компонентеTweetForm.vue
и автоматически изменяет размер поля «контент» в зависимости от его содержимого. Таким образом, поле содержит только одну строку текста, с которой начинается, но расширяется по мере ввода пользователем.useCountCharacterLimit.js
: также используется компонентомTweetForm.vue
, этот компонуемый объект возвращает реактивный обратный отсчет символов на основе заданного текста и ограничения.useFromRoute.js
: этот составной объект используется многими компонентами. Это небольшой рефакторинг, который помогает справиться с хуками Vue Router. Обычно нам нужно добавить некоторый код, когда мы вводим маршрутизатор, и некоторый другой код, когда маршрут обновляется, но компоненты остаются прежними, например. тема меняется на странице тем. Эта функция позволяет нам один раз написать некоторую логику, которая будет запускаться для обоих событий.useSlug.js
: этот компонуемый используется для преобразования любого заданного текста в слаг. Например, фраза «Солана ПОТРЯСАЮЩАЯ» станет «солана-потрясающая». Это используется везде, где нам нужно убедиться, что тема предоставлена в виде слага. Таким образом, у нас снижается риск того, что пользователи, пишущие твиты на одну и ту же тему, не найдут твиты друг друга из-за чувствительности к регистру.
Вывод
Отлично, у нас появился пользовательский интерфейс! Я искренне надеюсь, что у вас не возникло никаких проблем на этом пути, временами интерфейсный мир может быть довольно неумолимым. Если у вас есть какие-либо проблемы, не стесняйтесь комментировать ниже или, что еще лучше, создайте новую проблему в репозитории проекта.
Говоря о репозиториях, вы можете просмотреть код этого эпизода в ветке episode-7
и сравнить код с предыдущим эпизодом, как обычно. На этот раз я также добавил еще одну ссылку для сравнения после коммита, который сгенерировал внешний интерфейс через vue create app
, чтобы вы могли увидеть, что мы изменили впоследствии.
Просмотреть эпизод 7 на GitHub
Сравните с Эпизодом 6/[Сравните после создания приложения VueJS](https://github.com /lorisleiva/solana-twitter/compare/1d7642e092a5946e6f08f5b90cadc612051b9b75...эпизод-7)
В следующих трех эпизодах мы соединим наш фиктивный пользовательский интерфейс с реальными данными и с реальными взаимодействиями с нашей программой Solana. Мы начнем с интеграции нашего интерфейса с кошельками Solana, такими как Phantom, чтобы мы могли идентифицировать подключенного пользователя в нашем приложении. Увидимся в следующем выпуске!