【问题标题】:vue.js $watch array of objectsvue.js $watch 对象数组
【发布时间】:2016-03-20 22:17:32
【问题描述】:
mounted: function() {
  this.$watch('things', function(){console.log('a thing changed')}, true);
}

things 是一个对象数组[{foo:1}, {foo:2}]

$watch 检测何时添加或删除对象,但不会检测对象上的值何时更改。我该怎么做?

【问题讨论】:

标签: vue.js


【解决方案1】:

您可以使用$watch('arr.0', () => {})$watch('dict.keyName', () => {}) 独立观察数组或字典中的每个元素的变化

来自https://vuejs.org/v2/api/#vm-watch

注意:当改变(而不是替换)一个对象或数组时, 旧值将与新值相同,因为它们引用 相同的对象/数组。 Vue 不保留预突变值的副本。

但是,您可以独立地迭代 dict/array 和 $watch 每个项目。 IE。 $watch('foo.bar') - 这会监视对象 'foo' 的属性 'bar' 的变化。

在这个例子中,我们观察 arr_of_numbers 中的所有项目,以及 arr_of_objects 中所有项目的 'foo' 属性:

mounted() {
        this.arr_of_numbers.forEach( (index, val) => {
            this.$watch(['arr_of_numbers', index].join('.'), (newVal, oldVal) => {
                console.info("arr_of_numbers", newVal, oldVal);
            });
        });

        for (let index in this.arr_of_objects) {
            this.$watch(['arr_of_objects', index, 'foo'].join('.'), (newVal, oldVal) => {
                console.info("arr_of_objects", this.arr_of_objects[index], newVal, oldVal);
            });
        }

    },
    data() {
        return {
            arr_of_numbers: [0, 1, 2, 3],
            arr_of_objects: [{foo: 'foo'}, {foo:'bar'}]
        }
    }

【讨论】:

    【解决方案2】:

    如果有人需要获取数组中已更改的项目,请检查:

    JSFiddle Example

    帖子示例代码:

    new Vue({
      ...
      watch: {
        things: {
          handler: function (val, oldVal) {
            var vm = this;
            val.filter( function( p, idx ) {
                return Object.keys(p).some( function( prop ) {
                    var diff = p[prop] !== vm.clonethings[idx][prop];
                    if(diff) {
                        p.changed = true;                        
                    }
                })
            });
          },
          deep: true
        }
      },
      ...
    })
    

    【讨论】:

    • 每次更新时对整个数组进行 diff 似乎有点过分。我想知道是否有办法让 Vue 直接告诉我更新了哪个元素...
    【解决方案3】:

    有一种更简单的方法可以在没有深度观察的情况下观察 Array 的项目:使用计算值

    {
      el: "#app",
      data () {
        return {
          list: [{a: 0}],
          calls: 0,
          changes: 0,
        }
      },
      computed: {
        copy () { return this.list.slice() },
      },
      watch: {
        copy (a, b) {
          this.calls ++
          if (a.length !== b.length) return this.onChange()
          for (let i=0; i<a.length; i++) {
            if (a[i] !== b[i]) return this.onChange()
          }
        }
      },
      methods: {
        onChange () {
          console.log('change')
          this.changes ++
        },
        addItem () { this.list.push({a: 0}) },
        incrItem (i) { this.list[i].a ++ },
        removeItem(i) { this.list.splice(i, 1) }
      }
    }
    

    https://jsfiddle.net/aurelienlt89/x2kca57e/15/

    我们的想法是构建一个计算值copy,它恰好具有我们想要检查的内容。计算值很神奇,只将观察者放在实际读取的属性上(这里,list 的项目在list.slice() 中读取)。 copy watcher 中的检查实际上几乎没有用(可能除了奇怪的极端情况),因为计算值已经非常精确。

    【讨论】:

      【解决方案4】:

      你应该传递一个对象而不是布尔值作为options,所以:

      mounted: function () {
        this.$watch('things', function () {
          console.log('a thing changed')
        }, {deep:true})
      }
      

      或者您可以像这样将观察者设置为 vue 实例:

      new Vue({
        ...
        watch: {
          things: {
            handler: function (val, oldVal) {
              console.log('a thing changed')
            },
            deep: true
          }
        },
        ...
      })
      

      [demo]

      【讨论】:

      • 很好,这是一个迁移问题。在 vue 的最后一个版本中,我使用 true 作为最后一个参数的意思是 deep。泰!
      • 为我工作。谢谢!
      猜你喜欢
      • 1970-01-01
      • 2021-07-13
      • 2020-04-10
      • 2016-10-19
      • 2016-01-23
      • 1970-01-01
      • 2017-12-15
      • 1970-01-01
      • 2016-07-20
      相关资源
      最近更新 更多