侦听器
watch允许我们手动的去侦听特定的数据源并在回调函数中执行副作用,默认情况下它是惰性的,因为只有当被侦听的数据源发生改变时才会执行回调。
(1)懒执行副作用,当然可以设置immediate属性让其立即执行。
(2)可以访问侦听状态变化前后数据的值。
watch的基本使用
<template id="my-app">
您的问题: <input type="text" v-model="question" />
<button @click="queryAnswer">查找答案</button>
</template>
<script>
const App = {
template: "#my-app",
data() {
return {
question: "Hello World",
};
},
watch: {
// question:侦听的data中的属性的名称
// newValue:变化后的新值
// oldValue:变化前的旧值
question: function (newValue, oldValue) {
console.log("新值: ", newValue, "旧值", oldValue);
this.queryAnswer(); //懒执行的,只有当所监听的属性发生变化才会执行回调
},
},
methods: {
queryAnswer() {
console.log(`你的问题${this.question}的答案是哈哈哈哈哈`);
},
},
};
Vue.createApp(App).mount("#app");
</script>
需要注意的是在默认情况下,watch只能监听数据本身的改变并不会监听到数据内部即对象嵌套所发生的改变,因此我们可以使用对象的方式去使用更多的属性去完成对其内部嵌套数据的深度监听(例如:handler、deep、immediate)
<template id="my-app">
<h2>{{info.name}}</h2>
<button @click="changeInfo">改变info</button>
<button @click="changeInfoName">改变info.name</button>
<button @click="changeInfoNbaName">改变info.nba.name</button>
</template>
<script>
const App = {
template: "#my-app",
data() {
return {
//存在对象的嵌套
info: { name: "why", age: 18, nba: { name: "kobe" } },
};
},
watch: {
// 默认情况下我们的侦听器只会针对监听的数据本身的改变(内部发生的改变是不能侦听)
// 进行深度侦听/立即执行(一定会执行一次)
info: {
handler: function (newInfo, oldInfo) {
console.log(
"newValue:",
newInfo.nba.name,
"oldValue:",
oldInfo.nba.name
);
},
deep: true, // 深度侦听
// immediate: true // 立即执行
},
},
methods: {
changeInfo() {
this.info = { name: "kobe" };
},
changeInfoName() {
this.info.name = "kobe";
},
changeInfoNbaName() {
this.info.nba.name = "james";
},
},
};
Vue.createApp(App).mount("#app");
</script>
watch的其他使用方式
<template id="my-app">
<h2>{{info.name}}</h2>
<button @click="changeInfo">改变info</button>
<button @click="changeInfoName">改变info.name</button>
<button @click="changeInfoNbaName">改变info.nba.name</button>
<button @click="changeFriendName">改变friends[0].name</button>
</template>
<script>
const App = {
template: "#my-app",
data() {
return {
info: { name: "why", age: 18, nba: { name: "kobe" } },
friends: [{ name: "why" }, { name: "kobe" }],
};
},
watch: {
//可以对数据整体做一个监听
info(newValue, oldValue) {
console.log(newValue, oldValue);
},
//也可以对对象的某一个属性单独做一个监听
"info.name": function (newName, oldName) {
console.log(newName, oldName);
},
"friends[0].name": function (newName, oldName) {
console.log(newName, oldName);
},
// friends: {
// handler(newFriends, oldFriend) {
// },
// deep: true
// }
},
methods: {
changeInfo() {
this.info = { name: "kobe" };
},
changeInfoName() {
this.info.name = "kobe";
},
changeInfoNbaName() {
this.info.nba.name = "james";
},
changeFriendName() {
this.friends[0].name = "curry";
},
},
created() {
//注意:返回值为一个函数
const unwatch = this.$watch(
"info",
function (newInfo, oldInfo) {
console.log(newInfo, oldInfo);
},
{
deep: true,
immediate: true,
}
);
// unwatch() //再次调用便是取消监听
},
};
Vue.createApp(App).mount("#app");
</script>
vue3中侦听器的使用情况
在vue2中,我们可以通过watch选项来侦听数据变化,但是在vue3中,我们可以使用watchEffect和watch两种方式来共同完成对数据的侦听,而二者的主要区别是watch需要手动指定侦听的数据源,而watchEffect用于自动收集响应式数据的依赖。在vue3中,watch的使用基本不变,因此我们主要关注watchEffect的使用即可。
watchEffect的基本使用
<template>
<div>
<h2>{{name}}-{{age}}</h2>
<button @click="changeName">修改name</button>
<button @click="changeAge">修改age</button>
</div>
</template>
<script>
import { ref, watchEffect } from 'vue';
export default {
setup() {
// watchEffect: 自动收集响应式的依赖
const name = ref("why");
const age = ref(18);
const changeName = () => name.value = "kobe"
const changeAge = () => age.value++
watchEffect(() => {
console.log("name:", name.value, "age:", age.value);
});
return {
name,
age,
changeName,
changeAge
}
}
}
</script>
watchEffect的执行时机
setup函数在执行时就会立即执行watchEffect这个函数,而这个时候DOM并没有挂载,所以打印为null,但是当DOM挂载后会给title的ref对象赋值新的值,这时候watchEffect函数再执行的话就会打印出来对应的数据。但是如果我们希望在第一次的时候就打印出来对应的元素呢?因此这个时候我们需要改变副作用函数的执行时机,将默认值pre改为post即可。除此之外flush选项还接受sync,这将强制效果始终同步触发,然而这是低效的,应该很少使用。
<template>
<div>
<h2 ref="title">哈哈哈</h2>
</div>
</template>
<script>
import { ref, watchEffect } from 'vue';
export default {
setup() {
const title = ref(null);
watchEffect(() => {
console.log(title.value);
}, {
flush: "post"
})
return {
title
}
}
}
</script>
watchEffect清除副作用
什么是清除副作用呢?比如在开发中我们需要在侦听函数中执行网络请求,但是在网络请求还没有到达的时候,我们就停止了侦听器或者侦听器函数被再次执行了,那么上一次的网络请求就应该被取消掉,这个时候我们就需要清除上一次的副作用,在我们给watchEffect传入的函数被回调时,其实可以获取到一个onInvalidate参数,而它本身也是一个函数,因此我们可以在onInvalidate函数中去清除副作用。
<template>
<div>
<h2>{{name}}-{{age}}</h2>
<button @click="changeName">修改name</button>
<button @click="changeAge">修改age</button>
</div>
</template>
<script>
import { ref, watchEffect } from 'vue';
export default {
setup() {
// watchEffect: 自动收集响应式的依赖
const name = ref("why");
const age = ref(18);
//我们可以在回调函数中又传入一个参数,而这个参数本身又是一个函数
const stop = watchEffect((onInvalidate) => {
const timer = setTimeout(() => {
console.log("网络请求成功~");
}, 2000)
// 根据name和age两个变量发送网络请求
onInvalidate(() => {
// 在这个函数中清除额外的副作用
// request.cancel()
clearTimeout(timer);
console.log("onInvalidate");
})
console.log("name:", name.value, "age:", age.value);
});
const changeName = () => name.value = "kobe"
const changeAge = () => {
age.value++;
if (age.value > 25) {
stop();
}
}
return {
name,
age,
changeName,
changeAge
}
}
}
</script>
watchEffect停止侦听
<template>
<div>
<h2>{{name}}-{{age}}</h2>
<button @click="changeName">修改name</button>
<button @click="changeAge">修改age</button>
</div>
</template>
<script>
import { ref, watchEffect } from 'vue';
export default {
setup() {
// watchEffect: 自动收集响应式的依赖
const name = ref("why");
const age = ref(18);
//返回值为函数
const stop = watchEffect(() => {
console.log("name:", name.value, "age:", age.value);
});
const changeName = () => name.value = "kobe"
const changeAge = () => {
age.value++;
if (age.value > 25) {
stop();//再次调用即停止侦听
}
}
return {
name,
age,
changeName,
changeAge
}
}
}
</script>