简介
watch可以监控一个值的变换,并调用因为变化需要执行的方法。可以通过watch动态改变关联的状态。
data:{
a:1,
b:{
c:1
}
},
watch:{
a(val, oldVal){ //普通的watch监听
console.log("a: "+val, oldVal);
},
b:{ //深度监听,可监听到对象、数组的变化
handler(val, oldVal){
console.log("b.c: "+val.c, oldVal.c);
},
deep:true //true 深度监听
}
}
侦听属性
现在我们侦听属性num的变化,当它变化时要打印出变化。
<body>
<div id="content">
<button type="button" @click="add">+1</button>
<div>
{{num}}
</div>
</div>
</body>
<script>
var vm = new Vue({
el: '#content',
data: {
num: 0
},
methods:{
add(){
this.num++;
}
},
watch:{
num:function(newValue,oldValue){
console.log(`num改变了,改变前:${oldValue},改变后:${newValue}`);
}
}
});
</script>
运行:
侦听数组
由于js的限制,vue中数组更新数据在有些情况下并不会渲染。
不会更新渲染的情况:
- 利用索引直接设置一个项时,例如:arr[0] = newValue
- 修改数组的长度时,例如:arr.length = newLength
会更新渲染的情况:
- push()
- pop()
- shift()
- unshift()
- splice()
- sort()
- reverse()
- vue2.0还增加了Vue.set(items, indexOfItem, newValue)
- filter(), concat(), slice() 。这些不会改变原始数组,但总是返回一个新数组。当使用非变异方法时,可以用新数组替换旧数组
实例:
<body>
<div id="content">
<button type="button" @click="add">+1</button>
<button type="button" @click="remove">pop</button>
<div>
{{arr}}
</div>
</div>
</body>
<script>
var vm = new Vue({
el: '#content',
data: {
arr: [0, 2, 3, 4, 7, 9, 6, 45, 3],
},
methods: {
//数组第一个元素++
add() {
this.arr[0]+=1;
},
//移除数组最后一个元素
remove() {
this.arr.pop()
}
},
watch: {
arr:function(newValue,oldValue){
console.log(`num改变了,改变前:${oldValue},改变后:${newValue}`);
}
}
});
</script>
运行add()更新了数据,但没有渲染,没有被侦听到。remove()更新了数据,有渲染,有被侦听到。
解决方法,数组的splice(index,howmany,newItem1,…,newItemX)或者Vue.set(items, indexOfItem, newValue):
<body>
<div id="content">
<button type="button" @click="add">+1</button>
<button type="button" @click="remove">pop</button>
<div>
{{arr}}
</div>
</div>
</body>
<script>
var vm = new Vue({
el: '#content',
data: {
arr: [0, 2, 3, 4, 7, 9, 6, 45, 3],
},
methods: {
add() {
let temp = this.arr[0] + 1;
//使用数组的splice方法
this.arr.splice(0, 1, temp);
//使用Vue.set也行
//this.$set(this.arr, 0, temp);
},
remove() {
this.arr.pop()
}
},
watch: {
arr: function(newValue, oldValue) {
console.log(`num改变了,改变前:${oldValue},改变后:${newValue}`);
}
}
});
</script>
运行,虽然达到了我们想要的效果,但是log中,监听到的newValue和oldValue相同。这是因为在变异 (不是替换) 对象或数组时,旧值将与新值相同,因为它们的引用指向同一个对象/数组。Vue 不会保留变异之前值的副本。
要解决这个问题,需要深拷贝再替换旧数组。
add() {
let brr = [];
for (var item in this.arr) brr[item] = this.arr[item]; //循环遍历赋值来深拷贝
brr[0]++; //操作新数组
this.arr = brr; //新数组替换旧数组
},
运行,newValue和oldValue是预期的样子了:
侦听对象
现在有一个对象obj,三个按钮分别可以让obj名字改变,年龄增加,增加一个sex属性并赋值。
<body>
<div id="content">
<button type="button" @click="changeName">改变名字</button>
<button type="button" @click="addAge">增加1岁</button>
<button type="button" @click="addSex">增加性别属性</button>
<div>
{{obj}}
</div>
</div>
</body>
<script>
var vm = new Vue({
el: '#content',
data: {
obj: {
"name": "张三",
"age": 18
}
},
methods: {
changeName() {
this.obj.name = this.obj.name + "增加";
},
addAge() {
this.obj.age++;
},
addSex() {
this.obj.sex = "男";
}
},
watch: {
obj: function(newValue, oldValue) {
console.log(`obj改变了,改变前:${oldValue},改变后:${newValue}`);
}
}
});
</script>
运行发现,改变name和age时数据和视图更新了,侦听无反应。增加sex属性时,数据更新了,侦听无反应。
这个问题可以通过**深度侦听(deep:true)**解决。
watch: {
obj: {
handler(newValue, oldValue) {
console.log("obj改变了!")
console.log("旧的obj:")
for (const value in oldValue) {
console.log(value + ":" + oldValue[value]);
}
console.log("新的obj:")
for (const value in newValue) {
console.log(value + ":" + newValue[value]);
}
},
deep: true //深度侦听
}
}
运行,name和age确实达到了预期的更新,不过log中oldValue与newValue相同。而且新增sex属性没有被监听到,sex渲染更新也不及时。
oldValue与newValue相同的问题如同上面数组一样,使用深拷贝可以解决,不再赘述。要解决sex渲染更新的问题可以使用Vue.set(obj, props, newValue):
addSex() {
// this.obj.sex = "男"; //无效
this.$set(this.obj, 'sex', '男')
}
运行: