基本概念
计算属性允许我们声明性地计算衍生值。然而在有些情况下,我们需要在状态变化时执行一些“副作用”,所以需要侦听器。
选项式
可以使用 watch 选项。
使用示例:
DOM部分:
<div id="app">
<p>
Ask a yes/no question:
<input v-model="question" />
</p>
<p>{{answer}}</p>
</div>
script部分:
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
const { createApp } = Vue;
createApp({
data() {
return {
question: '',
answer: 'Questions usually contain a question mark. :-)',
}
},
methods: {
async getAnswer() {
this.answer = 'Thinking...'
try {
const res = await fetch('https://yesno.wtf/api')
this.answer = (await res.json()).answer
} catch (error) {
this.answer = 'Error! Could not reach the API. ' + error
}
},
},
watch:{
// 每当 question 改变时,这个函数就会执行
question(newQuestion, oldQuestion) {
console.log('question have be changed.');
if (newQuestion.includes('?')) {
this.getAnswer();
}
}
// 或
/* question: {
handler(newQuestion, oldQuestion) {
console.log('question have be changed.');
if (newQuestion.includes('?')) {
this.getAnswer();
}
}
} */
},
}).mount("#app");
</script>
组合式
可以使用 watch 函数。
使用示例:
DOM部分:
<div id="app">
<p>
Ask a yes/no question:
<input v-model="question" />
</p>
<p>{{answer}}</p>
</div>
script部分:
<script type="module">
import { createApp, ref, watch } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js'
createApp({
setup() {
const question = ref('');
const answer = ref('Questions usually contain a question mark. ;-)');
const dosomething = async (newQuestion, oldQuestion) => {
console.log("question have be changed.");
if (newQuestion.indexOf('?') > -1) {
answer.value = 'Thinking...'
try {
const res = await fetch('https://yesno.wtf/api')
answer.value = (await res.json()).answer
} catch (error) {
answer.value = 'Error! Could not reach the API. ' + error
}
}
};
// 每当 question 改变时,这个函数就会执行
watch(question, dosomething);
return {
question, answer
}
}
}).mount("#app");
</script>
使用详解
选项式
对象属性侦听: watch 选项支持把键设置成用 . 分隔的路径:
watch:{
'object.prop.value'(newValue,oldValue){
// ...
}
}
深层侦听: watch 默认是浅层的:被侦听的属性,仅在被赋新值时,才会触发回调函数,而嵌套属性的变化不会触发。这时需要开启 deep 选项。
watch:{
dataObject: {
handler(newValue,oldValue){
// ...
},
deep: true,
}
}
在嵌套的变更中,只要没有替换对象本身,那么 newValue 和 oldValue 相同;
深度侦听需要遍历被侦听对象中的所有嵌套的属性,当用于大型数据结构时,开销很大。
即时回调: watch 默认是懒执行的:仅当数据源变化时,才会执行回调。当需要在创建侦听器时,立即执行一遍回调,需要开启 immediate 选项。
watch:{
dataObject: {
handler(newValue,oldValue){
// ...
},
immediate: true,
}
}
回调的触发时机: watch 触发相对组件更新的时机,由 flush 选项控制,有 sync、pre、post 3个值可选。sync 同步执行,pre 组件更新之前执行,post 组件更新之后执行。
watch:{
dataObject: {
handler(newValue,oldValue){
// ...
},
flush: 'post',
}
}
代码示例:
watch: {
// 1. 每当 question 改变时,这个函数就会执行
// 2. 可以用“.”运算,对对象属性侦听,如: 'object1.prop.value'(newValue,oldValue){...}
question: {
handler(newQuestion, oldQuestion) {
console.log("question have be changed.");
if (newQuestion.includes('?')) {
this.getAnswer();
}
deep: true; // 深层侦听是否开启
immediate: true // 是否强制立即执行回调
flush: 'post' // 组件更新之后执行
}
},
},
组合式
watchEffect()
待补充……
停止侦听器
选项式
对于选项式API,用 watch 选项或者 $watch() 实例方法声明的侦听器,会在宿主组件卸载时自动停止。因此,在大多数场景下,你无需关心怎么停止它。
当确实需要提前停止,必须是使用 $watch() 实例方法声明的侦听器。调用 $watch() 返回的函数:
const unwatch = this.$watch('question', callback)
此时unwatch 会有这么一个值:
() => {
effect.stop();
if (instance && instance.scope) {
remove(instance.scope.effects, effect);
}
}
是唯一对应 this.$watch('question', callback) 创建的侦听器的停止方法。
组合式
对于选项式API,在 setup() 或 <script setup> 中用同步语句创建的侦听器,会自动绑定到宿主组件实例上,并且会在宿主组件卸载时自动停止。
但是如果用异步回调创建一个侦听器,那么它不会绑定到当前组件上,你必须手动停止它,以防内存泄漏。
// 它会自动停止
watch(() => {})
// ...这个则不会!
setTimeout(() => {
watch(() => {})
}, 100)
要手动停止一个侦听器,需要调用 watch( ) 或 watchEffect( ) 返回的函数:
const unwatch = watch('question', callback,{}})
// ...当该侦听器不再需要时
unwatch()
注意:$watch( )、watch( ) 或 watchEffect( ) 返回的函数是对应原侦听器的!
代码展示:
选项式:
<!-- 待补充 -->
组合式:
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<p>
Ask a yes/no question:
<input v-model="question" />
</p>
<p>{{answer}}</p>
<button @click="unwatch">停止侦听</button>
</div>
</body>
<script type="module">
import { createApp, ref, watch } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js'
createApp({
setup() {
const question = ref('');
const answer = ref('Questions usually contain a question mark. ;-)');
const dosomething = async (newQuestion, oldQuestion) => {
console.log("question have be changed.");
if (newQuestion.indexOf('?') > -1) {
answer.value = 'Thinking...'
try {
const res = await fetch('https://yesno.wtf/api')
answer.value = (await res.json()).answer
} catch (error) {
answer.value = 'Error! Could not reach the API. ' + error
}
}
};
// 1. 每当 question 改变时,这个函数就会执行
// 2. 可以用“.”运算,对对象属性侦听,如: 'object1.prop.value'(newValue,oldValue){...}
const stopwatch = watch(question, dosomething, {
// deep: true, // 深层侦听是否开启,在嵌套的变更中,只要没有替换对象本身,那么`newValue`和`oldValue`相同
// immediate: true, // 是否强制立即执行回调
// flush: 'post', // {sync,pre,post}3个值可选,sync同步执行,pre组件更新之前执行,post组件更新之后执行
});
const unwatch = () => {
stopwatch();
console.log("停止侦听");
console.log(stopwatch);
}
return {
question, answer, unwatch, stopwatch
}
}
}).mount('#app')
</script>
</html>
内容参考自:
Vue.js 官方文档
vue3-watch、watchEffect侦听器 - 我的猫在哪里 - 博客园