我试图了解如何正确观察一些道具变化。我有一个父组件(.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:{
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
获得新元素或现有元素中的某些变化时执行一些操作。任何建议将不胜感激。
您可以为此使用 deep watcher:
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
}
}
(我从 comment here 中的 @peerbolte 学到了这种方法)
'item.someOtherProp'(newVal, oldVal){ .. }
VueJs 深入观察子对象
new Vue({
el: "#myElement",
data: {
entity: {
properties: []
}
},
watch: {
'entity.properties': {
handler: function (after, before) {
// Changes detected. Do work...
},
deep: true
}
}
});
我个人更喜欢这种干净的实现:
watch: {
myVariable: {
handler(newVal, oldVal){ // here having access to the new and old value
// do stuff
},
deep: true,
immediate: true // Also very important the immediate in case you need it, the callback will be called immediately after the start of the observation
}
}
myVariable
名称来监听嵌套值。 'myObject.property': { handler(new, old) { // do stuff here } }
如果您想看一段时间然后不看它怎么办?或者看一个库子组件的属性?
您可以使用“动态观察者”:
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
Note that you should not use an arrow function to define a watcher (e.g. searchQuery: newValue => this.updateAutocomplete(newValue)). The reason is arrow functions bind the parent context, so this will not be the Vue instance as you expect and this.updateAutocomplete will be undefined.
另一种添加我曾经“破解”此解决方案的方法是这样做:我设置了一个单独的 computed
值,它将简单地返回嵌套对象值。
data : function(){
return {
countries : {
UnitedStates : {
value: "hello world";
}.
},
};
},
computed : {
helperName : function(){
return this.countries.UnitedStates.value;
},
},
watch : {
helperName : function(newVal, oldVal){
// do this...
}
}
跟踪列表中的单个更改项
如果您想查看列表中的所有项目并知道列表中的哪个项目发生了变化,您可以分别在每个项目上设置自定义观察者,如下所示:
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();
},
});
这里没有提到它,但如果您要扩展 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
}
...
}
我发现它也可以这样工作:
watch: {
"details.position"(newValue, oldValue) {
console.log("changes here")
}
},
data() {
return {
details: {
position: ""
}
}
}
我对使用 deep: true
的公认答案的问题是,在深入观察数组时,我无法轻松识别数组的 哪个 元素包含更改。我发现的唯一明确的解决方案是this answer, which explains how to make a component so you can watch each array element individually.
oldVal
和 newVal
都引用同一个对象,所以它们是相同的)。 deep watcher
的目的是在值更改时做某事,例如发出事件、进行 ajax 调用等。否则,您可能需要一个组件,正如 Mani 的回答中所指出的那样。
对我来说没有一个答案是有效的。实际上,如果您想通过多次调用组件来查看嵌套数据。所以他们被称为不同的道具来识别它们。例如 <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
},
}
在任何更新商店的组件功能上:
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]);
},
},
对于任何寻找 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
我使用了 deep:true,但发现 watch 函数中的新旧值始终相同。作为以前解决方案的替代方案,我尝试了这个,它将通过将整个对象转换为字符串来检查整个对象的任何变化:
created() {
this.$watch(
() => JSON.stringify(this.object),
(newValue, oldValue) => {
//do your stuff
}
);
},
这是一种为嵌套属性编写观察器的方法:
new Vue({
...allYourOtherStuff,
watch: {
['foo.bar'](newValue, oldValue) {
// Do stuff here
}
}
});
您甚至可以将此语法用于异步观察者:
new Vue({
...allYourOtherStuff,
watch: {
async ['foo.bar'](newValue, oldValue) {
// Do stuff here
}
}
});
不定期副业成功案例分享
newValue
、oldValue
,但对于对象,我发现它传递了两个 args 相同的值。 (可能是出于性能考虑——他们不想对结构进行深层复制。)这意味着您无法告诉 为什么 然后根据参数调用处理程序。 (如果这对您来说是一个问题。)