vue3.0(八) 监听器(watch),高级监听器(watchEffect)


watch

watch特性进行了一些改变和优化。与computed不同,watch通常用于监听数据的变化,并执行一些副作用,侦听一个或多个响应式数据源,并在数据源变化时调用所给的回调函数。

1 watch的用法

  1. watch的基本用法
    watch(source, callback, options?)
    

    source表示要监听的数据,可以是一个响应式的数据对象、一个计算属性或一个方法的返回值;callback表示当数据发生变化时要执行的回调函数;options表示watch的一些配置选项,例如immediate、deep、flush等。

  2. 监听 ref 定义的响应式数据
    <template>
      <div>
        <div>值:{{count}}</div>
        <button @click="add">改变值</button>
      </div>
    </template>
     
    <script>
    import { ref, watch } from 'vue'
    export default {
      setup () {
        const count = ref(0)
        const add = () => {
          count.value++
        }
        watch(count, (newVal, oldVal) => {
          console.log('值改变了', newVal, oldVal)
        })
        return {
          count,
          add
        }
      }
    }
    </script>
    
    上述代码执行效果
  3. 监听 reactive 定义的响应式数据
    <template>
      <div>
        <div>姓名:{{ obj.name }}</div>
        <div>年龄:{{ obj.age }}</div>
        <button @click="changeName">改变值</button>
      </div>
    </template>
     
    <script>
    import { reactive, watch } from 'vue'
    export default {
      setup () {
        const obj = reactive({
          name: 'zs',
          age: 14
        })
        const changeName = () => {
          obj.name = 'ls'
        }
        watch(obj, (newVal, oldVal) => {
          console.log('值改变了', newVal, oldVal)
        })
        return {
          obj,
          changeName
        }
      }
    }
    </script>
    

上述代码执行结果
注:此处监听的新值和旧值相同,主要是因为新旧值引用地址是相同的,
此处可采取computed计算属性先实现深拷贝。

	<template>
	  <div>
	    <div>姓名:{{ obj.name }}</div>
	    <div>年龄:{{ obj.age }}</div>
	    <button @click="changeName">改变值</button>
	  </div>
	</template>
	 
	<script>
	import { reactive, watch, computed } from 'vue'
	export default {
	  setup () {
	    const obj = reactive({
	      name: 'zs',
	      age: 14
	    })
	    const changeName = () => {
	      obj.name = 'ls'
	    }
	    const deepObj = computed(() => {
	      return JSON.parse(JSON.stringify(obj))
	    })
	    watch(deepObj, (newVal, oldVal) => {
	      console.log('值改变了', newVal, oldVal)
	    })
	  
	    return {
	      obj,
	      changeName
	    }
	  }
	}
	</script>
  1. 监听多个ref的值,采用数组形式
    <template>
      <div>
        <!-- 侦听多个变量 -->
        姓名:<input v-model="userName" type="text">
        年龄:<input v-model="age" type="number">
      </div>
    </template>
    <script lang="ts">
    import { defineComponent, ref, watch } from 'vue'
    export default defineComponent({
      setup () {
        const userName = ref<string>('张三')
        const age = ref<number>(18)
        watch([userName, age], (newVal, oldVal) => {
          console.log(newVal, oldVal)
        })
        return {
          userName,
          age
        }
      }
    })
    </script>
    

在这里插入图片描述

  1. 监听reactive对象中某个属性的变化
    <template>
      <div>
        <div>{{obj.name}}</div>
        <div>{{obj.age}}</div>
        <button @click="changeName">改变值</button>
      </div>
    </template>
     
    <script>
    import { reactive, watch } from 'vue'
    export default {
      setup () {
        const obj = reactive({
          name: 'zs',
          age: 14
        })
        const changeName = () => {
          obj.age++
        }
        watch(() => obj.age, (newVal, oldVal) => {
          console.log(newVal, oldVal)
        })
        return {
          obj,
          changeName
        }
      }
    }
    </script>
    

在这里插入图片描述

2 watch的高级用法

  1. 一次性监听
    每当被侦听源发生变化时,侦听器的回调就会执行。如果希望回调只在源变化时触发一次,请使用 once: true 选项。
    <template>
     <div>
        <div>{{obj.name}}</div>
        <div>{{obj.classInfo.studentNum}}</div>
        <button @click="changeName">改变值</button>
      </div>
    </template>
     
    <script>
    import { reactive, watch } from 'vue'
    export default {
      setup () {
        const obj = reactive({
          name: 'zs',
          age: 14,
          classInfo: {
            className: '一年级',
            studentNum: 45
          }
        })
        const changeName = () => {
          obj.classInfo.studentNum++
        }
        watch(() => obj, (newVal, oldVal) => {
          console.log(newVal, oldVal)
        }, {
          once: true
        })
        return {
          obj,
          changeName
        }
      }
    }
    </script>
    
  2. 深层侦听器
    直接给 watch() 传入一个响应式对象,会隐式地创建一个深层侦听器——该回调函数在所有嵌套的变更时都会被触发:deep 选项,强制转成深层侦听器:
    <template>
      <div>
        <div>{{obj.name}}</div>
        <div>{{obj.classInfo.studentNum}}</div>
        <button @click="changeName">改变值</button>
      </div>
    </template>
     
    <script>
    import { reactive, watch } from 'vue'
    export default {
      setup () {
        const obj = reactive({
          name: 'zs',
          age: 14,
          classInfo: {
            className: '一年级',
            studentNum: 45
          }
        })
        const changeName = () => {
          obj.classInfo.studentNum++
        }
        watch(() => obj, (newVal, oldVal) => {
          console.log(newVal, oldVal)
        }, {
          deep: true // 不添加deep属性,watch不会触发
        })
        return {
          obj,
          changeName
        }
      }
    }
    </script>
    

    注意:当我们使用deep选项时,watch的性能会受到一定的影响,因为Vue需要对对象或数组进行递归遍历。因此,只有在必要的情况下才应该使用deep选项

  3. 即时回调的侦听器
    watch 默认是懒执行的:仅当数据源变化时,才会执行回调。但在某些场景中,我们希望在创建侦听器时,立即执行一遍回调。举例来说,我们想请求一些初始数据,然后在相关状态更改时重新请求数据。通过传入 immediate: true 选项来强制侦听器的回调立即执行:
    import { reactive, watch } from 'vue'
    const state = reactive({
      count: 2
    })
    watch(
      () => state.count,
      (newVal, oldVal) => {
        console.log(newVal, oldVal)
      },
      { immediate: true }
    )
    
  4. flush 回调的触发时机
  • sync:同步模式下执行
  • pre:在数据变化之前执行回调函数
  • post:在数据变化之后执行回调函数,但是需要等待所有依赖项都更新后才执行
    import { reactive, watch } from 'vue'
    const state = reactive({
      count: 2
    })
    watch(
      () => state.count,
      (newVal, oldVal) => {
        console.log(newVal, oldVal)
      },
      { flush: sync }
    )
    

3 watch性能优化

  1. 使用computed代替watch
    import { reactive, computed } from 'vue'
    const state = reactive({
      count: 2
    })
    
    const doubleCount = computed(() => {
      return state.count * 2
    })
    console.log(doubleCount.value) // 输出:2
    state.count++
    console.log(doubleCount.value) // 输出:4
    
  2. 使用throttle和debounce控制回调函数的执行频率
    频繁地监听一个值的变化,并在变化时执行一些操作。如果回调函数执行的太频繁,会影响性能。为了避免这种情况,我们可以使用throttle和debounce控制回调函数的执行频率
    throttle可以用于控制函数在一定时间内只能执行一次,
    debounce可以用于控制函数在一定时间内不会连续执行
    import { reactive, watch } from 'vue'
    import { throttle } from 'lodash-es'
    const state = reactive({
      count: 3
    })
    watch(
      () => state.count,
      throttle((newVal, oldVal) => {
        console.log(newVal, oldVal)
      }, 1000)
    )
    state.count++
    

watchEffect

watchEffect 函数来创建高级侦听器。与 watch 和 computed 不同,
watchEffect 不需要指定依赖项,自动追踪响应式状态的变化,并在变化时重新运行

<template>
  <div>
    <div>{{obj.name}}</div>
    <div>{{obj.age}}</div>
    <button @click="changeAge">改变值</button>
  </div>
</template>
<script>
import { reactive, watch, watchEffect } from 'vue'
export default {
  setup () {
    const obj = reactive({
      name: 'zs',
      age: 14,
    })
    const changeName = () => {
      obj.age++
    }
    watchEffect(() => {
      console.log(obj.age)
    })
    return {
      obj,
      changeAge
    }
  }
}
</script>

1 停止监听

watchEffect 函数不会返回一个停止侦听的函数。如果我们需要停止侦听,我们可以将 watchEffect 的返回值设为 null

const stop = watchEffect(() => {})

// 当不再需要此侦听器时:
stop()

2 侦听多个状态

如果需要侦听多个响应式状态,可以在 watchEffect 函数中使用这些状态,并在函数中返回一个计算值,

<script>
import { reactive, watchEffect } from 'vue'
export default {
  setup () {
    const state = reactive({
      count1: 2,
      count2: 3
    })
    watchEffect(() => {
      const sum = state.count1 + state.count2
      console.log(sum)
    })
    // 改变状态,输出  5
    state.count1++

    // 改变状态,输出 8
    state.count2 += 2
    return {
    }
  }
}
</script>

3 懒执行

watchEffect 函数也支持懒执行(lazy evaluation)。如果我们将 watchEffect 的第二个参数设置为 { lazy: true },则这个函数会在第一次访问响应式状态时才会被运行

const state = reactive({
   count: 2
 })
 watchEffect(() => {
   console.log(state.count)
 }, { lazy: true })
 // 改变状态,输出  2 3
 state.count++

watch和watchEffect的区别

watch 和 watchEffect 都能响应式地执行有副作用的回调。它们之间的主要区别是追踪响应式依赖的方式:
watch 监听函数可以添加配置项,也可以配置为空,配置项为空的情况下,
watch的特点为:

  • 有惰性:运行的时候,不会立即执行;
  • 更加具体:需要添加监听的属性;
  • 可访问属性之前的值:回调函数内会返回最新值和修改之前的值;
  • 可配置:配置项可补充 watch 特点上的不足:
  • immediate:配置 watch 属性是否立即执行,值为 true 时,一旦运行就会立即执行,值为 false时,保持惰性。
  • deep:配置 watch 是否深度监听,值为 true 时,可以监听对象所有属性,值为 false 时保持更加具体特性,必须指定到具体的属性上。
    watchEffect 特点
  • 非惰性:一旦运行就会立即执行;
  • 更加抽象:使用时不需要具体指定监听的谁,回调函数内直接使用就可以;
  • 不可访问之前的值:只能访问当前最新的值,访问不到修改之前的值;

watch和computed的区别

computed(计算属性):

  1. 定义:computed是一个基于其依赖的数据进行计算的值,可以缓存计算的结果。
  2. 工作原理:Vue会自动追踪computed属性方法对data的数据依赖,并在依赖数据发生变化时重新计算computed属性的值。
    computed特点:
    • 支持缓存,只有依赖数据发生改变时才会重新进行计算。
    • 不支持异步操作,如果computed内有异步操作,则无效。
    • 必须通过return返回一个值。
    • 适合当一个属性受多个属性影响时使用。
    • 在模板中可以直接使用,访问时不需要加括号。
      watch(监听):
  3. 定义:watch用于监听响应式数据的变化,并在数据发生变化时执行一些自定义的操作。
  4. 工作原理:当监听的数据发生变化时,会直接触发相应的操作。
    watch特点:
    • 不支持缓存功能,监听的数据发生变化时会直接触发相应的操作。
    • 支持异步操作,适合监听路由和设置计时器等。
    • 可以没有返回值。
    • 适合当一条数据影响多条数据时使用。
    • 在模板中不能直接使用,需要通过事件或方法进行调用。
  • 34
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值