watch
的第一个参数可以是不同形式的“数据源”:它可以是一个 ref (包括计算属性)、一个响应式对象、一个 getter 函数、或多个数据源组成的数组
这里先提一下,对于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包装后的响应式对象
就好啦
我们可以通过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++
}
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
}
总结:
没有开启深度侦听时,无论是直接传一个ref响应式对象,还是一个getter函数,watch皆无效;
开启深度侦听时,无论是直接传一个ref响应式对象,还是一个getter函数,新旧值一样
。
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函数
,新旧值不一样
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函数都无法拿到旧值
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 的第三个参数)包含以下选项:
immediate
(boolean):
false
true
,回调函数
将在初始渲染时立即执行一次,即使侦听的数据在初始时没有发生变化。watch(
() => someData,
(newValue, oldValue) => {
//
},
{
immediate: true,
}
);
deep
(boolean):
false
,对于reactive默认是truetrue
,则会递归地侦听对象内部的属性变化。注意,在Vue 3中,deep
不再支持深度侦听数组。watch(
() => someData,
(newValue, oldValue) => {
// 回调函数
},
{
deep: true,
}
);
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的响应式数据对象
ref
包装后的响应式对象即可,新旧值不一样。例如:watch(counter, (newValue, oldValue) => {...})
。watch
侦听多个数据源的变化,直接将多个ref
响应式对象组成的数组传递给watch
,新旧值不一样。例如:watch([name, age], ([newName, newAge], [preName, preAge]) => {...})
。ref
响应式对象无效,需要传一个getter函数
,新旧值不一样。例如:watch(() => ({...person.value}), (newVal, oldVal) => {...})
。ref
响应式对象或者使用getter函数,新旧值一样。例如:watch(person, (newVal, oldVal) => {...}, { deep: true })
或 watch(() => ({...person.value}), (newVal, oldVal) => {...}, { deep: true })
。ref
响应式对象无效,需要传一个getter函数,新旧值不一样。例如:watch(() => [...arr1.value], (newArr1, oldArr1) => {...})
。对于reactive 的响应式数据对象
一个reactive的对象(无多层嵌套):直接传一个reactive响应式对象,新旧值一样;传入一个getter函数,新旧值不一样。例如:watch(() => ({...person}), (newVal, oldVal) => {...})
。
一个reactive 的对象(多层嵌套):直接传一个reactive响应式对象还是传一个getter函数都无法拿到旧值。
一个reactive 的数组:直接传入一个reactvie响应式数组,新旧值一样;传入一个getter函数,新旧值不一样。watch(() => [...arr1], (newArr1, oldArr1) => {...})
。
一般来说,我们只是用ref包装的单个数据值,reactive包装的单层对象,reactive包装的多层对象,reactive包装的数组,所以我们只需要记相应的watch使用方法就好了