加载中...
侦听器
第1节:环境准备与项目创建
第2节:响应式基础
第3节:计算属性
第4节:侦听器
第5节:生命周期
第6节:组件基础
第7节:实战1——添加账本数据
第8节:实战2——完善小账本
第9节:实战3——Toast插件与项目打包
课文封面

侦听响应式数据的变化,可在数据变化时执行相应的操作

  1. watch函数的使用
  2. watchEffect函数的使用

侦听器🎞️

侦听器和计算属性都可以用于侦听响应式数据的变化,如果需要在数据变化后执行操作,修改依赖项,那么就应该使用侦听器
watchwatchEffect都可以侦听数据源并执行回调操作,不同的是他们追踪响应式依赖的方式。

watch:只追踪指定的数据源,所以可以精确地控制回调函数的触发时机

watchEffect:自动追踪回调内的响应式数据,相比watch更加简洁,但有时其响应性依赖关系会不那么明确。

watch函数

第一个参数需要指定需要侦听的数据。
可以是响应式数据(ref、reactive、computed…)、getter函数、也可以是以上数据源组成的数组,也就是同时侦听多个数据源。

第二个参数是在侦听到数据变动时,需要执行的回调方法。
该回调方法有两个参数,第一个为数据更新后的值,第二个为数据更新前的值。

import { ref, watch } from 'vue' const count = ref(0) const price = ref(10) // 侦听响应式数据 watch(count, (newVal, oldVal) => { // 每当count更新时,都会执行这里的函数 console.log("new count:", newVal) // 1 更新后的值 console.log("old count:", oldVal) // 0 更新前的值 }) // 侦听getter函数 watch( () => count.value * price.value, (newVal, oldVal) => { // 每当count或者price更新时,都会执行这里的函数 console.log("新总价:", newVal) // 10 更新后的值 console.log("旧总价:", oldVal) // 0 更新前的值 }) // 使用数组侦听多个数据源 watch( [count, () => price.value], (newValArr, oldValArr) => { // 每当count或者price更新时,都会执行这里的函数 // 需要注意此时的回调数据为数组 // newValArr[0] count // newValArr[1] () => price.value] // oldValArr 同上 console.log(newValArr) // [1, 10] 更新后的值 console.log(oldValArr) // [0, 10] 更新前的值 }) count.value++ // 更新count值

可选配置

以上两个参数在侦听器中是必传的,除此之外watch函数还有一些可选配置。

立即执行

watch默认是在数据源更新时才会执行回调,如果想要在创建时立刻执行回调函数,就可以通过传入 immediate:true 使侦听器的回调立即执行

const count = ref(0) watch(count, (newVal,oldVal) => { console.log(newVal) // 0 console.log(oldVal) // undefind }, { immediate: true // 创建时立即执行一次回调 })

深层侦听器

给watch函数传入一个响应式对象,会隐式地创建一个深层侦听器——该回调函数在所有嵌套的变更时都会被触发:

const obj1 = reactive({ val: 1 }) watch(obj1, (newVal, oldVal) => { // 在obj的嵌套属性变更时触发 // 注意此处的newVal oldVal的值是相等的 // 因为他们是同一个对象 }) const obj2 = ref({ val: 2 }) // 传入ref侦听的对象时,添加.value才会默认创建深层侦听 watch(obj2.value, (newVal, oldVal) => {}) obj1.val++ obj2.value.val++

当直接传入ref创建的对象或是返回响应式对象的getter函数时,只有在它们的对象被整个替换时才会触发回调。如果也想创建深层侦听,可以通过传入deep:true强制转成深层侦听器。

// 当直接传入ref创建的对象或是返回响应式对象的getter函数时 // 只有在它们的对象被整个替换时才会触发回调 // 例:obj2.value = { val: 3 } // 传入deep选项即可创建深层侦听 // getter函数 watch(() => obj2.value, (newVal, oldVal) => { // 注意此处的newVal oldVal的值是相等的 // 除非替换掉整个obj2.value }, { deep: true // 当值变更时触发回调 }) // ref创建的对象数据 watch(obj2, (newVal, oldVal) => { // 注意此处的newVal oldVal的值是相等的 // 除非替换掉整个obj2.value }, { deep: true // 当值变更时触发回调 })

watchEffect函数

和计算属性一样,不需要指定要侦听的响应式数据,而是会自动跟踪回调的响应式依赖,并在依赖变化时重新执行回调函数;
并且创建后回调会立即执行一次(不需要传入 immediate:true);
相比watch函数更加简洁方便。

import { ref, watchEffect } from 'vue'; const count = ref(0) watchEffect(() => { console.log(count.value) // 0 })

停止侦听

当业务需要,或者在异步操作中创建侦听器时,
可以通过以下方法来停止侦听器。

const unwatch = watchEffect(() => { }) // 当不需要时调用 unwatch() 即可销毁侦听 unwatch()
setTimeout(() => { // 在同步中创建的侦听器在组件销毁时,自动停止,而异步中创建的不会! watchEffect(() => {}) }, 100)

DOM更新后触发回调

侦听的数据源变更,有可能同时触发vue组件更新与侦听器回调。
默认情况下,回调会在组件更新之前执行,也就是在回调中获取dom获取到的是vue组件更新之前的状态,
如果想要在回调中访问更新之后的状态那么就需要传入**flush: ‘post’**选项

watch(count, ()=>{ }, { flush: 'post' }) watchEffect(()=>{ }, { flush: 'post' })

注意点

watch函数的回调参数

回调函数的参数有两个,第一个为"新值",第二个为"旧值",如果只需要使用"新值",可以忽略第二个值,
如果监听的数据是对象,那么他们的"新值"与"旧值"是相等的,因为都指向同一个内存地址,除非整个替换掉。

尽量不要在异步中创建侦听

尽量不要在异步中创建侦听,在异步中创建的侦听不会自动销毁,如果在异步中创建侦听,请手动结束侦听。