一文讲清楚vue3中watch的使用

前言

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

侦听一个ref的数据值

这里先提一下,对于ref函数返回的响应式数据对象,它在模板里面使用时是不需要.value的

import { ref, watch } from 'https://cdn.bootcdn.net/ajax/libs/vue/3.3.4/vue.esm-browser.js'

let template = `
    
{{counter}}
`
export default{ setup: function () { let counter = ref(0) watch(counter, (newValue, oldValue) =>{ console.log(newValue, oldValue) }) return {counter} }, template, }

总结:单个数据值在watch里就直接传一个ref包装后的响应式对象就好啦

侦听多个ref的数据值

我们可以通过watch侦听多个数据源的变化。

如果在同一个函数里同时改变这些被侦听的来源,侦听器只会执行一次。

import { ref, watch } from 'https://cdn.bootcdn.net/ajax/libs/vue/3.3.4/vue.esm-browser.js'

let template = `
    
{{name}}{{age}}
`
export default { setup: function () { let name = ref('李四') let age = ref(22) let change = () => { name.value += '~' age.value++ } watch([name, age], ([newName, newAge], [preName, preAge]) => { console.log('姓名变化了', newName, preName) console.log('年龄变化了', newAge, preAge) }) return { name, age, change } }, template }

若要使侦听器执行多次,我们可以利用 nextTick ,等待侦听器在下一步改变之前运行。

import { nextTick } from 'https://cdn.bootcdn.net/ajax/libs/vue/3.3.4/vue.esm-browser.js'

let change = async () => {
    name.value += '~'
    await nextTick()
    age.value++
}

侦听一个ref的对象

无多层嵌套的对象

import { ref, watch } from 'https://cdn.bootcdn.net/ajax/libs/vue/3.3.4/vue.esm-browser.js'

let template = ` 
    
{{person.name}}
{{person.age}}
`
export default { setup: function () { let person = ref({ name: '张三', age: 18 }) // 这个侦听器无效,即控制台无输出 watch(person, (newVal, oldVal) =>{ console.log('侦听器1:',newVal, oldVal) }) // getter函数形式,新旧值不一样 watch(()=> ({...person.value}), (newVal, oldVal) =>{ console.log('侦听器2:',newVal, oldVal) }) return { person } }, template }

总结:无多层嵌套的对象不需要开启深度侦听,直接传一个ref响应式对象无效,需要传一个getter函数

多层嵌套的对象

import { ref, watch } from 'https://cdn.bootcdn.net/ajax/libs/vue/3.3.4/vue.esm-browser.js'

let template = `
    
{{person.name}}
{{person.age}}
{{person.scores}}
`
export default { setup: function () { let person = ref({ name: '张三', age: 18, scores: { math: 100, chinese: 90, english: 80 } }) // 这个watch无效,即控制台没任何输出 watch(person, (newValue, oldValue) => { console.log('第一个侦听器:person变化了', newValue, oldValue) }) // 开启深度侦听,新旧值一样 watch(person, (newValue, oldValue) => { console.log('第二个侦听器:person变化了', newValue, oldValue) }, { deep: true }) // 采用函数形式,这个watch无效,即控制台没任何输出 watch(() => ({ ...person.value }), (newValue, oldValue) => { console.log('第三个侦听器:person变化了', newValue, oldValue) }) // 采用函数形式,开深度侦听,新旧值一样 watch(() => ({ ...person.value }), (newValue, oldValue) => { console.log('第四个侦听器:person变化了', newValue, oldValue) }, { deep: true }) return { person } }, template }

总结:

  1. 没有开启深度侦听时,无论是直接传一个ref响应式对象,还是一个getter函数,watch皆无效;

  2. 开启深度侦听时,无论是直接传一个ref响应式对象,还是一个getter函数,新旧值一样

侦听一个ref的数组

import { ref, watch } from 'https://cdn.bootcdn.net/ajax/libs/vue/3.3.4/vue.esm-browser.js'

let template = `
    
{{arr1}}
`
export default { setup: function () { let arr1 = ref([1, 2, 3]) // 这个watch无效,即控制台没任何输出 watch(arr1, (newArr1, oldArr1) => { console.log(newArr1, oldArr1) }) // 采用函数形式,新旧值不一样 watch(() => [...arr1.value], (newArr1, oldArr1) => { console.log(newArr1, oldArr1) // (3) [8, 2, 3] (3) [1, 2, 3] }) let changeArr1 = () => { arr1.value[0] = 8 } return { arr1, changeArr1 } }, template }

总结:直接传一个ref响应式对象无效,需要传一个getter函数,新旧值不一样

侦听一个 reactive的响应式对象

无多层嵌套的对象

import { reactive, watch } from 'https://cdn.bootcdn.net/ajax/libs/vue/3.3.4/vue.esm-browser.js'

let template = `
    
{{person.name}}{{person.age}}
`
export default { setup: function(){ let person = reactive({ name: '张三', age: 18 }) // 拿不到旧值 watch(person, (newValue, oldValue) =>{ console.log('年龄变化了', newValue, oldValue) }) // 能拿到旧值 watch(()=>({...person}), (newValue, oldValue) =>{ console.log('年龄变化了', newValue, oldValue) }) return { person } }, template }

对于reactive的响应式对象,默认是开启深度侦听的,且通过配置深度侦听为false是无效的

reactive一般是用来侦听一个对象的,所以这里不介绍侦听单个值类型的情况

总结:直接传一个reactive响应式对象无法拿到旧值,需要传一个getter函数才能拿到旧值

多层嵌套的对象

import { reactive, watch } from 'https://cdn.bootcdn.net/ajax/libs/vue/3.3.4/vue.esm-browser.js'

let template = `
    
{{person.name}}
{{person.age}}
{{person.scores}}
`
export default { setup: function () { let person = reactive({ name: '张三', age: 18, scores: { math: 100, chinese: 90, english: 80 } }) // 新旧值一样 watch(person, (newValue, oldValue) => { console.log('第一个侦听器:person变化了', newValue, oldValue) }, { deep: true }) // 新旧值一样 watch(() => ({ ...person }), (newValue, oldValue) => { console.log('第二个侦听器:person变化了', newValue, oldValue) }, { deep: true }) return { person } }, template }

总结:直接传一个reactive响应式对象还是传一个getter函数都无法拿到旧值

侦听一个 reactive的响应式数组

import {  reactive, watch } from 'https://cdn.bootcdn.net/ajax/libs/vue/3.3.4/vue.esm-browser.js'

let template = `
    
{{arr2}}
`
export default { setup: function () { let arr2 = reactive([4, 5, 6]) // 是否配置deep为true都是无效的,新旧值一样 watch(arr2, (newArr2, oldArr2) =>{ console.log(newArr2, oldArr2) // Proxy(Array) {0: 9, 1: 5, 2: 6} Proxy(Array) {0: 9, 1: 5, 2: 6} }) // 采用函数形式,新旧值不一样 watch(()=>[...arr2], (newArr2, oldArr2) =>{ console.log(newArr2, oldArr2) // (3) [9, 5, 6] (3) [4, 5, 6] }) return { arr2 } }, template }

总结:直接传入一个reactvie响应式数组,新旧值一样;传入一个getter函数,新旧值不一样

配置对象

在Vue 3的 watch 函数中,配置对象(watch API 的第三个参数)包含以下选项:

  1. immediate(boolean):

    • 默认值:false
    • 如果设置为 true回调函数 将在初始渲染时立即执行一次,即使侦听的数据在初始时没有发生变化。
    watch(
     () => someData,
     (newValue, oldValue) => {
       // 
     },
     {
       immediate: true,
     }
    );
    
  2. deep(boolean):

    • 默认值:false,对于reactive默认是true
    • 如果设置为 true,则会递归地侦听对象内部的属性变化。注意,在Vue 3中,deep 不再支持深度侦听数组。
    watch(
     () => someData,
     (newValue, oldValue) => {
       // 回调函数
     },
     {
       deep: true,
     }
    );
    
  3. flush(string):

    • 默认值:'pre'
    • 指定在何时触发回调函数,有三个选项:
      • 'pre':在依赖变化之前触发。
      • 'post':在依赖变化之后触发。
      • 'sync':与依赖变化同步触发。

    默认情况下,用户创建的侦听器回调,都会在依赖数据变化之后, Vue 组件更新之前被调用。这意味着你在侦听器回调中访问的 DOM 将是被 Vue 更新之前的状态。通过设置flush为 ‘post’,拿到的就是更新后的DOM

    import { ref, watch } from 'https://cdn.bootcdn.net/ajax/libs/vue/3.3.4/vue.esm-browser.js'
    
    let template = `
       
    {{count}}
    `
    export default { setup: function () { let count = ref(0) let stopWatch = watch(count, () => { let div = document.querySelector('#count') console.log(div.innerHTML) }, { flush: 'post' }) let changeCount = () => { count.value++ } return { count, changeCount, stopWatch } }, template }

停止侦听

在Vue 3的Composition API中,你可以通过 watch 函数返回的停止函数来停止侦听。

import { ref, watch } from 'https://cdn.bootcdn.net/ajax/libs/vue/3.3.4/vue.esm-browser.js'

let template = `
    
{{count}}
`
export default { setup: function () { let count = ref(0) let stopWatch = watch(count, (newVal, oldVal) => { console.log(newVal, oldVal) }) return { count, stopWatch } }, template }

在这个例子中,watch 函数返回一个停止函数 stopWatch,然后我们可以在组件的其他地方调用它来停止侦听。在这个例子中,我们通过调用 stopWatch 方法来停止侦听。实际上,你可以在任何地方调用 stopWatch 函数,以停止对数据的侦听。

总结

watch的第一个参数的传递方式:

对于ref的响应式数据对象

  1. 单个数据值: 直接传一个ref包装后的响应式对象即可,新旧值不一样。例如:watch(counter, (newValue, oldValue) => {...})
  2. 多个数据值: 使用watch侦听多个数据源的变化,直接将多个ref响应式对象组成的数组传递给watch,新旧值不一样。例如:watch([name, age], ([newName, newAge], [preName, preAge]) => {...})
  3. 一个ref的对象(无多层嵌套): 无需开启深度侦听,直接传一个ref响应式对象无效,需要传一个getter函数,新旧值不一样。例如:watch(() => ({...person.value}), (newVal, oldVal) => {...})
  4. 一个ref的对象(多层嵌套): 如果有多层嵌套,需要开启深度侦听,可以直接传一个ref响应式对象或者使用getter函数,新旧值一样。例如:watch(person, (newVal, oldVal) => {...}, { deep: true })watch(() => ({...person.value}), (newVal, oldVal) => {...}, { deep: true })
  5. 一个ref的数组: 直接传一个ref响应式对象无效,需要传一个getter函数,新旧值不一样。例如:watch(() => [...arr1.value], (newArr1, oldArr1) => {...})

对于reactive 的响应式数据对象

  1. 一个reactive的对象(无多层嵌套):直接传一个reactive响应式对象,新旧值一样;传入一个getter函数,新旧值不一样。例如:watch(() => ({...person}), (newVal, oldVal) => {...})

  2. 一个reactive 的对象(多层嵌套):直接传一个reactive响应式对象还是传一个getter函数都无法拿到旧值。

  3. 一个reactive 的数组:直接传入一个reactvie响应式数组,新旧值一样;传入一个getter函数,新旧值不一样。watch(() => [...arr1], (newArr1, oldArr1) => {...})

一般来说,我们只是用ref包装的单个数据值,reactive包装的单层对象,reactive包装的多层对象,reactive包装的数组,所以我们只需要记相应的watch使用方法就好了

你可能感兴趣的:(Vue,vue.js,前端,javascript)