7

Vue.js – Как правильно отслеживать вложенные данные

1

Я пытаюсь понять, как правильно отслеживать изменения определённых свойств. У меня есть родительский компонент (.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 ответ(ов)

0

В 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. Это особенно полезно, когда структура данных сложная и содержит объекты на нескольких уровнях вложенности.

0

Лично я предпочитаю такую чистую реализацию:

watch: {
  myVariable: {
     handler(newVal, oldVal) {  // здесь у нас есть доступ к новым и старым значениям
       // выполняем необходимые действия
       console.log(newVal, oldVal);
     },
     deep: true,
     /*
         Также очень важно установить immediate в случае, если это необходимо,
         колбэк будет вызван сразу после начала наблюдения
     */
     immediate: true    
  }
}

Таким образом, вы можете отслеживать изменения переменной myVariable, а также получить доступ к её старому и новому значениям в обработчике.

0

Другой способ "взломать" это решение заключается в следующем: я создал отдельное вычисляемое свойство, которое просто возвращает значение вложенного объекта.

data : function() {
    return {
        countries : {
            UnitedStates : {
                value: "hello world"
            }
        }
    };
},
computed : {
    helperName : function() {
        return this.countries.UnitedStates.value;
    }
},
watch : {
    helperName : function(newVal, oldVal) {
        // делать это...
    }
}

Таким образом, вы можете отслеживать изменения helperName и реагировать на них, когда значение вложенного объекта value изменяется.

0

Отслеживание отдельных изменённых элементов в списке

Если вы хотите отслеживать все элементы в списке и знать, какой из них изменился, вы можете установить индивидуальные наблюдатели для каждого элемента, как показано ниже:

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();
  },
});
0

Вы можете использовать следующий код для отслеживания изменений в свойстве position объекта details. Ниже приведён пример, который работает следующим образом:

watch: {
    "details.position"(newValue, oldValue) {
        console.log("Изменение произошло");
    }
},
data() {
    return {
        details: {
            position: ""
        }
    }
}

В этом примере, когда значение details.position изменяется, сработает метод наблюдателя watch, и в консоль будет выведено сообщение "Изменение произошло". Это позволяет удобно отслеживать изменения в вложенных свойствах объекта. Если у вас есть другие вопросы, не стесняйтесь спрашивать!

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь