Vue侦听器 | watch选项

👀watch基本用法

  • 在 Vue.js 中,watch 是一个选项,用于侦听 Vue 实例中数据的变化并作出相应的响应。

    • watch 选项可以接收两个参数:要观察的数据属性的名称和一个回调函数
    • 当被观察的数据发生变化时,Vue.js 会自动调用该回调函数,并传入两个参数:新值和旧值
    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
    <script setup>
    import { ref, watch } from 'vue'

    const question = ref('')
    const answer = ref('Questions usually contain a question mark. ;-)')
    const loading = ref(false)

    // 可以直接侦听一个 ref
    watch(question, async (newQuestion, oldQuestion) => {
    if (newQuestion.includes('?')) {
    loading.value = true
    answer.value = 'Thinking...'
    try {
    const res = await fetch('https://yesno.wtf/api')
    answer.value = (await res.json()).answer
    } catch (error) {
    answer.value = 'Error! Could not reach the API. ' + error
    } finally {
    loading.value = false
    }
    }
    })
    </script>

    <template>
    <p>
    Ask a yes/no question:
    <input v-model="question" :disabled="loading" />
    </p>
    <p>{{ answer }}</p>
    </template>

👀监听数据源类型

  • watch 的第一个参数可以是不同形式的“数据源”:它可以是一个 ref (包括计算属性)、一个响应式对象、一个 getter 函数、或多个数据源组成的数组:

    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
    const x = ref(0)
    const y = ref(0)

    // 单个 ref
    // 在 x 的值发生变化时执行回调函数,并传入新的值 newX

    watch(x, (newX) => {
    console.log(`x is ${newX}`)
    })


    // getter 函数
    // 在 x 或 y 的值发生变化时执行回调函数,并计算 x.value + y.value 的结果作为侦听的值。当侦听的值发生变化时,执行回调函数,并传入新的值 sum

    watch(
    () => x.value + y.value,
    (sum) => {
    console.log(`sum of x + y is: ${sum}`)
    }
    )


    // 多个来源组成的数组
    // 同时监听 x 和 y 的值的变化,并在任一值发生变化时执行回调函数。回调函数的参数是一个数组,包含了 x 和 y 的新值 [newX, newY]

    watch([x, () => y.value], ([newX, newY]) => {
    console.log(`x is ${newX} and y is ${newY}`)
    })

👀watch options

immediate

  • 在使用watch时, 使用immediate: true选项, 这样它就会在组件创建时立即执行.
  • 举例来说,我们想请求一些初始数据,然后在相关状态更改时重新请求数据。
1
2
3
4
5
6
7
watch(
source,
(newValue, oldValue) => {
// 立即执行,且当 `source` 改变时再次执行
},
{ immediate: true }
)

 

deep

  • deep即深入观察, 监听器会层层遍历, 给对象的所有属性(及子属性)添加监听器.

  • 这样做无疑会有很大的性能开销, 修改obj任何一个属性都会触发监听器中的处理函数.

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

1
2
3
4
5
6
7
8
9
const obj = reactive({ count: 0 })

watch(obj, (newValue, oldValue) => {
// 在嵌套的属性变更时触发
// 注意:`newValue` 此处和 `oldValue` 是相等的
// 因为它们是同一个对象!
})

obj.count++

一个返回响应式对象的 getter 函数,只有在返回不同的对象时,才会触发回调:

1
2
3
4
5
6
watch(
() => state.someObject,
() => {
// 仅当 state.someObject 被替换时触发
}
)
1
2
3
4
5
6
7
8
watch(
() => state.someObject,
(newValue, oldValue) => {
// 注意:`newValue` 此处和 `oldValue` 是相等的
// *除非* state.someObject 被整个替换了
},
{ deep: true }
)

 

总之,如果第一个参数直接传入一个对象或者手动设置deep深层监听,该对象任何一个属性改变都会触发回调函数;否则若使用的getter函数,只有当整个对象被改变时才会触发回调函数


👀watchEffect()

1
2
3
4
5
6
7
8
9
10
11
12
13
const todoId = ref(1)
const data = ref(null)

watch(
todoId,
async () => {
const response = await fetch(
`https://jsonplaceholder.typicode.com/todos/${todoId.value}`
)
data.value = await response.json()
},
{ immediate: true }
)

todoId同时在源和回调函数中使用,可以替换为:

1
2
3
4
5
6
watchEffect(async () => {
const response = await fetch(
`https://jsonplaceholder.typicode.com/todos/${todoId.value}`
)
data.value = await response.json()
})

 

对于有多个依赖项的侦听器来说,使用 watchEffect() 可以消除手动维护依赖列表的负担。此外,如果你需要侦听一个嵌套数据结构中的几个属性,watchEffect() 可能会比深度侦听器更有效,因为它将只跟踪回调中被使用到的属性,而不是递归地跟踪所有的属性