如何让 Vue.js 对动态添加的数组属性的更改做出反应?

How can I get Vue.js to be reactive for changes to dynamically added array properties?

在 Vue.js 中,我有一个动态 added/edited 属性的数据对象,这些属性本身就是数组。例如,属性 开始如下:

data: function () {
    return {
        vals: {}
    };
}

随着时间的推移,通过各种按钮点击等,vals 可能如下所示(实际 属性 名称和值根据多种因素是 100% 动态的) :

vals: {
    set1: [
        {
            prop1: 123,
            prop2: 'hello'
        },
        {
            prop1: 456,
            prop2: 'bye'
        }
    ],
    set2: [
        {
            prop3: 'Why?!',
            prop4: false
        }
    ]
}

随着数组属性(即 set1set2)发生变化,我希望能够对这些变化做出反应。

例如,我可能会在我的代码中执行如下操作:

var prop = 'set1';

this.vals[prop].push({
    {
        prop1: 789,
        prop2: 'hmmm...'
    }
});

但是,当我这样做时,组件没有更新(我想是因为我将一个对象推到对象子数组的末尾;并且 Vue.js 似乎没有跟踪这些变化).

我已经能够通过在上述 push 之后执行 this.$forceUpdate(); 来强制组件响应,但我想必须有更多 eloquent 的方式来获得 Vue.js 当对象被推到对象子数组的末尾时反应。

有谁知道有更好的方法来尝试实现我想要实现的目标吗?谢谢。

任何时候向对象添加新的 属性 或更改数组中的值时,都需要使用 Vue.set().

Vue.set(this.vals, prop, [ /* ... */ ])

理想情况下,您应该预先定义所有属性,这样 Vue 就不必根据数据模型的形状使计算属性无效。即使您将它们设置为 null,您也应该尝试映射出您希望组件需要的所有属性。

此外,在您的第一个代码块中,您的 return: 之后有一个冒号,它将评估为一个标签,这意味着您的数据函数没有返回任何内容。

您可以尝试使用 lodashdeep watcher..

更多关于 deep watcher here

new Vue({
  el: "#app",
  methods: {
    setValue() {
      this.oldPeople = _.cloneDeep(this.people);
    }
  },
  mounted() {
    this.setValue();
  },
  el: "#app",
  data: {
    changed: null,
    people: [
      { id: 0, name: "Bob", age: 27 },
      { id: 1, name: "Frank", age: 32 },
      { id: 2, name: "Joe", age: 38 }
    ],
    oldPeople: []
  },
  watch: {
    people: {
      deep: true,
      handler(after, before) {
        // Return the object that changed
        let vm = this;
        let changed = after.filter(function(p, idx) {
          return Object.keys(p).some(function(prop) {
            return p[prop] !== vm.oldPeople[idx][prop];
          });
        });
        vm.setValue();
        this.changed = changed;
      },      
    }
  }
});
input {
  display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.6/vue.js"></script>
<script src="https://cdn.jsdelivr.net/lodash/4.17.2/lodash.min.js"></script>

<div id="app">
  <div>
    <input type="text" v-for="(person, index) in people" v-model="people[index].age" />
    <div v-if="changed !== null">
      You changed:<br/>{{ changed }}
    </div>
  </div>
</div>