В Vue мы любим одну вещь — реактивность системы. Если мы изменим значение данных, это вызовет обновление страницы, чтобы отразить это изменение.
Например:
varapp = newVue({
el: '#app',
data: {
message: "Hello World"
}
});
setTimeout(function() {
app.message = "Goodbye World";
}, 2000)
<divid="app">
<!--Renders as "Hello World"...-->
<!--Then after 2 seconds re-renders as "Goodbye World"-->
{{ message }}
</div>
Свойства данных, как message
в этом примере, являются реактивными , что означает, что они будут вызывать повторную визуализацию при изменении.
Подводные камни автоматической конфигурации реактивности
Vue автоматически настраивает реактивность всякий раз, когда вы создаете свойство данных, вычисляемое свойство, привязываете подпорку и т. Д. Эта автоматическая настройка удобна при кодировании приложения, поскольку она может:
- Сэкономьте нам время.
- Сделайте наш код лаконичным.
- Помогите минимизировать нашу познавательную нагрузку.
Это просто делает вещи проще. Но эта простота может вернуться, чтобы укусить нас! Подводный камень в том, что, как и в случае с автоматической машиной, автоматическая реактивность делает нас ленивыми, и когда она не работает, мы понятия не имеем, почему!
Когда хорошая реактивность становится плохой
Студент курса Ultimate Vue.js для разработчиков, которого я преподаю, на днях принес мне интересную проблему. Он работал над Vue.js Poster Shop , первым проектом курса, который требует от вас создания корзины покупок с помощью Vue.
Товар, отображаемый в магазине, изначально представлен так:
varmyProduct = {
id: 1,
name: 'My Product',
price: 9.99
};
Но когда вы добавляете товар в корзину, вам также нужно количество, с которым он справился таким способом:
functionaddToCart(id) {
varitem = this.cart.findById(id);
if (item) {
item.qty++;
} else {
item.qty = 1;
this.cart.push(item);
}
}
addToCart(myProduct.id);
Логика метода заключается в следующем:
- Найдите предмет в корзине.
- Если это там, увеличьте количество.
- Если его там нет, дайте ему 1 и добавьте в корзину.
Появляется проблема
Шаблон корзины покупок просто отображает список элементов корзины:
<ul class="shopping-cart">
<li v-for="item in cart">{{ item.name }} x {{ item.qty }}</li>
<!-- Renders: myProduct x 1 -->
</ul>
Проблема заключалась в том, что, независимо от значения qty
, шаблон всегда отображал значение «1».
Первоначально я думал, что логика addToCart
функции должна быть неправильной. Но после некоторой отладки я обнаружил, что qty
свойство действительно увеличивается при каждом вызове метода, так что это не проблема.
Как реактивность работает под капотом
Хотя в большинстве случаев система реактивности Vue вызывает легкую форму радости, она может вызвать замешательство и разочарование, если она не работает так, как вы ожидаете.
Этого можно избежать, если вы понимаете, как это работает.
Добытчики и сеттеры
Поведение по умолчанию для объекта JavaScript при обращении к нему заключается в получении или изменении соответствующего свойства напрямую. Например:
varmyObj={
a:"Hello World"
};
console.log(myObj.a)// "Hello World"
Но когда get
и set
были определены свойства псевдо, эти функции переопределить поведение по умолчанию. Узнайте больше о геттерах и сеттерах, если вы не знаете, о чем я говорю.
Когда создается экземпляр Vue, каждое свойство данных, пропеллер компонента и т. Д. Просматриваются, и для каждого из них добавляются получатели и установщики. Эти методы получения и установки позволяют Vue наблюдать за изменениями данных и запускать обновления.
reactiveSetter ()
Итак, возвращаясь к нашему объекту продукта, который выглядел следующим образом, когда мы его определили:
{
id:1,
name:'My Item',
price:9.99
}
После создания экземпляра Vue мы можем просмотреть этот объект в консоли и увидеть методы получения и установки, которые Vue определил для него:
Эти функции получения и установки имеют несколько заданий (проверьте исходный код ), но одно из заданий rectiveSetter
состоит в том, чтобы вызвать уведомление об изменении, которое приводит к повторному отображению страницы!
Предостережения
Это блестящая система, хотя и ошибочная. Если вы добавляете (или удаляете) свойство после создания экземпляра Vue (например, в методе или хуке жизненного цикла), Vue не знает об этом .
// In the addToCart method, called after instantiation
myProduct.qty=1;
Посмотрите и увидите, что хотя qty
объект определен, для него нет методов получения / установки:
Обновление реактивных объектов
В примере с корзиной покупок мы решили проблему путем создания нового объекта при добавлении в корзину, а не при добавлении свойства. Это гарантирует, что Vue имеет возможность определять реактивные геттеры и сеттеры:
functionaddToCart(id) {
varitem = this.cart.findById(id);
if (item) {
item.qty++;
} else {
// Don't add a property e.g. item.qty = 1;
// Instead create a fresh object
varnewItem = {
id: item.id,
name: item.name,
price: item.price,
qty: 1
};
this.cart.push(item);
}
}
addToCart(myProduct.id);
Vue.set
Но если вы не хотите создавать новый объект, вы можете использовать Vue.set, чтобы установить новое свойство объекта. Этот метод гарантирует, что свойство создано как реактивное свойство и запускает обновления просмотра:
functionaddToCart(id) {
varitem = this.cart.findById(id);
if (item) {
item.qty++;
} else {
// Don't add a property directly e.g. item.qty = 1;
// Use Vue.set to ensure the property is reactive
Vue.set(item, 'qty', 1);
this.cart.push(item);
}
}
addToCart(myProduct.id);
Массивы
Как и объекты, массивы являются реактивными и наблюдаются на предмет изменений. Также как объекты, у массивов есть предостережения для манипуляции. Vue переносит методы массива, такие как push
, splice
и т. Д., Поэтому они также будут запускать обновления представления
Это невозможно при прямой установке элемента с индексом, например:
// This array change will not be detected:
app.myArray[index]=newVal;
Опять Vue.set
приходит на помощь
Vue.set(app.myArray,index,newVal);