Мы построим свой твиттер с лайками и комментариями! Присоединяйся, заодно разберешься с крутым 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
Комментарии