Vue3-数据的监听-学习笔记整理

196 阅读5分钟

笔记整理-主要参考:vue3.chengpeiquan.com/

数据的监听

  • 应用场景:监听路由变化,监听参数变化
  • 特点比较:vue3保留原来的watch,新增了watchEffect进行更简单监听

watch

基础用法
// 不要忘了导入要用的 API
import { defineComponent, reactive, watch } from 'vue'

export default defineComponent({
  setup() {
    // 定义一个响应式数据
    const userInfo = reactive({
      name: 'Petter',
      age: 18,
    })

    // 2s后改变数据
    setTimeout(() => {
      userInfo.name = 'Tom'
    }, 2000)

    /**
     * 可以直接监听这个响应式对象
     * callback 的参数如果不用可以不写
     */
    watch(userInfo, () => {
      console.log('监听整个 userInfo ', userInfo.name)
    })

    /**
     * 也可以监听对象里面的某个值
     * 此时数据源需要写成 getter 函数
     */
    watch(
      // 数据源,getter 形式
      () => userInfo.name,
      // 回调函数 callback
      (newValue, oldValue) => {
        console.log('只监听 name 的变化 ', userInfo.name)
        console.log('打印变化前后的值', { oldValue, newValue })
      }
    )
  },
})
批量监听
  • 批量监听的应用场景:子组件有多个prop,任意一个prop变化,都需执行初始化函数重置组件的状态
  • 写法1特点

    • 抽离相同的处理行为为公共函数
    • 定义多个监听操作,并传入公共函数
import { defineComponent, ref, watch } from 'vue'

export default defineComponent({
  setup() {
    const message = ref<string>('')
    const index = ref<number>(0)

    // 2s后改变数据
    setTimeout(() => {
      // 来到这里才会触发 watch 的回调
      message.value = 'Hello World!'
      index.value++
    }, 2000)

    // 抽离相同的处理行为为公共函数
    const handleWatch = (
      newValue: string | number,
      oldValue: string | number
    ): void => {
      console.log({ newValue, oldValue })
    }

    // 然后定义多个监听操作,传入这个公共函数
    watch(message, handleWatch)
    watch(index, handleWatch)
  },
})
  • 写法2特点

    • 数据源和回调函数都变成数组形式
    • 数据源:数组形式传入,每一项都是响应式数据
    • 回调参数:原来的value和newValue都变成数组,每个数组里的顺序和数据源数组排序一致
  • 写法3:使用watchEffect完成批量监听
import { defineComponent, ref, watch } from 'vue'

export default defineComponent({
  setup() {
    // 定义多个数据源
    const message = ref<string>('')
    const index = ref<number>(0)

    // 2s后改变数据
    setTimeout(() => {
      message.value = 'Hello World!'
      index.value++
    }, 2000)

    watch(
      // 数据源改成了数组
      [message, index],
      // 回调的入参也变成了数组,每个数组里面的顺序和数据源数组排序一致
      ([newMessage, newIndex], [oldMessage, oldIndex]) => {
        console.log('message 的变化', { newMessage, oldMessage })
        console.log('index 的变化', { newIndex, oldIndex })
      }
    )
  },
})
监听的选项
  • 说明:watch API的第三个参数options,options是一个对象的形式传入
选项类型默认值可选值作用
deepbooleanfalse(监听reactive对象或数组时,默认是true)truefalse是否进行深度监听
immediatebooleanfalseturefalse是否立即执行监听回调
flushstring'pre''pre''post''sync'控制监听回调的调用时机
onTrack(e)=>void数据源被追踪是调用
onTrigger(e)=>void监听回调被触发时调用
监听选项之deep(数据变化时触发watch回调)
  • 说明:若监听一个响应式的引用类型,其属性值有变化但其本身是不变的(所以不会触发watch的callback
import { defineComponent, ref, watch } from 'vue'

export default defineComponent({
  setup() {
    // 定义一个响应式数据,注意用的是 ref 来定义
    const nums = ref<number[]>([])

    // 2s后给这个数组添加项目
    setTimeout(() => {
      nums.value.push(1)

      // 可以打印一下,确保数据确实变化了
      console.log('修改后', nums.value)
    }, 2000)

    // 但是这个 watch 不会按预期执行
    watch(
      nums,
      // 这里的 callback 不会被触发
      () => {
        console.log('触发监听', nums.value)
      },
      // 因为关闭了 deep
      {
        deep: false,
      }
    )
  },
})
  • 注意点:reactive API定义的对象无法将deep成功设置为false

    • 解决方法:通过isReactive()判断,若为true则手动开启深度监听
    • // ...
      if (isReactive(source)) {
        getter = () => source
        deep = true // 被强制开启了
      }
      // ...
      
监听选项之immediate(数据初始化时,就触发watch回调)
  • 注意点:带有 immediate 选项时,不能在第一次回调时取消该数据源的监听
import { defineComponent, ref, watch } from 'vue'

export default defineComponent({
  setup() {
    // 这一次在这里可以会触发 watch 的回调了
    const message = ref<string>('')

    // 2s后改变数据
    setTimeout(() => {
      // 这一次,这里是第二次触发 watch 的回调,不再是第一次
      message.value = 'Hello World!'
    }, 2000)

    watch(
      message,
      () => {
        console.log('触发监听', message.value)
      },
      // 设置 immediate 选项
      {
        immediate: true,
      }
    )
  },
})
监听选项之flush
  • 特点:flush选项用于控制调用回调时机,接受指定字符串,默认是'pre'
  • 补充说明:

    • 'pre''post' ,回调使用队列进行缓冲。回调只被添加到队列中一次
    • 即使观察值变化了多次,值的中间变化将被跳过,不会传递给回调,这样做不仅可以提高性能,还有助于保证数据的一致性
可选值回调的调用时机使用场景
’pre'将在渲染前被调用允许回调在模板运行前更新
‘sync’在渲染时被同步调用目前没太大好处,不建议使用
'post'被推迟到渲染之后调用若通过ref操作DOM元素与子组件,需要使用这个值来启用该选项,以达到预期的执行效果
停止监听(有啥应用场景?——待整理)
监听效果清理(有啥应用场景?——待整理)

watchEffect

  • 特点:

    • watch可访问侦听状态变化前后的值,watchEffect没有
    • watchEffect会先默认执行一次,然后在属性改变时再执行
    • export default defineComponent({
        setup() {
          const foo = ref<string>('')
      
          setTimeout(() => {
            foo.value = 'Hello World!'
          }, 2000)
      
          function bar() {
            console.log(foo.value)
          }
           // 使用 watch 需要先手动执行一次
          bar()
          // 然后当 foo 有变动时,才会通过 watch 来执行 bar()
          watch(foo, bar)
      
          // 可以通过 watchEffect 实现 bar() + watch(foo, bar) 的效果
          watchEffect(bar)
        },
      })
      
  • watchEffect可用的监听选项

    • watchEffect,不支持deep和immediate,其他用法一样

watchPostEffect

watchEffect API 使用 flush: 'post' 选项时的别名,具体区别详见 监听选项之 flush 部分。

Vue v3.2.0 及以上版本才支持该 API 。

watchSyncEffect

watchEffect API 使用 flush: 'sync' 选项时的别名,具体区别详见 监听选项之 flush 部分。

Vue v3.2.0 及以上版本才支持该 API