用于声明在数据更改时调用的侦听回调。
watch 选项期望接受一个对象,其中键是需要侦听的响应式组件实例属性 (例如,通过 data 或 computed 声明的属性)——值是相应的回调函数。该回调函数接受被侦听源的新值和旧值。
除了一个根级属性,键名也可以是一个简单的由点分隔的路径,例如 a.b.c。注意,这种用法不支持复杂表达式——仅支持由点分隔的路径。如果你需要侦听复杂的数据源,可以使用命令式的 $watch() API。
值也可以是一个方法名称的字符串 (通过 methods 声明),或包含额外选项的对象。当使用对象语法时,回调函数应被声明在 handler 中。额外的选项包含:
immediate:在侦听器创建时立即触发回调。第一次调用时,旧值将为 undefined。deep:如果源是对象或数组,则强制深度遍历源,以便在深度变更时触发回调。详见深层侦听器。flush:调整回调的刷新时机。详见回调的触发时机及 watchEffect()。onTrack / onTrigger:调试侦听器的依赖关系。详见侦听器调试。声明侦听器回调时避免使用箭头函数,因为它们将无法通过 this 访问组件实例。
作用:监视数据的变化(和Vue2中的watch作用一致)
特点:Vue3中的watch只能监视以下四种数据:
ref定义的数据。
reactive定义的数据。函数返回一个值(
getter函数)。一个包含上述内容的数组。
监视ref定义的【基本类型】数据:直接写数据名即可,监视的是其value值的改变。
- <div class="person">
- <h1>情况一:监视【ref】定义的【基本类型】数据h1>
- <h2>当前求和为:{{sum}}h2>
- <button @click="changeSum">点我sum+1button>
- div>
-
- <script lang="ts" setup name="Person">
- import {ref,watch} from 'vue'
- // 数据
- let sum = ref(0)
- // 方法
- function changeSum(){
- sum.value += 1
- }
- // 监视,情况一:监视【ref】定义的【基本类型】数据
- const stopWatch = watch(sum,(newValue,oldValue)=>{
- console.log('sum变化了',newValue,oldValue)
- if(newValue >= 10){
- stopWatch()
- }
- })
- script>
监视ref定义的【对象类型】数据:直接写数据名,监视的是对象的【地址值】,若想监视对象内部的数据,要手动开启深度监视。
注意:
若修改的是
ref定义的对象中的属性,newValue和oldValue都是新值,因为它们是同一个对象。若修改整个
ref定义的对象,newValue是新值,oldValue是旧值,因为不是同一个对象了。
- <div class="person">
- <h1>情况二:监视【ref】定义的【对象类型】数据h1>
- <h2>姓名:{{ person.name }}h2>
- <h2>年龄:{{ person.age }}h2>
- <button @click="changeName">修改名字button>
- <button @click="changeAge">修改年龄button>
- <button @click="changePerson">修改整个人button>
- div>
-
- <script lang="ts" setup name="Person">
- import {ref,watch} from 'vue'
- // 数据
- let person = ref({
- name:'张三',
- age:18
- })
- // 方法
- function changeName(){
- person.value.name += '~'
- }
- function changeAge(){
- person.value.age += 1
- }
- function changePerson(){
- person.value = {name:'李四',age:90}
- }
- /*
- 监视,情况一:监视【ref】定义的【对象类型】数据,监视的是对象的地址值,若想监视对象内部属性的变化,需要手动开启深度监视
- watch的第一个参数是:被监视的数据
- watch的第二个参数是:监视的回调
- watch的第三个参数是:配置对象(deep、immediate等等.....)
- */
- watch(person,(newValue,oldValue)=>{
- console.log('person变化了',newValue,oldValue)
- },{deep:true})
-
- script>
监视reactive定义的【对象类型】数据,且默认开启了深度监视。
- <div class="person">
- <h1>情况三:监视【reactive】定义的【对象类型】数据h1>
- <h2>姓名:{{ person.name }}h2>
- <h2>年龄:{{ person.age }}h2>
- <button @click="changeName">修改名字button>
- <button @click="changeAge">修改年龄button>
- <button @click="changePerson">修改整个人button>
- <hr>
- <h2>测试:{{obj.a.b.c}}h2>
- <button @click="test">修改obj.a.b.cbutton>
- div>
- template>
-
- <script lang="ts" setup name="Person">
- import {reactive,watch} from 'vue'
- // 数据
- let person = reactive({
- name:'张三',
- age:18
- })
- let obj = reactive({
- a:{
- b:{
- c:666
- }
- }
- })
- // 方法
- function changeName(){
- person.name += '~'
- }
- function changeAge(){
- person.age += 1
- }
- function changePerson(){
- Object.assign(person,{name:'李四',age:80})
- }
- function test(){
- obj.a.b.c = 888
- }
-
- // 监视,情况三:监视【reactive】定义的【对象类型】数据,且默认是开启深度监视的
- watch(person,(newValue,oldValue)=>{
- console.log('person变化了',newValue,oldValue)
- })
- watch(obj,(newValue,oldValue)=>{
- console.log('Obj变化了',newValue,oldValue)
- })
- script>
监视ref或reactive定义的【对象类型】数据中的某个属性,注意点如下:
若该属性值不是【对象类型】,需要写成函数形式。
若该属性值是依然是【对象类型】,可直接编,也可写成函数,建议写成函数。
结论:监视的要是对象里的属性,那么最好写函数式,注意点:若是对象监视的是地址值,需要关注对象内部,需要手动开启深度监视。
- <div class="person">
- <h1>情况四:监视【ref】或【reactive】定义的【对象类型】数据中的某个属性h1>
- <h2>姓名:{{ person.name }}h2>
- <h2>年龄:{{ person.age }}h2>
- <h2>汽车:{{ person.car.c1 }}、{{ person.car.c2 }}h2>
- <button @click="changeName">修改名字button>
- <button @click="changeAge">修改年龄button>
- <button @click="changeC1">修改第一台车button>
- <button @click="changeC2">修改第二台车button>
- <button @click="changeCar">修改整个车button>
- div>
-
- <script lang="ts" setup name="Person">
- import {reactive,watch} from 'vue'
-
- // 数据
- let person = reactive({
- name:'张三',
- age:18,
- car:{
- c1:'奔驰',
- c2:'宝马'
- }
- })
- // 方法
- function changeName(){
- person.name += '~'
- }
- function changeAge(){
- person.age += 1
- }
- function changeC1(){
- person.car.c1 = '奥迪'
- }
- function changeC2(){
- person.car.c2 = '大众'
- }
- function changeCar(){
- person.car = {c1:'雅迪',c2:'爱玛'}
- }
-
- // 监视,情况四:监视响应式对象中的某个属性,且该属性是基本类型的,要写成函数式
- /* watch(()=> person.name,(newValue,oldValue)=>{
- console.log('person.name变化了',newValue,oldValue)
- }) */
-
- // 监视,情况四:监视响应式对象中的某个属性,且该属性是对象类型的,可以直接写,也能写函数,更推荐写函数
- watch(()=>person.car,(newValue,oldValue)=>{
- console.log('person.car变化了',newValue,oldValue)
- },{deep:true})
- script>
监视上述的多个数据
- <div class="person">
- <h1>情况五:监视上述的多个数据h1>
- <h2>姓名:{{ person.name }}h2>
- <h2>年龄:{{ person.age }}h2>
- <h2>汽车:{{ person.car.c1 }}、{{ person.car.c2 }}h2>
- <button @click="changeName">修改名字button>
- <button @click="changeAge">修改年龄button>
- <button @click="changeC1">修改第一台车button>
- <button @click="changeC2">修改第二台车button>
- <button @click="changeCar">修改整个车button>
- div>
-
- <script lang="ts" setup name="Person">
- import {reactive,watch} from 'vue'
-
- // 数据
- let person = reactive({
- name:'张三',
- age:18,
- car:{
- c1:'奔驰',
- c2:'宝马'
- }
- })
- // 方法
- function changeName(){
- person.name += '~'
- }
- function changeAge(){
- person.age += 1
- }
- function changeC1(){
- person.car.c1 = '奥迪'
- }
- function changeC2(){
- person.car.c2 = '大众'
- }
- function changeCar(){
- person.car = {c1:'雅迪',c2:'爱玛'}
- }
-
- // 监视,情况五:监视上述的多个数据
- watch([()=>person.name,person.car],(newValue,oldValue)=>{
- console.log('person.car变化了',newValue,oldValue)
- },{deep:true})
-
- script>
监听过程中由于浅拷贝和深拷贝的原因可能会出现newvalue和oldvalue相同的情况。
通过深拷贝(以扩展运算符为例),解决newValue与 oldValue值相同问题
-
- const refData = ref(['a','b','c'])
- watch(
- () => [...refData.value],
- (newValue, oldValue) => {
- console.log(newValue, oldValue)
- },
- { deep: true }
- )
立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行该函数。
watch对比watchEffect
都能监听响应式数据的变化,不同的是监听数据变化的方式不同
watch:要明确指出监视的数据
watchEffect:不用明确指出监视的数据(函数中用到哪些属性,那就监视哪些属性)。
- <div class="person">
- <h1>需求:水温达到50℃,或水位达到20cm,则联系服务器h1>
- <h2 id="demo">水温:{{temp}}h2>
- <h2>水位:{{height}}h2>
- <button @click="changePrice">水温+1button>
- <button @click="changeSum">水位+10button>
- div>
-
- <script lang="ts" setup name="Person">
- import {ref,watch,watchEffect} from 'vue'
- // 数据
- let temp = ref(0)
- let height = ref(0)
-
- // 方法
- function changePrice(){
- temp.value += 10
- }
- function changeSum(){
- height.value += 1
- }
-
- // 用watch实现,需要明确的指出要监视:temp、height
- watch([temp,height],(value)=>{
- // 从value中获取最新的temp值、height值
- const [newTemp,newHeight] = value
- // 室温达到50℃,或水位达到20cm,立刻联系服务器
- if(newTemp >= 50 || newHeight >= 20){
- console.log('联系服务器')
- }
- })
-
- // 用watchEffect实现,不用
- const stopWtach = watchEffect(()=>{
- // 室温达到50℃,或水位达到20cm,立刻联系服务器
- if(temp.value >= 50 || height.value >= 20){
- console.log(document.getElementById('demo')?.innerText)
- console.log('联系服务器')
- }
- // 水温达到100,或水位达到50,取消监视
- if(temp.value === 100 || height.value === 50){
- console.log('清理了')
- stopWtach()
- }
- })
- script>
有点组件的全局引入和精确引入的感觉。