Vue 提供了一种更通用的方式来观察和响应 Vue 实例上的数据变动:侦听属性。
1、常规用法
<template>
<div>
<input v-model="question">
<span>{{ copyQuestion }}</span>
</div>
</template>
<script>
export default {
data() {
return {
question: 'Hello World',
copyQuestion: ''
};
},
watch: {
question: function (value) {
this.copyQuestion = value;
}
// 下面可以简写,键值一体,键为question,值为question()方法
// question(value) {
// this.copyQuestion = value;
// }
// 此外,下面可以监听新旧值的
// question(newValue, oldValue) {
// .......
// }
}
};
</script>
注意:使用watch基本用法时有一个特点,就是当值第一次绑定的时候,不会执行监听函数,只有值发生改变才会执行。
2、绑定方法
<template>
<div>
<input v-model="question">
<span>{{ copyQuestion }}</span>
</div>
</template>
<script>
export default {
data() {
return {
question: 'Hello World',
copyQuestion: ''
};
},
watch: {
// 键为question,值为’handleQuestion()方法,每次监听到question变化,'handleQuestion()方法就会执行一次
question: 'handleQuestion'
},
methods: {
handleQuestion(value) {
this.copyQuestion = value;
}
}
};
</script>
注意:双向绑定的值(v-model="message"和data() {return
{message}})和watch监听的键要保持一致,同为message
3、deep + handler
当需要监听一个对象的改变时,基本的watch方法无法监听到对象内部属性的改变,只有data中的数据才能够监听到变化,此时就需要deep属性对对象进行深度监听。
原因:受现代 JavaScript 的限制 (以及废弃 Object.observe),Vue 不能检测到对象属性的添加或删除。由于Vue 会在初始化实例时对属性执行 getter/setter 转化过程,所以属性必须在 data 对象上存在才能让 Vue转换它,这样才能让它是响应的。
<template>
<div>
<input v-model="deepQuestion.a.b">
<span>{{ copyQuestion }}</span>
</div>
</template>
<script>
export default {
data() {
return {
deepQuestion: {
a: {
b: 'deep question'
}
},
copyQuestion: ''
};
},
watch: {
deepQuestion: {
handler: 'handleQuestion',
deep: true
}
},
methods: {
handleQuestion(value) {
this.copyQuestion = value.a.b;
}
}
};
</script>
默认情况下 watch方法只监听data中的对象,而无法监听到对象内部属性的改变,此时就需要deep属性对对象进行深度监听。(默认:deep:false)
这样只会给对象的某个特定的属性加监听器。数组(一维、多维)的变化不需要通过深度监听,对象数组中对象的属性变化则需要deep深度监听。
注意到handler了吗,我们给 firstName 绑定了一个handler方法,之前我们写的 watch 方法其实默认写的就是这个handler,Vue.js会去处理这个逻辑,最终编译出来其实就是这个handler。
deep的意思就是深入观察,监听器会一层层的往下遍历,给对象的所有属性都加上这个监听器,但是这样性能开销就会非常大了,任何修改obj里面任何一个属性都会触发这个监听器里的 handler。下面我们使用字符串的形式进行监听优化。
watch: {
'deepQuestion.a.b': {
handler(newValue, oldValue) {
console.log(newValue, oldValue);
},
immediate: true,
// deep: true
}
}
只监听对象的某少数个属性值时,可以用对象.属性字符串形式进行监听。
这样Vue.js才会一层一层解析下去,直到遇到属性b,然后才给b设置监听函数。
4、immediate
watch默认情况下在页面首次渲染时,即使监听的值有初始值,也不会直接执行,这种情况下想要第一次渲染后直接监听就需要添加属性:immediate: true
<template>
<div>
<input v-model="question">
<span>{{ copyQuestion }}</span>
</div>
</template>
<script>
export default {
data() {
return {
question: 'hello question',
copyQuestion: ''
};
},
watch: {
question: {
handler: 'handleQuestion',
immediate: true
}
},
methods: {
handleQuestion(value) {
this.copyQuestion = value;
}
}
};
</script>
比如当父组件向子组件动态传值时,子组件props首次获取到父组件传来的默认值时,也需要执行函数,此时就需要将immediate设为true。
监听的数据后面写成对象形式,包含handler方法和immediate,之前我们写的函数其实就是在写这个handler方法。
5、注销watch
为什么要注销 watch?因为我们的组件是经常要被销毁的,比如我们跳一个路由,从一个页面跳到另外一个页面,那么原来的页面的 watch 其实就没用了,这时候我们应该注销掉原来页面的 watch 的,不然的话可能会导致内置溢出。好在我们平时 watch 都是写在组件的选项中的,他会随着组件的销毁而销毁。如上都是在组件中使用的案例。
但是,如果我们使用下面这样的方式写 watch,那么就要手动注销了,这种注销其实也很简单
const unWatch = app.$watch('text', (newVal, oldVal) => {
console.log(`${newVal} : ${oldVal}`);
})
unWatch(); // 手动注销watch
app.$watch调用后会返回一个值,就是unWatch方法,你要注销 watch 只要调用unWatch方法就可以了。
6、watch监听路由
watch: {
$route: {
handler: function (to, from) {
console.log(to); // to表示去往的界面
console.log(from); // from表示来自于哪个界面
if (to.path == '/goods/detail') {
console.log('货品详情');
}
}
}
}
或者
// 监听,当路由发生变化的时候执行
watch: {
$route: {
handler: function(val, oldVal){
console.log(val);
},
// 深度观察监听
deep: true
}
}
或者
// 监听,当路由发生变化的时候执行
watch: {
'$route':'getPath'
},
methods: {
getPath(){
console.log(this.$route.path);
}
}
(完)
源码解析:Vue.js 源码分析(七) 基础篇 侦听器 watch属性详解