Vue - 深入观察一系列物体并计算变化?

Cra*_*der 87 javascript vue.js vuejs2

我有一个名为people包含对象的数组,如下所示:

之前

[
  {id: 0, name: 'Bob', age: 27},
  {id: 1, name: 'Frank', age: 32},
  {id: 2, name: 'Joe', age: 38}
]
Run Code Online (Sandbox Code Playgroud)

它可以改变:

[
  {id: 0, name: 'Bob', age: 27},
  {id: 1, name: 'Frank', age: 33},
  {id: 2, name: 'Joe', age: 38}
]
Run Code Online (Sandbox Code Playgroud)

弗兰克刚满33岁.

我有一个应用程序,我正在尝试观察人员数组,当任何值更改时,请记录更改:

<style>
input {
  display: block;
}
</style>

<div id="app">
  <input type="text" v-for="(person, index) in people" v-model="people[index].age" />
</div>

<script>
new Vue({
  el: '#app',
  data: {
    people: [
      {id: 0, name: 'Bob', age: 27},
      {id: 1, name: 'Frank', age: 32},
      {id: 2, name: 'Joe', age: 38}
    ]
  },
  watch: {
    people: {
      handler: function (val, oldVal) {
        // Return the object that changed
        var changed = val.filter( function( p, idx ) {
          return Object.keys(p).some( function( prop ) {
            return p[prop] !== oldVal[idx][prop];
          })
        })
        // Log it
        console.log(changed)
      },
      deep: true
    }
  }
})
</script>
Run Code Online (Sandbox Code Playgroud)

我的基础是我昨天问的有关阵列比较的问题,并选择了最快的工作答案.

所以,此时我希望看到以下结果: { id: 1, name: 'Frank', age: 33 }

但我回到控制台的所有内容都是(记住我在组件中有它):

[Vue warn]: Error in watcher "people" 
(found in anonymous component - use the "name" option for better debugging messages.)
Run Code Online (Sandbox Code Playgroud)

我创建codepen中,结果是一个空数组,而不是更改的对象,这将是我所期望的.

如果有人能说明为什么会发生这种情况或者我在这里出错了,那么我们将不胜感激,非常感谢!

Man*_*ani 114

旧值和新值之间的比较函数存在一些问题.最好不要将事情复杂化,因为它会在以后增加您的调试工作量.你应该保持简单.

最好的方法是person-component在其自己的组件内创建并分别观察每个人,如下所示:

<person-component :person="person" v-for="person in people"></person-component>
Run Code Online (Sandbox Code Playgroud)

请在下面找到一个用于观看人员内部组件的工作示例.如果你想在父方面处理它,你可以使用$emit向上发送一个包含id修改过的人的事件.

Vue.component('person-component', {
    props: ["person"],
    template: `
        <div class="person">
            {{person.name}}
            <input type='text' v-model='person.age'/>
        </div>`,
    watch: {
        person: {
            handler: function(newValue) {
                console.log("Person with ID:" + newValue.id + " modified")
                console.log("New age: " + newValue.age)
            },
            deep: true
        }
    }
});

new Vue({
    el: '#app',
    data: {
        people: [
          {id: 0, name: 'Bob', age: 27},
          {id: 1, name: 'Frank', age: 32},
          {id: 2, name: 'Joe', age: 38}
        ]
    }
});
Run Code Online (Sandbox Code Playgroud)
<script src="https://unpkg.com/vue@2.1.5/dist/vue.js"></script>
<body>
    <div id="app">
        <p>List of people:</p>
        <person-component :person="person" v-for="person in people"></person-component>
    </div>
</body>
Run Code Online (Sandbox Code Playgroud)

  • 顺便说一句,感谢您花时间解释这一点,它帮助我了解了更多关于 Vue 的知识,我很感激! (2认同)

Vip*_*ock 20

我已经改变了它的实现以解决你的问题,我做了一个对象来跟踪旧的变化并将其与之进行比较.您可以使用它来解决您的问题.

在这里,我创建了一个方法,其中旧值将存储在一个单独的变量中,然后将在手表中使用.

new Vue({
  methods: {
    setValue: function() {
      this.$data.oldPeople = _.cloneDeep(this.$data.people);
    },
  },
  mounted() {
    this.setValue();
  },
  el: '#app',
  data: {
    people: [
      {id: 0, name: 'Bob', age: 27},
      {id: 1, name: 'Frank', age: 32},
      {id: 2, name: 'Joe', age: 38}
    ],
    oldPeople: []
  },
  watch: {
    people: {
      handler: function (after, before) {
        // Return the object that changed
        var vm = this;
        let changed = after.filter( function( p, idx ) {
          return Object.keys(p).some( function( prop ) {
            return p[prop] !== vm.$data.oldPeople[idx][prop];
          })
        })
        // Log it
        vm.setValue();
        console.log(changed)
      },
      deep: true,
    }
  }
})
Run Code Online (Sandbox Code Playgroud)

查看更新的 codepen


Qui*_*irk 16

这是明确定义的行为.您无法获得变异对象的旧值.这是因为无论是newValoldVal指的是同一个对象.Vue 不会保留您变异的对象的旧副本.

如果您用另一个替换了该对象,Vue会为您提供正确的引用.

阅读文档中Note部分.(vm.$watch)

更多关于这里这里.

  • 哦,我的帽子,非常感谢!这是一个棘手的问题......我完全希望val和oldVal有所不同,但在检查它们之后,我看到它是新阵列的两个副本,它之前没有跟踪它.阅读更多内容,发现有关同一误解的未解答的问题:http://stackoverflow.com/questions/35991494/find-out-which-property-changed-in-a-vue-js-deep-custom-directive (3认同)

Eri*_*ans 9

组件方案和深度克隆方案各有优势,但也存在问题:

  1. 有时您想跟踪抽象数据中的变化 - 围绕该数据构建组件并不总是有意义的。

  2. 每次进行更改时深度克隆整个数据结构可能非常昂贵。

我认为有更好的方法。如果您想查看列表中的所有项目并知道列表中的哪个项目发生了变化,您可以分别为每个项目设置自定义观察器,如下所示:

var vm = new Vue({
  data: {
    list: [
      {name: 'obj1 to watch'},
      {name: 'obj2 to watch'},
    ],
  },
  methods: {
    handleChange (newVal) {
      // Handle changes here!
      console.log(newVal);
    },
  },
  created () {
    this.list.forEach((val) => {
      this.$watch(() => val, this.handleChange, {deep: true});
    });
  },
});
Run Code Online (Sandbox Code Playgroud)

使用此结构,handleChange()将接收更改的特定列表项 - 从那里您可以进行任何您喜欢的处理。

我还在此处记录了一个更复杂的场景,以防您在列表中添加/删除项目(而不是仅操作已经存在的项目)。


Alp*_*glu 7

这就是我用来深入观察物体的方法。我的要求是观察对象的子字段。

new Vue({
    el: "#myElement",
    data:{
        entity: {
            properties: []
        }
    },
    watch:{
        'entity.properties': {
            handler: function (after, before) {
                // Changes detected.    
            },
            deep: true
        }
    }
});
Run Code Online (Sandbox Code Playgroud)

  • 我相信您可能无法理解 /sf/answers/2879533051/ 中描述的警告。需要明确的是,这不是问题的解决方案,并且在某些情况下不会像您预期的那样起作用。 (2认同)