Vue.js для новичков: уроки, примеры и разбор основ

:

10.03.2017

Если бы я подводила итог своего опыта с Vue.js одним предложением, я бы сказала что-то вроде «Это разумный фреймворк» или «Он дает мне то что я хочу, когда я это хочу и никогда не ставит палки в колеса». Снова и снова, изучая Vue, я радуюсь его ясности и элегантности.

Это мое собственное введение в Vue.js. Это статья, которую я пожелала бы иметь, когда начала изучать этот фреймворк. Если вы хотите более беспристрастный подход, то лучше посетите официальный сайт Vue, там очень хорошо продуманное и понятное руководство.

Серия статей

  1. Рендеринг, директивы и события (Вы здесь)
  2. Components, Props и Slots
  3. Vue-cli
  4. Vuex
  5. Анимации

Интересно в Vue то, что он впитывает в себя все успешные технологии от других фреймворков, не становясь при этом хаотичным. Вот некоторые моменты, которые я для себя выделила:

  • Virtual DOM с реактивными компонентами, которые дают только View слоя, props и Redux-подобный магазин, похожий на React.
  • Условный рендеринг, а также сервисы, аналогичные Angular.
  • Вдохновленный Polymer простотой и производительностью, Vue предлагает аналогичную разработку где HTML, CSS и JavaScript объеденины.

Преимущества Vue над конкурентами: чистота, семантическое преподношение API, производительность лучше чем у React, не используются polyfills как в Polymer, и изолированный, менее жесткий вид, чем у Angular, архитектура которого — MVC.

Я могла бы продолжать, но, лучше, если вы почитаете, основанное на мнении общества, сравнение с другими фреймворками. Оно того стоит, но можете сделать это и попозже, если хотите погрузиться в код.

Начнём!

Мы не можем начать без классического «Hello, world!» примера. Давайте попробуем, просто запустите это:

1
2
3
<div id="app">
{{ text }} Приятно познакомиться, Vue.
</div>

<div id="app"> {{ text }} Приятно познакомиться, Vue. </div>

1
2
3
4
5
6
new Vue({
 el: '#app',
 data: {
   text: 'Привет, Мир!'
 }
});

new Vue({ el: '#app', data: { text: 'Привет, Мир!' } });

See the Pen LypmvZ by FurFurFur (@FurFurFur) on CodePen.29134

Если вы знакомы с React, то найдете сходство. Мы сбежали в JavaScript переменную, прямо посреди контента, с помощью «усиков», но различие только одно — сверху мы работаем с HTML вместо JSX. JSX довольно прост в работе, но зато не придется менять class на className и т.д. Заметьте, какой легкий «Hello World!» у нас получился.

Теперь давайте попробуем мое любимое в Vue: циклы и условный рендеринг.

Условная визуализация

Допустим у меня есть набор элементов, например навигация, и я знаю что еще раз буду использовать её. Можно поместить её в массив, чтобы обновить в нескольких местах динамично и последовательно. В ванильном JS (с Babel) мы могли бы сделать что-то вроде этого: создать массив, потом создать пустую строку, где мы оборачиваем каждый элемент в <li>, а затем обернуть все это в <ul> и добавить в DOM с innerHTML:

1
<div id="container"></div>

<div id="container"></div>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const items = [
  'Пункт меню',
  'Еще один пункт меню',
  'Какая-то штука',
  'Бла-бла-бла'
];
 
function listOfStuff() {
  let full_list = '';
  for (let i = 0; i < items.length; i++) {
      full_list = full_list + `<li> ${items[i]} </li>`
  }
  const contain = document.querySelector('#container');
  contain.innerHTML = `<ul> ${full_list} </ul>`;     
}
 
listOfStuff();

const items = [ 'Пункт меню', 'Еще один пункт меню', 'Какая-то штука', 'Бла-бла-бла' ]; function listOfStuff() { let full_list = ''; for (let i = 0; i < items.length; i++) { full_list = full_list + `<li> ${items[i]} </li>` } const contain = document.querySelector('#container'); contain.innerHTML = `<ul> ${full_list} </ul>`; } listOfStuff();

See the Pen KmdeWj by FurFurFur (@FurFurFur) on CodePen.29134

Работает как надо, но для такой стандартной штуки выглядит не очень красиво. Давайте реализуем то же самое циклом с v-for:

1
2
3
4
5
6
7
<div id="app">
  <ul>
    <li v-for="item in items">
      {{ item }}
    </li>
  </ul>
</div>

<div id="app"> <ul> <li v-for="item in items"> {{ item }} </li> </ul> </div>

1
2
3
4
5
6
7
8
9
10
11
const app4 = new Vue({
  el: '#app',
  data: {
    items: [
     'Пункт меню',
     'Еще один пункт меню',
     'Какая-то штука',
     'Бла-бла-бла'
    ]
  }
});

const app4 = new Vue({ el: '#app', data: { items: [ 'Пункт меню', 'Еще один пункт меню', 'Какая-то штука', 'Бла-бла-бла' ] } });

See the Pen PmPXQj by FurFurFur (@FurFurFur) on CodePen.29134

Красиво и понятно. Если вы знакомы с Angular, это тоже будет знакомо вам. Я считаю это отличным способом условной визуализации. И если вам вдруг понадобится обновить код, то это не составит труда.

Вот еще один хороший пример динамически связанной v-model. Смотрите:

1
2
3
4
5
<div id="app">
  <h3>Печатать здесь:</h3>
  <textarea v-model="message" class="message" rows="5" maxlength="72"></textarea><br>
  <p class="booktext">{{ message }} </p>
</div>

<div id="app"> <h3>Печатать здесь:</h3> <textarea v-model="message" class="message" rows="5" maxlength="72"></textarea><br> <p class="booktext">{{ message }} </p> </div>

1
2
3
4
5
6
7
8
new Vue({
  el: '#app',
  data() {
    return {
      message: 'Неплохое местечко, чтобы что то написать!'  
    }
  }
});

new Vue({ el: '#app', data() { return { message: 'Неплохое местечко, чтобы что то написать!' } } });

See the Pen KmdboB by FurFurFur (@FurFurFur) on CodePen.29134

Вы могли заметить две вещи в этом примере. Во-первых, что ничего не печатается непосредственно в книгу и нет динамического обновления текста. Однако, Vue позволяет нам очень легко настроить двустороннюю привязку между <textarea> и ><p> с v-model.

Во вторых, то, что мы вводим данные прямо в функции. Хотя, в этом примере, всё будет работать и без этого. Мы могли бы просто поместить данные в объект как в предыдущих примерах. Но это будет работать только для экземпляра Vue и точно так же в приложении (что не хорошо для отдельных компонентов). Нормально для одного экземпляра, но вдобавок будут передаваться данные во все дочерние компоненты. Эта практика ввода данных в функции понадобится, когда мы начнем использовать компоненты и захотим чтобы каждый сохранял свое собственное состояние.

Это не единственные способы привязок ввода, даже v-if имеет альтернативу v-show, который не будет подключать/отключать компонент, а вместо этого оставит его в DOM и сменит видимость.

Есть много других директив, но здесь примеры тех, которые лично я использую очень часто. Вдобавок некоторые из них имеют сокращения, мы рассмотрим их здесь. Далее мы будем в основном использовать сокращения, так что неплохо было бы вам ознакомиться с ними в этой таблице.

Директива Сокращение Назначение Пример
v-if, v-else-if, v-else нет Условный рендеринг <g v-if="flourish === 'A'"></g>
<g v-else-if="flourish === 'B'"></g>
<g v-else></g>
v-bind : Динамическая привязка атрибутов или передача props <div :style="{ background: color }"></div>
v-on @ Добавляет слушатель событий к элементу <button @click="fnName"></button>
v-model нет Создает двустороннюю привязку <textarea rows="5" v-model="message" maxlength="72"></textarea>
v-pre нет Пропускает компиляцию для исходного контента, может повысить производительность <div v-pre>{{ Исходное содержимое без методов }}</div>
v-once нет Не повторяет рендеринг <div class=”v-once”>Отключить повторный рендеринг</div>
v-show нет Покажет или скроет компонент / элемент на основе его состояния, но оставит его в DOM без размонтирования (в отличие от v-if) <child v-show=”showComponent”></child> (переключает видимость, когда включен параметр showComponent)

Есть и другие модификаторы событий и API обработчики для ускорения разработки, например:

@ Mousemove.stop на подобии e.stopPropogation ()
@ Mousemove.prevent это как e.preventDefault ()
@ Submit.prevent больше не будет перезагружать страницу
@ Click.once не следует путать с v-once, этот click event будет запущен только один раз.
v-model.lazy не заполнит контент автоматически, а будет ждать, пока не произойдет событие.

Вы даже можете забиндить свои собственные клавиши.

Попробуем рассмотреть всё это в примерах!

Обработка событий

Привязка данных это хорошо, но это далеко от обработчиков событий, поэтому давайте идти дальше! Это одна из моих любимых частей. Мы будем использовать привязку и слушатель события, чтобы слушать события DOM.

Есть несколько способов создания удобных методов. Как и в ванильном JS, вы можете назвать функции как хотите, но методы обычно называют, ну.. методами!

1
2
3
4
5
6
7
8
9
10
11
12
13
new Vue({
  el: '#app',
  data() {
   return {
    counter: 0
   }
  },
  methods: {
   increment() {
     this.counter++;
   }
  }
});

new Vue({ el: '#app', data() { return { counter: 0 } }, methods: { increment() { this.counter++; } } });

1
2
3
<div id="app">
  <p><button @click="increment">+</button> {{ counter }}</p>
</div>

<div id="app"> <p><button @click="increment">+</button> {{ counter }}</p> </div>

Мы создаем метод, который называется increment, и можно увидеть, что он автоматически привязывается к this и будет ссылаться на данные в этом примере и компонентах. Я люблю этот вид автоматической привязки. Приятно, что не придется смотреть console.log, чтобы увидеть на что ссылается this.  Здесь мы используем сокращение @click для привязки к click событию.

Методы не единственный способ создать пользовательскую функцию. Можно также использовать watch. Основное отличие заключается в том, что методы хороши для небольших синхронных вычислений, в то время как watch полезнее с запланированными, асинхронными или дорогостоящими операциями в ответ на изменение данных. Чаще всего я предпочитаю использовать watch с анимацией.

Давайте пойдем немного дальше и посмотрим, как пройдет само событие и сделаем динамические привязки стиля. Если вы помните таблицу выше, вместо того чтобы писать v-bind, вы можете использовать сокращение :, так что мы легко можем связать стиль (как и любые другие атрибуты), используя :style и прописать структуру css, или использовать :class. Есть много применений для этого вида привязки.

В приведенном ниже примере мы используем hsl(), цветовой тон здесь является кругом степеней цвета, который заполняет всё вокруг. Это пример использования, который не подведет, так мы отслеживаем нашу мышь в пикселях по экрану, а фон будет соответствующе менять цвет.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
new Vue({
  el: '#app',
  data() {
    return {
      counter: 0,
      x: 0
    }
  },
  methods: {
    increment() {
      this.counter++;
   },
   decrement() {
     this.counter--;
   },
   xCoordinate(e) {
     this.x = e.clientX;
   }
  }
});

new Vue({ el: '#app', data() { return { counter: 0, x: 0 } }, methods: { increment() { this.counter++; }, decrement() { this.counter--; }, xCoordinate(e) { this.x = e.clientX; } } });

1
2
3
4
<div id="app" :style="{ backgroundColor: `hsl(${x}, 80%, 50%)` }" @mousemove="xCoordinate">
  <p><button @click="increment">+</button> {{ counter }} <button @click="decrement">-</button></p>
  <p>Пикселей пройдено: {{ x }}</p>
</div>

<div id="app" :style="{ backgroundColor: `hsl(${x}, 80%, 50%)` }" @mousemove="xCoordinate"> <p><button @click="increment">+</button> {{ counter }} <button @click="decrement">-</button></p> <p>Пикселей пройдено: {{ x }}</p> </div>

See the Pen rmOoqg by FurFurFur (@FurFurFur) on CodePen.29134

Можете видеть, что нам даже не нужно передавать событие обработчику @click, Vue автоматически передаст его, чтобы быть доступным в качестве параметра для метода (показано как e).

Кроме того, могут быть также использованы нативные методы, например, event.clientX, и их легко объединить с this. При привязывании стиля на элементе используется верблюжийРегистр для свойств CSS через дефис. Этот пример наглядно показывает как проста и понятна работа с Vue.js.

На самом деле, нам не очень то и нужно создавать метод, мы можем также увеличить счетчик непосредственно встроенный в компонент, если событие достаточно простое:

1
2
3
4
5
6
7
8
9
10
11
<div id="app">
  <div class="item">
    <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/28963/backpack.jpg" width="235" height="300"/>
    <div class="quantity">
      <button class="inc" @click="counter > 0 ? counter -= 1 : 0">-</button>
      <span class="quant-text">Количество: {{ counter }}</span>
      <button class="inc" @click="counter += 1">+</button>
    </div>
    <button class="submit" @click="">Подтвердить</button>
  </div><!--item-->
</div>

<div id="app"> <div class="item"> <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/28963/backpack.jpg" width="235" height="300"/> <div class="quantity"> <button class="inc" @click="counter > 0 ? counter -= 1 : 0">-</button> <span class="quant-text">Количество: {{ counter }}</span> <button class="inc" @click="counter += 1">+</button> </div> <button class="submit" @click="">Подтвердить</button> </div><!--item--> </div>

1
2
3
4
5
6
7
8
new Vue({
  el: '#app',
  data() {
    return {
      counter: 0
    }
  }
});

new Vue({ el: '#app', data() { return { counter: 0 } } });

See the Pen YVyddp by FurFurFur (@FurFurFur) on CodePen.29134

Заметьте, что мы обновляем состояние непосредственно в обработчике, без метода @click вообще, также мы добавили немного логики (как будто это счётчик какого-то сайта продаж). Когда логика становится слишком сложной, тогда, принеся в жертву читаемость, лучше переместить его в метод. Хорошо если есть такая возможность.

Серия статей

  1. Введение в Vue.js: Рендеринг, директивы и события (Вы здесь)
  2. Введение в Vue.js: Components, Props и Slots
  3. Vue-cli
  4. Vuex
  5. Анимации
Оригинальная статья: https://css-tricks.com/intro-to-vue-1-rendering-directives-events/