Твиттер на Vue.js: руководство для начинающих

Мы построим свой твиттер с лайками и комментариями! Присоединяйся, заодно разберешься с крутым JavaScript-фреймворком Vue.js.

Твиттер на 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 все эти возможности нам предоставляет, нужно только узнать его получше.

Вот несколько хороших ресурсов для этого:

Оригинал от Ali Spittel: A Complete Beginner's Guide to Vue

Расскажите нам о своих проектах, созданных с Vue :)

Комментарии

ВАКАНСИИ

Добавить вакансию
Разработчик C++
Москва, по итогам собеседования

ЛУЧШИЕ СТАТЬИ ПО ТЕМЕ