如果不是对Vue的响应式原理了如指掌,请谨慎使用watch
本文不讨论watch一个基本类型的数据,因为这种情形几乎不会出现意料之外的表现。
试着看一下下面的代码:
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script src="https://cdn.bootcss.com/vue/2.5.16/vue.min.js"></script>
<script>
new Vue({
data() {
return {
city: {id: 1, name: '北京'}
}
},
watch: {
city() {
console.log('city changed')
}
},
created() {
this.city = {id: 1, name: '北京'}
}
})
</script>
</body>
</html>
会触发watch吗?
会的,因为在created方法里面重新给city赋值了一个对象,city前后的指向不同了
以上这点代码出自一个有三年工作经验的前端程序员。他期望city发生变化之后,重新请求跟city相关的数据,他并没有意识到重新赋值给this.city一个相同的对象也会触发更新。所以在页面加载的时候就连发了两个city相关数据的请求。
这就是我写这篇文章的原因,我们考虑更多的情景。
下面这种情况会触发watch吗?
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script src="https://cdn.bootcss.com/vue/2.5.16/vue.min.js"></script>
<script>
const city = {id: 1, name: '北京'}
new Vue({
data() {
return {
city
}
},
watch: {
city() {
console.log('city changed')
}
},
created() {
this.city.name = 'Beijing'
}
})
</script>
</body>
</html>
不会触发, 因为created方法执行之后, city的指向没有变
如果我们期望捕获这种更新,应该这样写代码:
watch: {
city: {
handler: () => console.log('city changed'),
deep: true
}
}
将选项deep设为true能让vue捕获对象内部的变化。
下面讨论一下watch一个数组:
new Vue({
el: '#body',
data() {
return {
cities: ['beijing', 'tianjin']
}
},
watch: {
cities() {
console.log('cities changed')
}
}
})
那下面哪些操作会触发cities的watch回调呢?
this.cities = ['beijing', 'tianjin']
this.cities.push('xiamen')
this.cities = this.cities.slice(0, 1)
this.cities[0] = 'dali'
this.cities.splice(0, 1)
this.cities.length = 0
答案是只有最后两行不会触发。
总结
- 如果watch的是一个对象,reference equal的变化不会触发watch回调,这里包括:
- 属性增减(delete 或直接新增属性)
- 修改某一个属性的值(需要添加deep: true的选项)
- 如果watch的对象赋值给一个内部所有属性和属性的值相等的新对象,也会触发更新。
- Vue内部已经重写了数组的一些方法,如: splice, shift, push pop等,但下面的情况不能捕获更新:
- 直接用index去更新数组,如 cities[1] = 'sss'
- 直接修改cities的长度 如cities.length = 0
网友评论