Vue.js – Как правильно отслеживать вложенные данные
Я пытаюсь понять, как правильно отслеживать изменения определённых свойств. У меня есть родительский компонент (.vue файлы), который получает данные с AJAX-запроса, помещает их в объект и использует для рендеринга дочернего компонента через директиву v-for. Ниже приведено упрощенное описание моей реализации:
<template>
<div>
<player v-for="(item, key, index) in players"
:item="item"
:index="index"
:key="key">
</player>
</div>
</template>
А затем внутри тега <script>
:
data() {
return {
players: {}
}
},
created() {
let self = this;
this.$http.get('../serv/config/player.php').then((response) => {
let pls = response.body;
for (let p in pls) {
self.$set(self.players, p, pls[p]);
}
});
}
Объекты item
выглядят следующим образом:
item: {
prop: value,
someOtherProp: {
nestedProp: nestedValue,
myArray: [{type: "a", num: 1},{type: "b", num: 6}, ...]
},
}
Теперь, внутри моего дочернего компонента "player", я пытаюсь отслеживать изменения любого свойства item
и использую следующее:
watch: {
'item.someOtherProp'(newVal) {
// работать с изменениями в "myArray"
},
'item.prop'(newVal) {
// работать с изменениями в prop
}
}
Это работает, но мне кажется немного сложным, и я хотел бы узнать, правильный ли это способ. Моя цель — выполнять какое-то действие каждый раз, когда prop
изменяется, или в myArray
появляются новые элементы, или происходят изменения внутри существующих. Буду признателен за любые советы.
5 ответ(ов)
В Vue.js, чтобы выполнять глубокое отслеживание изменений в дочерних объектах массива, вы можете использовать опцию deep
в наблюдателе (watch
). Вот пример, как это можно реализовать:
new Vue({
el: "#myElement",
data: {
entity: {
properties: []
}
},
watch: {
'entity.properties': {
handler: function (after, before) {
// Изменения выявлены. Выполняем необходимые действия...
},
deep: true
}
}
});
В этом примере, когда происходит изменение в любом из свойств в массиве properties
внутри объекта entity
, сработает обработчик. Параметр deep: true
позволяет отслеживать изменения на глубоком уровне, так что вы сможете ловить изменения даже в вложенных объектах в массиве properties
. Это особенно полезно, когда структура данных сложная и содержит объекты на нескольких уровнях вложенности.
Лично я предпочитаю такую чистую реализацию:
watch: {
myVariable: {
handler(newVal, oldVal) { // здесь у нас есть доступ к новым и старым значениям
// выполняем необходимые действия
console.log(newVal, oldVal);
},
deep: true,
/*
Также очень важно установить immediate в случае, если это необходимо,
колбэк будет вызван сразу после начала наблюдения
*/
immediate: true
}
}
Таким образом, вы можете отслеживать изменения переменной myVariable
, а также получить доступ к её старому и новому значениям в обработчике.
Другой способ "взломать" это решение заключается в следующем: я создал отдельное вычисляемое свойство, которое просто возвращает значение вложенного объекта.
data : function() {
return {
countries : {
UnitedStates : {
value: "hello world"
}
}
};
},
computed : {
helperName : function() {
return this.countries.UnitedStates.value;
}
},
watch : {
helperName : function(newVal, oldVal) {
// делать это...
}
}
Таким образом, вы можете отслеживать изменения helperName
и реагировать на них, когда значение вложенного объекта value
изменяется.
Отслеживание отдельных изменённых элементов в списке
Если вы хотите отслеживать все элементы в списке и знать, какой из них изменился, вы можете установить индивидуальные наблюдатели для каждого элемента, как показано ниже:
var vm = new Vue({
data: {
list: [
{name: 'obj1 to watch'},
{name: 'obj2 to watch'},
],
},
methods: {
handleChange (newVal, oldVal) {
// Обработка изменений здесь!
// ПРИМЕЧАНИЕ: Для мутированных объектов newVal и oldVal будут идентичны.
console.log(newVal);
},
},
created () {
this.list.forEach((val) => {
this.$watch(() => val, this.handleChange, {deep: true});
});
},
});
Если ваш список не заполняется сразу (как в оригинальном вопросе), вы можете переместить логику из created
в нужное место, например, внутрь блока .then()
.
Отслеживание изменяющегося списка
Если сам список обновляется и в нём добавляются или удаляются элементы, я разработал полезный паттерн, который «поверхностно» отслеживает сам список и динамически отслеживает или прекращает отслеживать элементы по мере изменения списка:
// ПРИМЕЧАНИЕ: Этот пример использует Lodash (_.differenceBy и _.pull) для сравнения списков
// и удаления элементов из списка. Тот же результат можно получить с помощью множества
// list.indexOf(...), если вам нужно избежать использования внешних библиотек.
var vm = new Vue({
data: {
list: [
{name: 'obj1 to watch'},
{name: 'obj2 to watch'},
],
watchTracker: [],
},
methods: {
handleChange (newVal, oldVal) {
// Обработка изменений здесь!
console.log(newVal);
},
updateWatchers () {
// Вспомогательная функция для сравнения элементов списка с "watchTracker".
const getItem = (val) => val.item || val;
// Элементы, которые еще не отслеживаются: отслеживаем и добавляем в список отслеживаемых.
_.differenceBy(this.list, this.watchTracker, getItem).forEach((item) => {
const unwatch = this.$watch(() => item, this.handleChange, {deep: true});
this.watchTracker.push({ item: item, unwatch: unwatch });
// Раскомментируйте ниже, если добавление нового элемента в список должно считаться "изменением".
// this.handleChange(item);
});
// Элементы, которые больше не существуют: прекращаем отслеживание и удаляем из списка отслеживаемых.
_.differenceBy(this.watchTracker, this.list, getItem).forEach((watchObj) => {
watchObj.unwatch();
_.pull(this.watchTracker, watchObj);
// При необходимости добавьте дополнительную очистку здесь для удаления элементов.
});
},
},
watch: {
list () {
return this.updateWatchers();
},
},
created () {
return this.updateWatchers();
},
});
Вы можете использовать следующий код для отслеживания изменений в свойстве position
объекта details
. Ниже приведён пример, который работает следующим образом:
watch: {
"details.position"(newValue, oldValue) {
console.log("Изменение произошло");
}
},
data() {
return {
details: {
position: ""
}
}
}
В этом примере, когда значение details.position
изменяется, сработает метод наблюдателя watch
, и в консоль будет выведено сообщение "Изменение произошло". Это позволяет удобно отслеживать изменения в вложенных свойствах объекта. Если у вас есть другие вопросы, не стесняйтесь спрашивать!
Где найти документацию по форматированию даты в JavaScript?
В чем разница между String.slice и String.substring?
Проверка соответствия строки регулярному выражению в JS
Как создать диалог с кнопками "Ок" и "Отмена"
Как определить нажатие клавиши Esc?