watch:监听/侦听
当监听到数据变化时,执行一个函数
语法
类型:{ [key: string]: string | Function | Object | Array }
watch是一个对象,里边是键值对的形式。值可以是字符串,函数,对象,数组
watch:{
//函数,val和oldVal是Vue自动传进去的
a:function(val,oldVal){},
//ES6新语法,可以把:function省略
b(){},
//字符串,方法名,在methods里定义
c:'methodName',
//对象
d:{
handler(){}, //数据变化时执行的操作
deep:true, //如果监听的是对象,往深了看
immediate:true //第一次渲染就触发watch
},
//数组,e变化,依次执行数组里的函数
e:[f1,f2,f3],
//监听一个对象里的属性,最好不要省掉:function,有的webpack的版本会报错
'obj.a':function(){}
}
vm.$watch('n',function(){
console.log('n变了')
},{deep:... , immediate:...})
例1:撤销
new Vue({
data: {
n: 0,
history: [],
inUndoMode: false
},
watch: {
n(newVal,oldVal){
console.log(this.inUndoMode)
if(!this.inUndoMode){
this.history.push({from:oldVal,to:newVal})
}
}
},
template: `
<div>
{{n}}
<hr />
<button @click="add1">+1</button>
<button @click="add2">+2</button>
<button @click="minus1">-1</button>
<button @click="minus2">-2</button>
<hr/>
<button @click="undo">撤销</button>
<hr/>
{{history}}
</div>
`,
methods: {
add1() {
this.n += 1;
},
add2() {
this.n += 2;
},
minus1() {
this.n -= 1;
},
minus2() {
this.n -= 2;
},
undo() {
const last=this.history.pop()
const old=last.from
this.inUndoMode=true
this.n=old
//this.inUndoMode=false watch是异步的
this.$nextTick=(()=>{
this.inUndoMode=false
},0)
}
}
}).$mount("#app");

所以用watch监听,当n变化时,执行相应函数。n()函数接收两个参数,新值和旧值。
添加撤销按钮。点击,就会把数字变成上一步的值,同时把history数组的最后一项删掉(undo函数里)。
但是有一个bug:我们通过this.n=old把n改成上一步的值,但是这也是对n的改变,watch就会监听到n的变化,就会又把这次变化push进数组。所以我们希望当点击撤销按钮时,不让watch监听。
解决方法:因为watch的要点就是监听数据的变化,并且触发函数。所以可以使用一个变量,来判断是否该触发watch函数。
我们添加一个this.inUndoMode变量,表示是否处于撤销模式。默认是false。点击撤销按钮时,改为true。所以watch里先判断,this.inUndoMode是false时,才push。然后在undo()点击函数里,修改this.n之前,把它改为true,表示现在是撤销模式了,watch不要操作了。然后修改完n之后,再改为false.
但是发现,这样也有bug。通过在watch里打log发现,点击撤销按钮,this.inUndoMode依然是false。
这是因为watch是异步的。 this.n=old 之后,会触发watch,但是watch并不会马上执行,而是先执行完后面代码,也就是this.inUndoMode=false,再去执行watch。所以当执行watch的时候,已经又变回false了,就又push进页面了。
解决办法:既然watch异步,那我把后边的代码也放在异步里,等watch执行完,再执行改回false的操作。因为watch里的异步接口是使用$nextTick实现的,所以也使用这个,属于同级异步。所以改变n在前,watch会先被触发,也就会先去执行。
例2:模拟computed
什么是变化?
我们说watch是当数据变化时,会触发函数。那什么是变化呢?
data: {
n: 0,
obj: {
a: "a"
}
},
template: `
<div>
<button @click="n += 1">n+1</button>
<button @click="obj.a += 'hi'">obj.a + 'hi'</button>
<button @click="obj = {a:'a'}">obj = 新对象</button>
</div>
`,
watch: {
n() {
console.log("n 变了");
},
obj() {
console.log("obj 变了");
},
"obj.a": function() {
console.log("obj.a 变了");
}
}
为了检验,可以添加三个按钮。可以用watch监听n,obj,obj.a,如果变化了,就会打印相应的话。
(嵌套属性在watch里,写成:'obj.a',可以把:function省掉)
修改n,n当然变了
修改obj.a,obj.a变了,obj没变
修改obj,虽然视觉上看起来和原来长得一样,但是地址变了,所以obj变了。但是obj.a没变,因为obj.a的值还是'a'
简单类型看值,复杂类型(对象)看地址
deep选项
由上边可以知道,obj对象里边的属性变了,obj是不变的,因为它的地址没变。
但是有时候的需求是,只要obj对象里的属性变了,就算obj变了。
可以使用deep选项,比如监听obj的变化,希望它里边的属性变化也会触发它的执行函数:
watch: {
n() {
console.log("n 变了");
},
obj:{
handler(){
console.log("obj 变了");
},
deep:true
}
}
要监听的obj写成对象形式,把数据变化要做的事放在handler函数里。deep属性写true。
deep的意思就是监听obj的时候,是否往深了看,看不看它里边的属性。默认是false。
面试题: computed和watch的区别
- 翻译
- 各自描述,可用代码举例
computed是计算属性,watch是监听的意思。
computed会根据它依赖的属性计算出属性。它调用的时候不加括号,直接当作属性使用。计算属性会被自动缓存,如果它依赖的属性不变,就不会重新计算。
watch是当监听的数据变化时,执行一个函数。它有两个选项,deep是当监听一个对象的时候,是否要看这个对象里边的属性的变化。immediate表示是否在第一次渲染的时候就执行这个函数。
总结来说就是,如果一个数据依赖于其他数据,那么把这个数据设计为computed的
如果你需要在某个数据变化时做一些事情,使用watch来观察这个数据变化