Твиттер на Vue.js: руководство для начинающих
Мы построим свой твиттер с лайками и комментариями! Присоединяйся, заодно разберешься с крутым JavaScript-фреймворком Vue.js.
Vue.js – это фреймворк, оптимизированный для прогрессивной интеграции в любой проект. Будь у вас огромное приложение или пустое место, на котором скоро вырастет что-то интересное, Vue вам подходит!
У него есть одно очень важное преимущество перед прочими фронтенд-инструментами: низкая кривая обучения. Правда-правда, для работы достаточно знать только HTML, CSS и JavaScript основы.
Все хорошие JavaScript фреймворки, и Vue в том числе, структурируют приложение в духе лучших практик, позволяя легко и просто расширять его по мере необходимости. К услугам разработчика также внушительный набор полезных утилит для интерфейса, чтобы не изобретать собственные колеса по поводу и без.
Фреймворк Vue.js имеет по-настоящему крутую экосистему. В нее прямо из коробки входит то, что вы привыкли искать в различных дополнительных библиотеках, – Vue Router и Vuex.
Кажется, похвал достаточно. Вам наверняка уже не терпится начать работать с этим замечательным инструментом. Не будем медлить – создадим на Vue свой твиттер!
Заготовка
Начнем с базовой разметки:
<div class="status" id="app"> <div class="tweet-content"> <img src="https://pbs.twimg.com/profile_images/1070775214370373633/borvu2Xx_400x400.jpg" class="logo" alt="Vue Vixens DC logo"> <div class="tweet"> <a href="https://twitter.com/vuevixensdc">Vue Vixens DC</a> <span>@VueVixensDC · Mar 20</span> <p class="tweet-text"> Hello, World! </p> <div class="reactions like"> <span class="like"> <span>♡</span> </span> </div> </div> </div> <div class="comment-bar"> <textarea placeholder="tweet your reply"> </textarea> <span class="characters-remaining"> 280 characters remaining </span> </div> </div>
body { background-color: #e6ecf0; font-family: sans-serif; } .tweet-content { display: flex; padding: 10px; } .status { width: 600px; margin: 20px auto; border-radius: 3px; background-color: white; } .logo { height: 75px; border-radius: 50%; padding: 5px; } .characters-remaining { font-size: 12px; } a { text-decoration: none; color: black; font-weight: bold; } span { color: grey; } .comment-bar { background-color: #f5f8fa; padding: 10px; border-top: 1px solid #e6ecf0; } textarea { width: 100%; border: 1px solid #1DA1F2; border-radius: 2px; padding: 2px; }
Посмотреть можно здесь.
Тут есть все, что нужно:
- готовый пост Hello World;
- сердечко, чтобы его лайкнуть;
- и поле для ввода комментария.
Пост нужно просто вывести на страницу. Сердечко будет реагировать на нажатия и становиться красным, если твит вам понравится. Кроме того, как в заправском твиттере, мы будем отслеживать размер комментария.
Итак, HTML и CSS уже есть, осталось самое простое – JavaScript-код :) Vue.js нам в этом поможет.
Настройка
На данном этапе важно разобраться в основах работы фреймворка, поэтому пока не стоит углубляться в тонкости его настройки. Чтобы было совсем просто, подключим Vue.js с CDN. Как только вы немножко разберетесь, взгляните на Vue CLI. Этот инструмент позволяет легко развернуть всю необходимую инфраструктуру для Vue-проектов.
Codepen все сделает за вас. Нажмите на кнопку с шестеренкой, откройте вкладку JavaScript и наберите Vue в поле поиска. Библиотека подключится к вашему проекту автоматически.
Чтобы начать пользоваться всеми благами Vue, нужно создать его экземпляр:
const app = new Vue()
Этому экземпляру можно передать объект, содержащий конфигурацию приложения и определение всей его логики.
Прежде всего нужно заполнить поле el
– DOM-элемент, который будет служить контейнером для всего приложения. Именно внутри этого контейнера Vue.js сможет развернуться на полную катушку. У нас это будет блок с классом status
:
const app = new Vue({ el: ".status" })
Данные
Конечно же, нужны данные. Например, это могут быть тексты твитов. Сейчас у нас всего один пост, да и в том сообщение прописано прямо в HTML. Так не пойдет. Тексты должны попадать на страницу динамически, например, из базы данных. Давайте обеспечим эту динамику:
const app = new Vue({ el: ".status", data: { tweetText: "Hello World!" } })
Все динамические данные нужно размещать в объекте data
, тогда Vue сможет вывести их на страницу. Давайте сразу же это и сделаем.
Если вы когда-нибудь работали с Pug, Handlebars или другим шаблонизатором, то найдете синтаксис интерполяции Vue довольно знакомым:
<p class="tweet-text"> {{ tweetText }} </p>
Вычисляемое выражение, например, переменная, помещается в двойные фигурные скобки. Потом фреймворк заменит всю эту конструкцию на текст "Hello World"
из объекта data
.
Поменяете текст в поле tweetText
объекта data
– он изменится и на странице. Вот она – динамика!
Вот так безо всяких усилий мы решили первую задачу – вывод поста. Что еще осталось сделать?
- Изменять по клику вид сердечка-лайка
- Считать количество оставшихся символов в поле ввода комментария
Для решения этих задач тоже потребуются некоторые данные, которые нужно поместить в объект data
:
data: { tweetText: "Hello World!", charactersRemaining: 280, liked: false }
Кстати, charactersRemaining
уже можно отправить на страницу:
<span class="characters-remaining"> {{ charactersRemaining }} characters remaining </span>
Лайками займемся через секунду, а пока поговорим о методах.
Методы
Данные – это замечательно, но с ними еще нужно как-то работать. Например, изменять после некоторых действий пользователя. Для этого существуют методы. Vue предлагает определять их в поле methods
объекта конфигурации.
const app = new Vue({ el: ".status", data: { tweetText: "Hello World!", charactersRemaining: 280, liked: false }, methods: {} })
Подсчет символов
Начнем, пожалуй, с подсчета символов и добавим метод countCharacters
:
methods: { countCharacters: function() { } }
Какова логика этой функции?
- Сначала нужно посчитать, сколько символов пользователь набрал в
textarea
. - Затем вычесть это количество из лимита (280 символов).
Для текста комментария нужно создать отдельное поле в объекте data
. А при вводе оно будет обновляться.
data: { tweetText: 'Hello World!', charactersRemaining: 280, commentText: '', liked: false },
<textarea placeholder="tweet your reply" v-model="commentText"></textarea>
Тут появился новый атрибут v-model
. Его миссия – синхронизировать содержимое textarea, которое вводит пользователь, и значение поля commentText
из объекта data
. v-model
– это одна из директив Vue. Все директивы начинаются с префикса v-
.
Теперь у нас всегда будет актуальный текст комментария для работы. Нужно каким-то образом получить его внутри метода: для этого достаточно просто обратиться к объекту this
, в нем все есть.
Зная текст комментария, легко посчитать, сколько символов осталось:
methods: { countCharacters: function() { this.charactersRemaining = 280 - this.commentText.length } }
Метод countCharacters
необходимо запускать при каждом изменении textarea
. Чтобы отслеживать эти события, воспользуемся еще одной директивой – v-on
. Это почти то же самое, что и привычный вам addEventListener
, только гораздо удобнее и симпатичнее.
Директива v-on:input="countCharacters"
будет отслеживать событие input
и запускать countCharacters
при каждом его возникновении:
<textarea placeholder="tweet your reply" v-model="commentText" v-on:input="countCharacters" ></textarea>
I like it! ♥️
Вот мы и добрались до сердечка. Помните, что его нужно изменять при клике? Добавим еще один метод в объект methods
:
methods: { ... toggleLike: function () { } }
Эта функция будет просто переключать значение свойства data.liked
. К нему также можно обратиться через объект this
:
toggleLike: function () { this.liked = !this.liked }
Запускать метод toggleLike нужно при клике на сердечко – это мы уже умеем:
<div class="reactions like" v-on:click="toggleLike"> ... </div>
Условия
Замечательно! Теперь приложение всегда знает, в каком состоянии находится сердечко. Нужно обозначить это на самой странице.
Vue прекрасно умеет работать с логическими данными благодаря условной директиве v-if
. Достаточно просто вывести нужный эмоджи в блоке .reactions
:
<span v-if="liked">♥️</span>
Теперь, если лайка нет, сердечко будет полностью пропадать. Но мы запланировали другое поведение – оно должно становиться прозрачным. Чтобы добиться этого, добавим альтернативное условие:
<span v-if="liked">♥️</span> <span v-else>♡</span>
Ура! Можно лайкать!
Что получилось на данный момент, можно посмотреть здесь.
Компоненты
Теперь у нас есть все необходимое для работы с одним-единственным твитом. Почему бы не сделать целую ленту разных постов, как в настоящем твиттере? Давайте превратим готовый код в отдельный компонент.
Все современные JavaScript фреймворки используют компонентный подход. Он позволяет легко организовать расширяемые интерфейсы, используя большие куски верстки и логики повторно. Если вы собираетесь вставлять некоторый фрагмент в приложение более одного раза, скорее всего, вам нужно превратить его в компонент.
В реальном приложении, скорее всего, следовало бы разбить целый твит на подкомпоненты:
- комментарий;
- лайк;
- изображение профиля и т. д.
Но для простоты и наглядности сейчас мы не будем этого делать.
Создание компонента
Для начала нужно переместить логику из экземпляра Vue в отдельный компонент.
Метод Vue.component
позволяет использовать на странице несколько одинаковых блоков с разными данными. Он принимает два аргумента:
- название компонента, которое станет его HTML-тегом – пусть будет
tweet
; - объект конфигурации со всей логикой.
Vue.component("tweet", { data: function() { return { charactersRemaining: 280, commentText: "", liked: false } }, methods: { countCharacters: function() { this.charactersRemaining = 280 - this.commentText.length }, toggleLike: function() { this.liked = !this.liked } } })
Обратите внимание, поле data
из простого объекта превратилось в функцию, которая этот объект возвращает.
Для компонента нужен HTML-шаблон, который будет отображаться на странице. Просто положим туда уже существующий код:
template: `<div class="status"> <div class="tweet-content"> <img src="https://pbs.twimg.com/profile_images/1070775214370373633/borvu2Xx_400x400.jpg" class="logo" alt="Vue Vixens DC logo"> <div class="tweet"> <a href="https://twitter.com/vuevixensdc">Vue Vixens DC</a> <span>@VueVixensDC · Mar 20</span> <p class="tweet-text"> {{ tweetText }} </p> <div class="reactions"> <span v-on:click="toggleLike" class="like"> <span v-if="liked">♥️</span> <span v-else>♡</span> </span> </div> </div> </div> <div class="comment-bar"> <textarea placeholder="tweet your reply" v-model="commentText" v-on:input="countCharacters"> </textarea> <span class="characters-remaining"> {{ charactersRemaining }} characters remaining </span> </div> </div>`
Компонент твита готов!
Данные компонента
Конечно, каждый твит должен иметь собственный текст. Он будет передаваться извне через атрибут тега tweet
. А чтобы получить его, нужно добавить название атрибута в массив props:
Vue.component('tweet', { props: ['tweetText'], ... })
Vue-твиттер
Вернемся к приложению в целом. Нужно немного изменить конфигурационный объект, передаваемый в экземпляр Vue:
new Vue({ el: "#app" })
И подготовить HTML-базу для него:
<div id="app"></div>
А внутри главного контейнера уже можно разместить пару первых твитов:
<div id="app"> <tweet tweet-text="hello world!"></tweet> <tweet tweet-text="hi!"></tweet> </div>
Два одинаковых компонента с разными входными данными дают нам два самостоятельных твита, которые можно лайкать и комментировать.
Обратите внимание, HTML-атрибуты тега указаны в kebab-case, а в массиве props
свойство записано в camelCase, как рекомендует язык JavaScript. Преобразование между этими форматами осуществляется автоматически.
Текущая версия нашего твиттера тут.
Циклы
Хардкодить твиты в HTML – неправильно. Обычно есть некоторая структура данных, содержащая несколько постов, динамически полученных из базы. Нужно перебрать все сообщения и вывести их в нужном формате. Язык программирования JavaScript для подобных задач использует циклы. Конечно, и у Vue такой найдется.
Исходные данные должны находиться в поле data
экземпляра приложения:
new Vue({ el: "#app", data: { tweets: [ { id: 1, tweetText: "hello world!" }, { id: 2, tweetText: "hi!" } ] } })
А вывести их на страницу поможет директива v-for
:
<div id="app"> <tweet v-for="tweet in tweets" v-bind:key="tweet.id" v-bind:tweet-text="tweet.tweetText" ></tweet> </div>
Что еще за v-bind
? Эта штука позволяет привязать атрибуты (key
и tweet-text
) к некоторым данным и автоматически их обновлять при необходимости.
Атрибут key
нужен фреймворку для однозначной идентификации объектов, генерируемых в цикле.
Теперь можно добавить в массив сколько угодно твитов – и все они появятся на странице!
Полезные ресурсы для изучения Vue
Компонент твита можно расширить множеством интересных функций:
- изменять аватар от поста к посту;
- выводить дату создания или данные пользователя;
- сообщать о превышении лимита символов и блокировать ввод;
- использовать API Twitter для вывода реальных твитов;
- публиковать комментарии.
Vue все эти возможности нам предоставляет, нужно только узнать его получше.
Вот несколько хороших ресурсов для этого:
- Введение во Vue.js от Sarah Drasner
- Документация фреймворка
- Vue 2019: дорожная карта начинающего разработчика
- 5 практических примеров использования Vue.js
- Хороший, спорный, злой Vue.js: опыт перехода с React
Оригинал от Ali Spittel: A Complete Beginner's Guide to Vue