Vue - 深入观察一系列对象并计算变化?
Vue - Deep watching an array of objects and calculating the change?
我有一个名为 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 that I made 中,结果是一个空数组,而不是我所期望的已更改的已更改对象。
如果有人能指出为什么会发生这种情况或我哪里出错了,将不胜感激,非常感谢!
您的旧值和新值之间的比较函数有问题。最好不要把事情复杂化,因为这会增加你以后的调试工作量。你应该保持简单。
最好的方法是创建一个person-component
并在其自己的组件中单独监视每个人,如下所示:
<person-component :person="person" v-for="person in people"></person-component>
请在下面找到一个用于观察内部人员组件的工作示例。如果想在parent端处理,可以用$emit
向上发送一个事件,包含修改person的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>
这是定义明确的行为。您无法获得 mutated 对象的旧值。那是因为 newVal
和 oldVal
指的是同一个对象。 Vue 将不会 保留您更改的对象的旧副本。
如果您用另一个对象替换该对象,Vue 会为您提供正确的引用。
中的 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,
}
}
})
查看更新 codepen
这是我用来深入观察一个物体的方法。
我的要求是监视对象的子字段。
new Vue({
el: "#myElement",
data:{
entity: {
properties: []
}
},
watch:{
'entity.properties': {
handler: function (after, before) {
// Changes detected.
},
deep: true
}
}
});
组件方案和深克隆方案各有优缺点:
有时您想跟踪抽象数据的变化 - 围绕该数据构建组件并不总是有意义。
每次进行更改时都深度克隆整个数据结构可能非常昂贵。
我认为有更好的方法。如果您想查看列表中的所有项目并知道列表中的哪个项目发生了变化,您可以分别为每个项目设置自定义观察者,如下所示:
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()
将收到更改的特定列表项 - 从那里您可以进行任何您喜欢的处理。
我还记录了一个 ,以防您的列表中有 adding/removing 个项目(而不是仅仅操纵已经存在的项目)。
如果我们有 Object 或 Array of object 并且我们想在 Vuejs 或 NUXTJS 中观察它们需要在 watch
中使用 deep: true
watch: {
'Object.key': {
handler (val) {
console.log(val)
},
deep: true
}
}
watch: {
array: {
handler (val) {
console.log(val)
},
deep: true
}
}
我有一个名为 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 that I made 中,结果是一个空数组,而不是我所期望的已更改的已更改对象。
如果有人能指出为什么会发生这种情况或我哪里出错了,将不胜感激,非常感谢!
您的旧值和新值之间的比较函数有问题。最好不要把事情复杂化,因为这会增加你以后的调试工作量。你应该保持简单。
最好的方法是创建一个person-component
并在其自己的组件中单独监视每个人,如下所示:
<person-component :person="person" v-for="person in people"></person-component>
请在下面找到一个用于观察内部人员组件的工作示例。如果想在parent端处理,可以用$emit
向上发送一个事件,包含修改person的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>
这是定义明确的行为。您无法获得 mutated 对象的旧值。那是因为 newVal
和 oldVal
指的是同一个对象。 Vue 将不会 保留您更改的对象的旧副本。
如果您用另一个对象替换该对象,Vue 会为您提供正确的引用。
中的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,
}
}
})
查看更新 codepen
这是我用来深入观察一个物体的方法。 我的要求是监视对象的子字段。
new Vue({
el: "#myElement",
data:{
entity: {
properties: []
}
},
watch:{
'entity.properties': {
handler: function (after, before) {
// Changes detected.
},
deep: true
}
}
});
组件方案和深克隆方案各有优缺点:
有时您想跟踪抽象数据的变化 - 围绕该数据构建组件并不总是有意义。
每次进行更改时都深度克隆整个数据结构可能非常昂贵。
我认为有更好的方法。如果您想查看列表中的所有项目并知道列表中的哪个项目发生了变化,您可以分别为每个项目设置自定义观察者,如下所示:
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()
将收到更改的特定列表项 - 从那里您可以进行任何您喜欢的处理。
我还记录了一个
如果我们有 Object 或 Array of object 并且我们想在 Vuejs 或 NUXTJS 中观察它们需要在 watch
中使用deep: true
watch: {
'Object.key': {
handler (val) {
console.log(val)
},
deep: true
}
}
watch: {
array: {
handler (val) {
console.log(val)
},
deep: true
}
}