watch
监听响应式数据的变化,才能够被watch所监听到;
vue2中的watch函数
vue2中的响应式数据是写在data
、computed
或者props
接收的数据;才能够被watch所监听到
基本写法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| <template> <div> <h2>{{ sum }}</h2> <button @click="sum++">点我加一</button> </div> </template>
<script> export default { data() { return { sum: 1, }; }, watch: { sum(newValue, oldValue) { console.log(newValue, oldValue); }, }, }; </script>
<style> </style>
|
进阶写法
相比较,就是handler
把原来的监听对象替换了,原来的监听对象以对象形式套在外面;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| export default { data() { return { sum: "1", }; }, watch: { sum: { immediate: true, deep:true, handler(newValue, oldValue) { console.log(newValue, oldValue); }, }, }, };
|
vue3中的watch函数
vue3中的响应式数据是被ref
或者reactive
包裹起来的数据;才能够被watch所监听到;
watch按需导入:import { watch } from "vue";
ref定义的一个响应式数据
ref不需要.value,因为watch监听的是一个响应式数据,如果.value了,监听的就不是proxy
代理的对象;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import { ref,watch } from "vue"; export default { setup() { let sum = ref(1); watch(sum, (newValue, oldValue) => { console.log(newValue, oldValue); }); return{ sum, } }, };
|
ref定义的多个响应式数据
方式一:有几个写几个watch
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| import { ref, watch } from "vue"; export default { setup() { let sum = ref(1); let msg = ref("hello");
watch(sum, (newValue, oldValue) => { console.log(newValue, oldValue); });
watch(msg, (newValue, oldValue) => { console.log(newValue, oldValue); }); return { sum, msg, }; }, };
|
方式二:第一个参数写成数组的形式,里面放入ref响应式对象;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| <template> <h2>{{ sum }}</h2> <button @click="sum++">点我加一</button> <br /> <h2>{{ msg }}</h2> <button @click="msg += '!'">点我加!</button> </template>
<script> import { ref, watch } from "vue"; export default { setup() { let sum = ref(1); let msg = ref("hello"); watch([sum,msg], (newValue, oldValue) => { console.log(newValue, oldValue); }); return { sum, msg, }; }, }; </script>
<style> </style>
|
ref定义的对象
watch监听ref定义的引用数据类型时,需要开启深度监听;reactive不需要开启,因为reactive源码中默认开启了深度监听;
watch监听的如果是引用数据类型,那么newVal和oldVal的值是一样的;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| <template> <div> <input type="text" v-model="message.foo.bar.name"> <br> <input type="text"> </div> </template>
<script lang="ts"> import { ref, reactive, watch } from 'vue'; export default { setup() { // 响应式数据 const message = ref({ foo: { bar: { name: "fsllala" } } });
// watch监听ref定义的引用数据类型时,需要开启深度监听;reactive不需要开启,因为reactive源码中默认开启了深度监听; watch(message, (newVal, oldVal) => { console.log(newVal, oldVal); }, { deep: true })
return { message, } } } </script>
|
监听reactive定义的一个响应式数据的全部属性
watch监听的如果是引用数据类型,那么newVal和oldVal的值是一样的;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| <template> <h2>{{ person.age }}</h2> <button @click="person.age++">点我加一</button> <br /> <h2>{{ person.name }}</h2> <button @click="person.name += '!'">点我加!</button> </template>
<script> import { reactive, watch } from "vue"; export default { setup() { let person = reactive({ name: "张三", age: 18, });
watch(person, (newValue, oldValue) => { console.log(newValue, oldValue); });
return { person, }; }, }; </script> <style> </style>
|
上面问题,目前还无法解决。。。
但是其实可以将需要用到 oldValue 的 数据写成 ref的,即可解决;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import { ref, reactive, watch } from "vue"; export default { setup() { let age = ref(18); let person = reactive({ name: "张三", }); watch(age, (newValue, oldValue) => { console.log(newValue, oldValue); });
watch(person, (newValue, oldValue) => { console.log(newValue, oldValue); });
return { person, age, }; }, };
|
reactive还有个问题就是:当监听全部属性的时候 强制
开启了深度监听,deep:false
也关不掉;会影响效率
监听reactive定义的一个响应式数据中的单一属性
第一个参数直接写 对象.属性
是不行滴,因为他不是proxy
代理的对象,需要写成()=>对象.属性
的回调函数 形式;
值的一提的是,这样监听 reactive的单一属性,是可以检测到 oldValue
的;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import { ref, reactive, watch } from "vue"; export default { setup() { let person = reactive({ name: "张三", age:18, });
watch(()=>person.age, (newValue, oldValue) => { console.log(newValue, oldValue); });
return { person, }; }, };
|
监听reactive定义的一个响应式数据中的某些属性
这个oldValue
也可以监听到,只要不是监听全部的属性,都可以监听到oldValue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import { ref, reactive, watch } from "vue"; export default { setup() { let person = reactive({ name: "张三", age:18, });
watch([()=>person.name,()=>person.age], (newValue, oldValue) => { console.log(newValue, oldValue); });
return { person, }; }, };
|
特殊情况
监听的是一个属性,但是这个属性又是一个对象,需要用到深度监听
;因为是个对象,所以oldValue
监听不到;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import { ref, reactive, watch } from "vue"; export default { setup() { let person = reactive({ name: "张三", age:18, job:{ a:{ b:"web" } } });
watch([()=>person.job], (newValue, oldValue) => { console.log(newValue, oldValue); },{deep:true});
return { person, }; }, };
|
vue3中的watchEffect函数
跟watch是不一样的,watchEffect是非惰性的,watchEffect一开始会自己自调用一次;
立即执行传入的一个函数,同时响应式追踪其依赖,并在其依赖变更时重新运行该函数;
如果用到 message 就只会监听 message ;就是用到几个监听几个 而且是非惰性 会默认调用一次;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| <template> <div> <input type="text" v-model="message"> <br> <input type="text" v-model="message2"> <br> <input type="text" v-model="objMessage.name"> </div> </template>
<script setup lang="ts"> import { ref, reactive, watchEffect } from 'vue';
let message = ref<string>("vue3.2"); let message2 = ref<string>("TS泛型"); const objMessage = reactive({ name: "fsllala", age: 18 })
watchEffect(() => { /** * watchEffect 这里ref需要加.value,因为如果不加.value,监听的是一个RefImpl对象,他的引用地址是不会变的; * 同理如果监听的是一个响应式数据,例如objMessage,即使里面的name改变了,也不会监听到,因为objMessage的引用地址是没变的; * */ // 监听谁,写在这里面就好了,会立即执行一次; console.log("message=====", message); console.log("message2=====", message2.value); console.log("objMessage=====", objMessage.name); }) </script>
|
清除副作用
就是在触发监听之前会调用一个函数可以处理你的逻辑例如防抖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| import { ref, reactive, watchEffect } from 'vue';
let message = ref<string>("vue3.2"); let message2 = ref<string>("TS泛型"); const objMessage = reactive({ name: "fsllala", age: 18 })
watchEffect((fsl) => {
console.log("message=====", message.value); console.log("message2=====", message2.value); console.log("objMessage=====", objMessage.name); fsl(() => { console.log("before~"); }) })
|
停止监听
停止跟踪 :watchEffect 返回一个函数 ,调用之后将停止更新;
离开界面可以做停止监听;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <template> <div> <input type="text" v-model="message"> <button @click="stopWatch">stopWatch</button> </div> </template>
<script setup lang="ts"> import { ref, reactive, watchEffect } from 'vue';
let message = ref<string>("vue3.2");
const stop = watchEffect((fsl) => { console.log("message=====", message.value); })
const stopWatch=()=>stop();
</script>
|
更多的配置项
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <template> <div> <input type="text" v-model="message" id="inputId"> </div> </template>
<script setup lang="ts"> import { ref, reactive, watchEffect } from 'vue';
let message = ref<string>("vue3.2");
watchEffect(() => { let domID: HTMLInputElement = document.getElementById('inputId') as HTMLInputElement;
console.log("message=====", message.value); console.log("inputId=====", domID); }, { flush: "post" })
</script>
|
断言 (assertion) 是一种在程序中的一阶逻辑 (如:一个结果为真或假的逻辑判断式),目的为了表示与验证软件开发者预期的结果 —— 当程序执行到断言的位置时,对应的断言应该为真。若断言不为真时,程序会中止执行,并给出错误信息。
|
pre |
sync |
post |
更新时机 |
组件更新前执行 |
强制效果始终同步触发 |
组件更新后执行 |