Vue.js -如何正确地监视嵌套数据

我试图理解如何正确地观看一些道具变化。 我有一个父组件(。Vue文件),从ajax调用接收数据,把数据放在一个对象中,并使用它来通过v-for指令渲染一些子组件,下面是我的实现的简化

<template>
<div>
<player v-for="(item, key, index) in players"
:item="item"
:index="index"
:key="key"">
</player>
</div>
</template>

... 然后在<script>标签内:

 data(){
return {
players: {}
},
created(){
let self = this;
this.$http.get('../serv/config/player.php').then((response) => {
let pls = response.body;
for (let p in pls) {
self.$set(self.players, p, pls[p]);
}
});
}

Item对象是这样的:

item:{
prop: value,
someOtherProp: {
nestedProp: nestedValue,
myArray: [{type: "a", num: 1},{type: "b" num: 6} ...]
},
}

现在,在我的孩子“播放器”里;组件我试图观察任何项目的属性变化,我使用:

...
watch:{
'item.someOtherProp'(newVal){
//to work with changes in "myArray"
},
'item.prop'(newVal){
//to work with changes in prop
}
}

这是可行的,但对我来说似乎有点棘手,我想知道这是否是正确的方法。我的目标是每次prop更改或myArray获得新元素或现有元素中的一些变化时执行一些操作。任何建议都将不胜感激。

521123 次浏览

你可以使用深观察家:

watch: {
item: {
handler(val){
// do stuff
},
deep: true
}
}

现在它将检测对item数组中的对象的任何更改以及对数组本身的添加(当与Vue.set一起使用时)。这是一个JSFiddle: http://jsfiddle.net/je2rw3rs/

编辑

如果你不想观察顶层对象的每一个变化,只是想要一个不那么尴尬的语法来直接观察嵌套对象,你可以简单地观察computed:

var vm = new Vue({
el: '#app',
computed: {
foo() {
return this.item.foo;
}
},
watch: {
foo() {
console.log('Foo Changed!');
}
},
data: {
item: {
foo: 'foo'
}
}
})

下面是JSFiddle: http://jsfiddle.net/oa07r5fw/

另一种更好的方法是:

 watch:{
'item.someOtherProp': function (newVal, oldVal){
//to work with changes in someOtherProp
},
'item.prop': function(newVal, oldVal){
//to work with changes in prop
}
}

(我从这里的评论中的@peerbolte中学到了这个方法)

如果你想看一段时间的财产,然后取消它怎么办?

或者查看库子组件属性?

您可以使用“动态监控器”:

this.$watch(
'object.property', //what you want to watch
(newVal, oldVal) => {
//execute your code here
}
)

$watch返回一个unwatch函数,如果它被调用,将停止监视。

var unwatch = vm.$watch('a', cb)
// later, teardown the watcher
unwatch()

你也可以使用deep选项:

this.$watch(
'someObject', () => {
//execute your code here
},
{ deep: true }
)

请务必看一下to docs

我对使用deep: true的公认答案的问题是,当深入观察一个数组时,我不能轻易地识别数组的哪一个元素包含更改。我找到的唯一明确的解决方案是这个答案解释了如何制作一个组件,以便您可以单独观察每个数组元素。

VueJs深入观察儿童物体

new Vue({
el: "#myElement",
data: {
entity: {
properties: []
}
},
watch: {
'entity.properties': {
handler: function (after, before) {
// Changes detected. Do work...
},
deep: true
}
}
});
另一种补充的方法是,我曾经“破解”这个解决方案是这样做的: 我设置了一个单独的computed值,它将简单地返回嵌套的对象值
data : function(){
return {
countries : {
UnitedStates : {
value: "hello world";
}.
},
};
},
computed : {
helperName : function(){
return this.countries.UnitedStates.value;
},
},
watch : {
helperName : function(newVal, oldVal){
// do this...
}
}

这里没有提到它,但是如果您正在扩展Vue类,也可以使用vue-property-decorator模式。

import { Watch, Vue } from 'vue-property-decorator';


export default class SomeClass extends Vue {
...


@Watch('item.someOtherProp')
someOtherPropChange(newVal, oldVal) {
// do something
}


...
}

跟踪列表中单个更改的项

如果你想监视列表中的所有项目,并且知道列表中的哪一个项目发生了变化,你可以在每个项目上分别设置自定义监视器,如下所示:

var vm = new Vue({
data: {
list: [
{name: 'obj1 to watch'},
{name: 'obj2 to watch'},
],
},
methods: {
handleChange (newVal, oldVal) {
// Handle changes here!
// NOTE: For mutated objects, newVal and oldVal will be identical.
console.log(newVal);
},
},
created () {
this.list.forEach((val) => {
this.$watch(() => val, this.handleChange, {deep: true});
});
},
});

如果你的列表没有立即填充(就像在最初的问题中),你可以将逻辑从created移到任何需要的地方,例如在.then()块中。

观察不断变化的列表

如果你的列表本身更新到有新的或删除的项目,我已经开发了一个有用的模式,“浅层”观察列表本身,并动态观察/取消观察项目随着列表的变化:

// NOTE: This example uses Lodash (_.differenceBy and _.pull) to compare lists
//       and remove list items. The same result could be achieved with lots of
//       list.indexOf(...) if you need to avoid external libraries.


var vm = new Vue({
data: {
list: [
{name: 'obj1 to watch'},
{name: 'obj2 to watch'},
],
watchTracker: [],
},
methods: {
handleChange (newVal, oldVal) {
// Handle changes here!
console.log(newVal);
},
updateWatchers () {
// Helper function for comparing list items to the "watchTracker".
const getItem = (val) => val.item || val;


// Items that aren't already watched: watch and add to watched list.
_.differenceBy(this.list, this.watchTracker, getItem).forEach((item) => {
const unwatch = this.$watch(() => item, this.handleChange, {deep: true});
this.watchTracker.push({ item: item, unwatch: unwatch });
// Uncomment below if adding a new item to the list should count as a "change".
// this.handleChange(item);
});


// Items that no longer exist: unwatch and remove from the watched list.
_.differenceBy(this.watchTracker, this.list, getItem).forEach((watchObj) => {
watchObj.unwatch();
_.pull(this.watchTracker, watchObj);
// Optionally add any further cleanup in here for when items are removed.
});
},
},
watch: {
list () {
return this.updateWatchers();
},
},
created () {
return this.updateWatchers();
},
});
对我来说,没有一个答案是有效的。实际上,如果你想观察嵌套的数据与组件被多次调用。所以他们被称为不同的道具来识别他们。 例如<MyComponent chart="chart1"/> <MyComponent chart="chart2"/> 我的解决方法是创建一个额外的vuex状态变量,我手动更新它以指向上次更新的属性

这是一台Vuex。Ts实现示例:

export default new Vuex.Store({
state: {
hovEpacTduList: {},  // a json of arrays to be shared by different components,
// for example  hovEpacTduList["chart1"]=[2,6,9]
hovEpacTduListChangeForChart: "chart1"  // to watch for latest update,
// here to access "chart1" update
},
mutations: {
setHovEpacTduList: (state, payload) => {
state.hovEpacTduListChangeForChart = payload.chart // we will watch hovEpacTduListChangeForChart
state.hovEpacTduList[payload.chart] = payload.list // instead of hovEpacTduList, which vuex cannot watch
},
}

在任何Component函数上更新存储:

    const payload = {chart:"chart1", list: [4,6,3]}
this.$store.commit('setHovEpacTduList', payload);

现在对任何组件进行更新:

    computed: {
hovEpacTduListChangeForChart() {
return this.$store.state.hovEpacTduListChangeForChart;
}
},
watch: {
hovEpacTduListChangeForChart(chart) {
if (chart === this.chart)  // the component was created with chart as a prop <MyComponent chart="chart1"/>
console.log("Update! for", chart, this.$store.state.hovEpacTduList[chart]);
},
},

我发现它也是这样运作的:

watch: {
"details.position"(newValue, oldValue) {
console.log("changes here")
}
},
data() {
return {
details: {
position: ""
}
}
}

下面是一种为嵌套属性编写观察者的方法:

    new Vue({
...allYourOtherStuff,
watch: {
['foo.bar'](newValue, oldValue) {
// Do stuff here
}
}
});


你甚至可以对异步观察者使用这个语法:

    new Vue({
...allYourOtherStuff,
watch: {
async ['foo.bar'](newValue, oldValue) {
// Do stuff here
}
}
});


我使用了deep:true,但发现在监视函数中的新旧值始终是相同的。作为之前解决方案的替代方案,我尝试了这个,它将通过将整个对象转换为字符串来检查它的任何变化:

created() {
this.$watch(
() => JSON.stringify(this.object),
(newValue, oldValue) => {
//do your stuff
}
);
},

我个人更喜欢这种简洁的实现方式:

watch: {
myVariable: {
handler(newVal, oldVal){  // here having access to the new and old value
// do stuff
console.log(newVal, oldVal);
},
deep: true,
/*
Also very important the immediate in case you need it,
the callback will be called immediately after the start
of the observation
*/
immediate: true


}
}

对于任何寻找Vue 3的人


import { watch } from 'vue';


...
...


watch(
() => yourNestedObject,              // first param, your object
(currValue, prevValue) => {          // second param, watcher callback
console.log(currValue, prevValue);
},
{ deep: true }                       // third param, for deep checking
);


您可以参考这里的文档:https://v3.vuejs.org/guide/reactivity-computed-watchers.html#watch

https://vuejs.org/guide/essentials/watchers.html#deep-watchers

export default {
watch: {
someObject: {
handler(newValue, oldValue) {
// Note: `newValue` will be equal to `oldValue` here
// on nested mutations as long as the object itself
// hasn't been replaced.
},
deep: true
}
}
}