你真的懂在「Vue3」中使用watch监听props中的数据吗?

925 阅读4分钟

document.png

每日一句

不积跬步,无以至千里;不积小流,无以成江海 ——劝学

引言

这篇文章主要介绍在**「Vue3」中使用「watch」**监听props数据的几个注意点。同时本系列采用的均为setup语法糖

watch()粗略介绍

  • 定义:侦听一个或多个响应式数据源,并在数据源变化时调用所给的回调函数。
  • 类型
// 侦听单个来源
function watch<T>(
  sourceWatchSource<T>,
  callbackWatchCallback<T>,
  options?: WatchOptions
): StopHandle

// 侦听多个来源
function watch<T>(
  sourcesWatchSource<T>[],
  callbackWatchCallback<T[]>,
  options?: WatchOptions
): StopHandle

type WatchCallback<T> = (
  value: T,
  oldValue: T,
  onCleanup: (cleanupFn: () => void) => void
) => void

type WatchSource<T> =
  | Ref<T> // ref
  | (() => T) // getter
  | T extends object
  ? T
  : never // 响应式对象

interface WatchOptions extends WatchEffectOptions {
  immediate?: boolean // 默认:false
  deep?: boolean // 默认:false
  flush?: 'pre' | 'post' | 'sync' // 默认:'pre'
  onTrack?: (event: DebuggerEvent) => void
  onTrigger?: (event: DebuggerEvent) => void
  once?: boolean // 默认:false (3.4+)
}

🥏左右滑动滚动条查看详细内容

详解

watch() 默认是懒侦听的,即仅在侦听源发生变化时才执行回调函数。

  • 第一个参数是侦听器的源。这个来源可以是以下几种:一个函数,返回一个值,一个 ref,一个响应式对象,...或是由以上类型的值组成的数组
    
  • 第二个参数是在发生变化时要调用的回调函数这个回调函数接受三个参数:新值、旧值,以及一个用于注册副作用清理的回调函数。当侦听多个来源时,回调函数接受两个数组,分别对应来源数组中的新值和旧值。
    
  • 第三个可选的参数是一个对象,支持以下这些选项:immediate,deep,flush,onTrack / onTrigger,once等。
    

更详细介绍见底部官网

监听props中的数据

监听 props 中基本数据类型

  • 父组件中对传入数据的处理
const text = ref("测试内容")
const handleAdd = () => {
  text.value += 'Name'
}
  • 子组件中监听传入的数据

    • 正确用法
watch(
  () => props.text,
  (newVal, oldVal) => {
    console.log('监听基本类型数据text')
    console.log('new value', newVal)
    console.log('old value', oldVal)
  }
)
  • 错误用法

watch(
 props.text,
() => {
  console.log('监听基本类型数据text')
}
)

解释:因为此时要使用getter函数返回值的形式才能触发监听

监听props中的引用数据类型

父组件不改变地址指向情况

  • 父组件中对传入数据的处理
const text = ref("测试内容")
const handleAdd = () => {
  let name = '哈哈'
  let age = 20
  arrayList.value.push({
    name: (name += '~'),
    age: (age += 1)
  })
  text.value += 'Name'
}
  • 子组件中监听传入的数据

    • 正确用法
watch(
  props.dataList(newVal, oldVal) => {
  console.log('监听引用类型数据dataList')
  console.log('new', newVal)
  console.log('old', oldVal)
})

解释:当父组件传入的是引用类型数据,且在父组件中没有改变该数据的引用地址时,在子组件中可以直接监听传入的数据

父组件改变地址指向情况

  • 父组件中对传入数据的处理
const text = ref("测试内容")
const handleAdd = () => {
  let name = '哈哈'
  let age = 20
  arrayList.value=[{name:name+"zwt",age"20}]
}
  • 子组件中监听传入的数据

    • 正确用法
watch(
  () => props.dataList,
  (newVal, oldVal) => {
    console.log('此时监听对象为引用类型数据的dataList')
    console.log('new', newVal)
    console.log('old', oldVal)
  }
)

实际开发场景

  • 瀑布流展示

使用情景:父组件从接口获取第一页数据,将数据存在dataList中:dataList.value = res.data

注意:此时,已经改变引用类型数据 dataList 的地址指向

  • 子组件通过watch监听传入的 dataList,并且调用 handleData() 方法处理 props.dataList 的数据结构:

    • 正确用法
watch(
  () => props.dataList,
  () => {
    console.log('拿到父组件传来的第一页数据,准备处理==,dataList')
    handleData()
    ... // 相应逻辑处理
  }
)

这种情况可以成功触发监听的

  • 用户继续下拉刷新操作,继续发起请求(也就是接下来对dataList执行push操作,也就是不会改变地址引用)
for(let item of res.data){
 dataList.value.push(item)
}

注意:此时,虽然父组件传入的 dataList 的值修改了,但是子组件已经不能触发watch及其处理逻辑了

  • 正确处理办法:

    1. 使用 computed
  const dataListTest = computed(() => {
  handleData()
  return props.dataList
})
  1. 使用 watchEffect
  watchEffect(() => {
  handleData()
})

内容总结

  1. watch监听 props 中的基本类型数据,需要通过 getter 函数返回值的形式(()=>props.xxx)才能监听
  2. watch监听 props 中的引用类型数据,且父组件中没有改变地址指向时,可以直接监听
  3. watch监听 props 中的引用类型数据,且父组件中改变了地址指向时,需要通过 getter 函数返回值的形式(()=>props.xxx)才能监听

Vue.js对组合式API中watch()的详细介绍

关注**「秋风念北寒」**公众号 更多Vue.js相关文档请参考 Vue.js 官网