Как вызвать реактивность при обновлении нескольких реквизитов в объекте, используя VueJS? - PullRequest
4 голосов
/ 02 апреля 2020

Я наблюдал какое-то странное поведение при создании моего приложения, когда часть домена не реагировала должным образом на ввод. Мутации регистрировались, состояние менялось, но опоры в DOM не было. Я заметил, что когда я вернулся, отредактировал одну новую пустую строку в html, вернулся, и теперь он отображал новые реквизиты. Но мне пришлось бы отредактировать, сохранить документ и затем вернуться, чтобы увидеть все новые изменения в состоянии.

Итак, состояние обновлялось, но Vue не реагировало на изменение. Вот почему я думаю, почему: https://vuejs.org/v2/guide/reactivity.html#For -объекты

Vue не могут обнаружить добавление или удаление свойства. Поскольку Vue выполняет процесс преобразования получателя / установщика во время инициализации экземпляра, в объекте данных должно присутствовать свойство, чтобы Vue могло преобразовать его и сделать его реактивным

Иногда вам может потребоваться назначить количество свойств существующего объекта, например, с помощью Object.assign () или _.extend (). Однако новые свойства, добавленные к объекту, не будут вызывать изменений. В таких случаях создайте объект fre sh со свойствами как исходного объекта, так и объекта mixin

Объект в моем состоянии является экземпляром js-libp2p. Периодически, когда экземпляр libp2p делает что-то, мне нужно обновить объект в моем состоянии. Я делал это, выполняя мутацию

syncNode(state, libp2p) {
    state.p2pNode = libp2p
}

Где libp2p - текущий экземпляр объекта, на который я пытаюсь заставить DOM реагировать, изменяя state.p2pNode. Я не могу использовать $set, то есть для редактирования одного значения, и я думаю, что .assign или .extend не будут работать, так как я пытаюсь заменить все дерево объектов.

Почему это так? это ограничение и есть ли решение для этой конкретной проблемы?

Ответы [ 3 ]

2 голосов
/ 02 апреля 2020

Единственное, что нужно для переназначения элемента Vuex state таким способом, - это объявить его заранее.

Не имеет значения, является ли этот элемент объектом или любым другим типом переменной, даже если перезаписывается все значение , Это не то же самое, что ситуации предупреждения реактивности, когда требуется set, потому что Vue не может обнаружить мутацию свойства объекта, несмотря на тот факт, что state является объектом. Это не нужно:

Vue.set(state, 'p2pNode', libp2p);

Должна быть другая проблема, если есть компонент, правильно использующий p2pNode, который не реагирует на переназначение. Подтвердите, что вы объявили / инициализировали его в начальном состоянии Vuex:

state: {
  p2pNode: null  // or whatever initialization value makes the most sense
}

Вот демоверсия для доказательства . Вероятно, проблема в том, что вы не использовали значение Vuex каким-либо реактивным способом.

2 голосов
/ 02 апреля 2020

Я считаю, что ваша проблема более сложна, чем базовые c правила о назначении новых свойств. Но первая половина этого ответа посвящена основным правилам.

И чтобы ответить, почему Vue имеет некоторые ограничения относительно правильного присвоения новых свойств реактивному объекту, это, вероятно, связано с производительностью и ограничениями язык. Теоретически, Vue может постоянно проходить через свои реактивные объекты в поисках новых свойств, но производительность, вероятно, будет ужасной.

Для чего бы это ни стоило, новый компилятор Vue 3 предположительно сможет справиться с этим легче. До этого документы, на которые вы ссылались, предоставляли правильное решение (см. Пример ниже) для большинства случаев.

var app = new Vue({
  el: "#app",
  data() {
    return {
      foo: {
        person: {
          firstName: "Evan"
        }
      }
    };
  },
  methods: {
    syncData() {
      // Does not work
      // this.foo.occupation = 'coder';

      // Does work (foo is already reactive)
      this.foo = {
        person: {
          firstName: "Evan"
        },
        occupation: 'Coder'
      };

      // Also works (better when you need to supply a 
      // bunch of new props but keep the old props too)
      // this.foo = Object.assign({}, this.foo, {
      //  occupation: 'Coder',
      // });      
    }
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  Hello {{foo.person.firstName}} {{foo.occupation}}!
  <button @click="syncData">Load new data</button>
</div>

Обновление: ответ Дэна был хорошим - вероятно, лучше, чем мой, в большинстве случаев, поскольку он учитывает Vuex. Учитывая, что ваш код все еще не работает, когда вы используете его решение, я подозреваю, что p2pNode иногда мутирует сам (Vuex ожидает, что все мутации в этом объекте будут go через официальный коммит). Учитывая, что у него, похоже, есть хуки жизненного цикла (например, libp2p.on('peer:connect'), я бы не удивился, если бы это было так. В итоге вы можете рвать на себе волосы, пытаясь получить идеальную реактивность на узле, который тихо мутирует на заднем плане.

Если это так, и libp2p не предоставляет libp2p.on('update') хука, через который вы могли бы информировать Vuex об изменениях, тогда вы можете захотеть реализовать своего рода базовое c игровое состояние l oop и просто скажите Vue пересчитывать все время от времени после короткого сна. См. { ссылка } и { ссылка }. Это немного хакерский (по крайней мере, информированный), но он может сделать вашу жизнь намного проще в краткосрочной перспективе, пока вы не разберетесь с этой колючей ошибкой, и мерцания быть не должно.

0 голосов
/ 02 апреля 2020

Просто мысль, я ничего не знаю о libp2p, но вы пытаетесь объявить свою переменную в параметрах данных, которые меняются при обновлении:

data: {
    updated: ''
  }

, а затем присваивать ей значение:

syncNode(state, libp2p) {
    this.updated = state
    state.p2pNode = libp2p
}
...