Переход с jQuery на Vue
Данное руководство предназначено для тех разработчиков, которые имеют опыт работы с jQuery. Но при этом хотят поближе познакомиться с Vue.
Вспомним, для чего чаще всего используется jQuery:
- Нахождение элементов в DOM.
- Изменение элементов DOM.
- Считывание и установка значений формы.
- Валидация формы.
- Ajax-вызов и обработка результатов;
- Обработка событий.
- Изменение стилей элемента.
Но давайте начнем с рассмотрения основных различий приложений, созданных с помощью этих фреймворков.
Когда лучше использовать Vue
С помощью jQuery на веб-странице можно реализовать любой функционал и в любом порядке. Например, сегодня клиент может попросить создать форму с валидацией, а через месяц разместить в заголовке сайта поисковую форму на Ajax.
Разработку проекта на Vue мы начинаем с определения «области» в DOM, с которой будем работать. Рассмотрим пример простой веб-страницы:
<body>
<header>
Fancy header stuff here
</header>
<div id="sidebar">
Some sidebar doohicky here
</div>
<main>
<p>
Main site content here, super important stuff...
</p>
<div id="loginForm">
A login form of course
</div>
</main>
</body>
В обычном jQuery-приложении мы можем написать код для работы с заголовком, боковой панелью, формой входа или чем-то еще.
$(document).ready(function() {
$('header') // что-то...
$('#sidebar') // что-то...
$('#loginForm') // что-то...
});
В Vue-приложении сначала нужно указать элемент, с которым будем работать. Представьте, что клиент попросил добавить валидацию к элементу loginForm. Тогда код будет следующим:
new Vue({
el: '#loginForm',
// Здесь код...
});
Поэтому мы добавим второе приложение, если позже клиент решит добавить что-то в боковую панель:
new Vue({
el:'#loginForm',
// Здесь код...
});
new Vue({
el:'#sideBar',
// Здесь код...
});
Благодаря этому мы получаем преимущество инкапсуляции. Если случайно используем переменную с общим именем, это не вызовет конфликтов с другими частями кода.
Поиск в DOM
Рассмотрим конкретный пример. На странице есть кнопка, и когда она нажата, что-то происходит:
<button id="myButton">Click Me!</button>
<!-- Другие элементы... -->
<script>
$(document).ready(function() {
$('#myButton').click(function() {
alert(1);
});
});
</script>
Теперь пример реализации на Vue:
<div id="app">
<button v-on:click="doSomething">Click Me!</button>
</div>
<script>
const app = new Vue({
el:'#app',
methods: {
doSomething: function() {
alert(1);
}
}
});
</script>
Vue-приложение более объемно. Но обратите внимание, что разметка имеет прямую связь между действием («click») и вызываемой функцией.
Код Vue не привязан к DOM. Кроме этого не нужно сильно беспокоиться о значении идентификаторов и селекторов. Если я изменю класс или идентификатор кнопки, не нужно беспокоиться об обновлении селекторов.
Рассмотрим другой пример: поиск и изменение текста в DOM. Представьте, что кнопка при нажатии меняет текст в другой части DOM.
<button id="myButton">Click Me!</button>
<span id="result"></span>
<!-- Другие вещи... -->
<script>
$(document).ready(function() {
$('#myButton').click(function() {
$('#result').text('You clicked me, thanks!');
});
});
</script>
Я добавил новый элемент span. Теперь, когда кнопка нажата, мы используем другой селектор, чтобы найти его. Для изменения текста используем jQuery.
Теперь рассмотрим Vue-версию:
<div id="app">
<button v-on:click="doSomething">Click Me!</button>
<!-- При клике изменяем текст в span -->
<span>{{resultText}}</span>
</div>
<script>
const app = new Vue({
el: '#app',
data: {
resultText: ''
},
methods: {
doSomething: function() {
this.resultText = 'You clicked me, thanks!';
}
}
});
</script>
В этом примере c помощью синтаксиса Vue мы указываем, что хотим отобразить переменную внутри элемента span.
Чтение и запись переменных формы
Рассмотрим простой jQuery- пример: чтение значений нескольких полей формы и установка значения другого:
<form>
<input type="number" id="first"> +
<input type="number" id="second"> =
<input type="number" id="sum">
<button id="sumButton">Sum</button>
</form>
<script>
$(document).ready(function() {
let $first = $('#first');
let $second = $('#second');
let $sum = $('#sum');
let $button = $('#sumButton');
$button.on('click', function(e) {
e.preventDefault();
let total = parseInt($first.val(),10) + parseInt($second.val(),10);
$sum.val(total);
});
});
</script>
Приведенный выше код показывает, как с помощью JQuery считывать и записывать значения полей формы, используя метод val().
В результате мы получаем из DOM четыре элемента (все три поля формы и кнопку) и используем простую математику для генерации результата.
Теперь рассмотрим Vue-версию:
<form id="myForm">
<input type="number" v-model.number="first"> +
<input type="number" v-model.number="second"> =
<input type="number" v-model="sum">
<button @click.prevent="doSum">Sum</button>
</form>
<script>
new Vue({
el: '#myForm',
data: {
first: 0,
second: 0,
sum: 0
},
methods: {
doSum: function() {
this.sum = this.first + this.second;
}
}
})
</script>
Здесь используется несколько сокращений Vue. v-model - так Vue создает двустороннюю привязку между значениями в DOM и JavaScript. Переменные блока data будут автоматически синхронизироваться с полями формы. Измените данные, и форма обновится. Измените форму, и данные обновятся.
number - это флаг Vue, который указывает обрабатывать строковые значения полей формы как числа.
@click.prevent - сначала @click определяет обработчик клика для кнопки. Затем часть .prevent блокирует поведение браузера, используемое по умолчанию при отправке формы (эквивалент event.preventDefault()).
Последняя часть - это добавление метода doSum, привязанного к кнопке. Он работает с переменными data (которые Vue делает доступными в области видимости this).
Наконец, мы можем полностью избавиться от кнопки:
<form id="myForm">
<input type="number" v-model.number="first"> +
<input type="number" v-model.number="second"> =
<input type="number" v-model="sum">
</form>
<script>
new Vue({
el: '#myForm',
data: {
first: 0,
second: 0
},
computed: {
sum: function() {
return this.first + this.second;
}
}
})
</script>
Одна из особенностей Vue - это вычисляемые свойства. Это виртуальные значения, которые распознают, когда обновляются их производные значения. В приведенном выше коде, как только любое из двух полей формы изменится, сумма будет обновлена. Это работает и вне полей формы. Мы могли бы найти сумму следующим образом:
The total is {{sum}}.
Работа с Ajax
В jQuery просто использовать Ajax. А что предлагает Vue, чтобы облегчить работу с Ajax?
Vue.js позволяет разработчикам самостоятельно решать, как обрабатывать HTTP-запросы. Первое, что нужно рассмотреть, это Axios - библиотеку, созданную на базе Promise. Вот простой пример (взят из README- файла этой библиотеки):
axios.get('/user?ID=12345')
.then(function (response) {
// успешная обработка
console.log(response);
})
.catch(function (error) {
// ошибка обработки
console.log(error);
})
.then(function () {
// всегда выполняется
});
Разумеется, Axios поддерживает запросы POST и позволяет указывать заголовки.
Но я являюсь большим поклонником Fetch. Это не внешняя библиотека, а стандартный способ выполнения HTTP-запросов. Fetch хорошо поддерживается браузерами.
Fetch создан на основе Promise и имеет дружественный API:
fetch('http://example.com/movies.json')
.then(function(response) {
return response.json();
})
.then(function(myJson) {
console.log(JSON.stringify(myJson));
});
И Axios, и Fetch охватывают все типы HTTP-запросов.
Давайте проведем простое сравнение. Вот реализация на jQuery, в которой используется API Star Wars.
<h1>Star Wars Films</h1>
<ul id="films">
</ul>
<script>
$(document).ready(function() {
$.get('https://swapi.com/api/films', function(res) {
let list = '';
res.results.forEach(function(r) {
list += `<li>${r.title}</li>`;
});
$('#films').html(list);
});
});
</script>
В приведенном выше примере я использую $.get, чтобы вызвать API и вернуть список фильмов. Затем я создаю список заголовков в виде элементов списка li с этими данными и вставляю их в ul.
Рассмотрим аналогичный пример, в котором используется Vue:
<div id="app">
<h1>Star Wars Films</h1>
<ul>
<li v-for="film in films">{{film.title}}</li>
</ul>
</div>
<script>
const app = new Vue({
el: '#app',
data: {
films: []
},
created() {
fetch('https://swapi.com/api/films')
.then(res => res.json())
.then(res => {
this.films = res.results;
});
}
})
</script>
Лучшая часть приведенного выше кода - использование шаблона v-for. Внедрение Vue делает процесс более естественным.
Полный пример
Допустим, клиент попросил создать современный интерфейс поиска с поддержкой Ajax для API товара. В реализованном решении должны присутствовать:
- Фильтрация по названию и категории товара.
- Валидация формы по поисковому запросу или категории.
- Пока API запрашивается, пользователю должно отображаться сообщение, а кнопка отправки должна быть отключена.
- После завершения поиска вывести сообщение о том, что товары не были найдены или список найденных.
Давайте начнем с jQuery-версии. Код HTML:
<form>
<p>
<label for="search">Search</label>
<input type="search" id="search">
</p>
<p>
<label for="category">Category</label>
<select id="category">
<option></option>
<option>Food</option>
<option>Games</option>
</select>
</p>
<button id="searchBtn">Search</button>
</form>
<div id="status"></div>
<div id="results"></div>
У нас форма с двумя фильтрами и двумя блоками div. Теперь код.
const productAPI = 'https://wt-c2bde7d7dfc8623f121b0eb5a7102930-0.sandbox.auth0-extend.com/productSearch';
$(document).ready(() => {
let $search = $('#search');
let $category = $('#category');
let $searchBtn = $('#searchBtn');
let $status = $('#status');
let $results = $('#results');
$searchBtn.on('click', e => {
e.preventDefault();
// Сначала очищаем предыдущие элементы
$status.html('');
$results.html('');
// теперь выполняем валидацию формы
let term = $search.val();
let category = $category.val();
if(term === '' && category === '') {
$status.html('You must enter a term or select a category.');
return false;
}
$searchBtn.attr('disabled','disabled');
$status.html('Searching - please stand by...');
$.post(productAPI, { name:term, category:category }, body => {
$searchBtn.removeAttr('disabled');
$status.html('');
if(body.results.length === 0) {
$results.html('<p>Sorry, no results!</p>');
return;
}
let result = '<ul>';
body.results.forEach(r => {
result += `<li>${r.name}</li>`
});
result += '</ul>';
$results.html(result);
});
});
});
Приведенный выше код начинается с создания набора переменных для полей формы, кнопок и элементов div.
Логика реализации находится в обработчике нажатия кнопки. Мы выполняем валидацию. Если все в порядке, отправляем POST-запрос к API. После получения ответа отображаем результаты или сообщение, что ничего не найдено.
Теперь рассмотрим Vue-версию. Начинаем с HTML:
<div id="app">
<form>
<p>
<label for="search">Search</label>
<input type="search" v-model="search">
</p>
<p>
<label for="category">Category</label>
<select v-model="category">
<option></option>
<option>Food</option>
<option>Games</option>
</select>
</p>
<button @click.prevent="searchProducts" :disabled="searchBtnDisabled">Search</button>
</form>
<div v-html="status"></div>
<ul v-if="results">
<li v-for="result in results">{{result.name}}</li>
</ul>
</div>
В приведенном выше коде:
- Макет обернут в div, который можно использовать, чтобы сообщить Vue, где работать.
- Используется директива v-model в полях формы для облегчения работы с данными.
- Используется @click.prevent для обработки основной операции поиска.
- Используется :disabled для проверки т, привязана ли кнопка к значению в Vue-приложении.
- Используется v-if для условного отображения списка результатов v-for и обработки итерации.
Теперь посмотрим на код приложения.
const productAPI = 'https://wt-c2bde7d7dfc8623f121b0eb5a7102930-0.sandbox.auth0-extend.com/productSearch';
const app = new Vue({
el: '#app',
data: {
search: '',
category: '',
status: '',
results: null,
searchBtnDisabled: false
},
methods: {
searchProducts:function() {
this.results = null;
this.status = '';
if(this.search === '' && this.category === '') {
this.status = 'You must enter a term or select a category.';
return;
}
this.searchBtnDisabled = true;
this.status = 'Searching - please stand by...';
fetch(productAPI, {
method: 'POST',
headers: {
'Content-Type':'application/json'
},
body: JSON.stringify({name:this.search,category:this.category})
}).then(res => res.json())
.then(res => {
this.status = '';
this.searchBtnDisabled = false;
this.results = res.results;
if(this.results.length === 0) this.status = '<p>Sorry, no results!</p>';
});
}
}
});
Первый блок, который должен быть вызван - это набор data полей. Некоторые из них соответствуют полям формы, а другие - результатам, сообщениям о состоянии и т. п.
Метод searchProducts обрабатывает многое из того, что и JQuery- версия. Но в нем намного кода, связанного с DOM.
JQuery умер! Да здравствует Vue!
Vue может стать следующим шагом для тех людей, которые привыкли работать с jQuery. Данный фреймворк позволят создавать сложные приложения, и включает в себя отличный инструмент командной строки. К тому же Vue может использоваться как замена jQuery при решении более простых задач.