1. 监听普通类型

# 1. 监听普通类型

toRefs 转换响应式对象中所有属性为单独响应式数据

🔔 Vue3中控制台打印的时候,看到 Proxy 或者 ref 都表示响应式数据,改变数据会同步到视图。

🔔 ref 可以让基本类型数据也变成响应式

import { ref, watch } from 'vue'

setup() {
    const number = ref(0)
    
    // 监听 number 变量,如果有发生变化,自动运行后面的回调函数
    watch(number, (newVal, oldVal) => {
      console.log({ newVal, oldVal })
    })
    return { number }
  }

1
2
3
4
5
6
7
8
9
10
11
12
  • toRef是函数,转换响应式对象某个属性为单独响应式数据,并且值是关联的
  • toRefs是函数,转换响应式对象中所有属性为单独响应式数据,对象成为普通对象,并且值是关联的

# 2. 监听响应式对象

  • reactive是一个函数,它可以定义一个复杂数据类型,成为响应式数据。

💥reactive 不能让基本类型数据变成响应式

    // 定义响应式对象数据
    const obj = reactive({
      name: '小象',
      age: 18,
    })
   
// 修改名字的函数
    const btn = () => {
      obj.name = '大象'
      console.log('obj变了吗', obj)
    }

1
2
3
4
5
6
7
8
9
10
11
12

# 3. 监听多个参数

import { ref, watch } from 'vue'
setup() {
    const number = ref(0)
    const msg = ref('你好')
    
    // 监听 number 变量,如果有发生变化,自动运行后面的回调函数
    // 监听 [number, msg] 多个响应式数据数据,其中一个数据发生变化,就会触发 watch 回调函数
    watch([number, msg], (newVal, oldVal) => {
      console.log({ newVal, oldVal })
    })
    return { number, msg }
  }

1
2
3
4
5
6
7
8
9
10
11
12
13

# 4. 深度监听和立即执行

💥 { deep: true , immediate: true }

    // 定义响应式对象数据
    const obj = reactive({
      name: '小象',
      age: 18,
      cp: {
        name: '小花',
        age: 16,
      },
    })

    // 监听 reactive 定义的响应式数据
    //   特点1:newVal === oldVal,根本就是同一个内容地址,就是对象本身
    //   特点2:reactive() 对象默认是深度监听,能监听到所有的子属性的变化
    
    // 注意点:
    //   💥 如果监听的属性值是基本类型数据,需要写成函数返回该属性的方式才能监听到

    watch(
      () => obj.cp,
      val => {
        console.log('小象找到CP了要发红包', val)
      },
      { deep: true , immediate: true }
    )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

  • 与Vue2.x中watch配置功能一致

  • 两个小“坑”:

    • 监视reactive定义的响应式数据时:oldValue无法正确获取、强制开启了深度监视(deep配置失效)。
    • 监视reactive定义的响应式数据中某个属性时:deep配置有效。
//情况一:监视ref定义的响应式数据
watch(sum,(newValue,oldValue)=>{
 console.log('sum变化了',newValue,oldValue)
},{immediate:true})

//情况二:监视多个ref定义的响应式数据
watch([sum,msg],(newValue,oldValue)=>{
 console.log('sum或msg变化了',newValue,oldValue)
}) 

/* 情况三:监视reactive定义的响应式数据
 若watch监视的是reactive定义的响应式数据,则无法正确获得oldValue!!
 若watch监视的是reactive定义的响应式数据,则强制开启了深度监视 
*/
watch(person,(newValue,oldValue)=>{
 console.log('person变化了',newValue,oldValue)
},{immediate:true,deep:false}) //此处的deep配置不再奏效

//情况四:监视reactive定义的响应式数据中的某个属性
watch(()=>person.job,(newValue,oldValue)=>{
 console.log('person的job变化了',newValue,oldValue)
},{immediate:true,deep:true}) 

//情况五:监视reactive定义的响应式数据中的某些属性
watch([()=>person.job,()=>person.name],(newValue,oldValue)=>{
 console.log('person的job变化了',newValue,oldValue)
},{immediate:true,deep:true})

//特殊情况
watch(()=>person.job,(newValue,oldValue)=>{
    console.log('person的job变化了',newValue,oldValue)
},{deep:true}) //此处由于监视的是reactive素定义的对象中的某个属性,所以deep配置有效
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

# 5. watchEffect函数

  • watch的套路是:既要指明监视的属性,也要指明监视的回调。

  • watchEffect的套路是:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性。

  • watchEffect有点像computed:

    • 但computed注重的计算出来的值(回调函数的返回值),所以必须要写返回值。
    • 而watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值。
    //watchEffect所指定的回调中用到的数据只要发生变化,则直接重新执行回调。
    watchEffect(()=>{
        const x1 = sum.value
        const x2 = person.age
        console.log('watchEffect配置的回调执行了')
    })
    
    1
    2
    3
    4
    5
    6

# 6.watchEffect进阶使用

# 1.停止监听

watchEffect 在组件的 setup() (opens new window) 函数或生命周期钩子 (opens new window)被调用时,侦听器会被链接到该组件的生命周期,并在组件卸载时自动停止。

在一些情况下,也可以显式调用返回值以停止侦听:

const stop = watchEffect(() => {
  /* ... */
})

// later
stop()
1
2
3
4
5
6

# 2.清除副作用(onInvalidate)

有时副作用函数会执行一些异步的副作用,这些响应需要在其失效时清除 (即完成之前状态已改变了) 。所以侦听副作用传入的函数可以接收一个 onInvalidate 函数作入参,用来注册清理失效时的回调。当以下情况发生时,这个失效回调会被触发:

  • 副作用即将重新执行时
  • 侦听器被停止 (如果在 setup() 或生命周期钩子函数中使用了 watchEffect,则在组件卸载时)
watchEffect(onInvalidate => {
  const token = performAsyncOperation(id.value)
  onInvalidate(() => {
    // id has changed or watcher is stopped.
    // invalidate previously pending async operation
    token.cancel()
  })
})

1
2
3
4
5
6
7
8
9

我们之所以是通过传入一个函数去注册失效回调,而不是从回调返回它,是因为返回值对于异步错误处理很重要。

在执行数据请求时,副作用函数往往是一个异步函数

const data = ref(null)
watchEffect(async onInvalidate => {
  onInvalidate(() => {
    /* ... */
  }) // 我们在Promise解析之前注册清除函数
  data.value = await fetchData(props.id)
})

1
2
3
4
5
6
7
8

我们知道异步函数都会隐式地返回一个 Promise,但是清理函数必须要在 Promise 被 resolve 之前被注册。另外,Vue 依赖这个返回的 Promise 来自动处理 Promise 链上的潜在错误。

# 3.watch和watchEffect的区别

1.watch可以访问新值和旧值,watchEffect不能访问。

2.watchEffect有副作用,DOM挂载或者更新之前就会触发,需要我们自己去清除副作用。

3.watch是惰性执行,也就是只有监听的值发生变化的时候才会执行,但是watchEffect不同,每次代码加载watchEffect都会执行。

4.watch需要指明监听的对象,也需要指明监听的回调。watchEffect不用指明监视哪一个属性,监视的回调函数中用到哪个属性,就监视哪个属性。

最后更新于: 2022/3/25 07:58:56
강남역 4번 출구
Plastic / Fallin` Dild