深度观察一组物体并计算变化?

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

之前

[
{id: 0, name: 'Bob', age: 27},
{id: 1, name: 'Frank', age: 32},
{id: 2, name: 'Joe', age: 38}
]

它可以改变:

之后

[
{id: 0, name: 'Bob', age: 27},
{id: 1, name: 'Frank', age: 33},
{id: 2, name: 'Joe', age: 38}
]

注意,弗兰克刚满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>

我基于 我昨天问的问题的数组比较选择了最快的工作答案。

因此,在这一点上,我希望看到一个结果: { id: 1, name: 'Frank', age: 33 }

但是我在控制台里得到的只是(记住我把它放在一个组件里了) :

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

我做的 Codepen中,结果是一个空数组,而不是我所期望的已更改的对象。

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

215437 次浏览

旧值和新值之间的比较函数有一些问题。最好不要把事情复杂化太多,因为这会增加以后的调试工作。你应该简单点。

最好的方法是创建一个 person-component,在它自己的组件中分别监视每个人,如下所示:

<person-component :person="person" v-for="person in people"></person-component>

请找到下面的一个工作示例来观察内部人员组成部分。如果您想在父方处理它,您可以使用 $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}
]
}
});
<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>

这是明确界定的行为。无法获取 变异了对象的旧值。这是因为 newValoldVal都引用同一个对象。Vue 会保留你变异的对象的旧拷贝。

如果您使用另一个 被取代了对象,Vue 将为您提供正确的引用。

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

更多关于 给你给你的信息。

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

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

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,
}
}
})

参见更新的 < a href = “ http://codepen.io/anon/pen/NbOBJK”rel = “ noReferrer”> Codepen

这是我用来深度观察物体的。 我的要求是观察对象的子字段。

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

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

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

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

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

如果我们有对象或数组的对象,我们想在 Vuejs 或 NUXtjs 观看他们需要使用观看 deep: true

    watch: {
'Object.key': {
handler (val) {
console.log(val)
},
deep: true
}
}


watch: {
array: {
handler (val) {
console.log(val)
},
deep: true
}
}

我没有用“手表”而是用“计算”解决了这个问题!

我还没有测试这个代码,但我认为它应该工作。请告诉我在评论,如果没有。

<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}
],
oldVal: {},
peopleComputed: computed({
get(){
this.$data.oldVal = { ...people };
return people;
},
set(val){
// Return the object that changed
var changed = val.filter( function( p, idx ) {
return Object.keys(p).some( function( prop ) {
return p[prop] !== this.$data.oldVal[idx][prop];
})
})
// Log it
console.log(changed)
this.$data.people = val;
}
}),
}
})
</script>