侦听器是干嘛用的?
理论知识讲:当被侦听的信息发生变化时,触发侦听器的回调函数,从而在侦听器的回调函数中执行一些后续的操作。
典型的场景:
当路由参数发生变化后,重新调用接口获取组件的数据
这个操作计算属性能做到吗?显然不能,因为计算属性只能处理同步操作,而调用接口获取组件数据的操作时异步操作,这时,侦听器的作用就出现了。
由此可见侦听器是会经常用到的,一定要熟练掌握它!
<template>
<div>侦听器练习</div>
<hr>
<div>{{count}}</div>
<button @click="count++">点击</button>
</template>
import { ref, watch } from 'vue'
export default {
name: 'App',
setup () {
const count = ref(0)
// 基于侦听器监听数据变化
// 这里有个watch的基本规则:被侦听的数据必须是响应式的
watch(count, (newValue, oldValue) => {
// newValue表示修改后的值
// oldValue表示修改前的值
console.log(newValue, oldValue)
})
return { count }
}
}
以上只是侦听了基本数据类型
当侦听对象类型的数据
<template>
<div>{{obj.msg}}</div>
<button @click="handleClick">侦听对象</button>
</template>
import { watch, reactive } from 'vue'
export default {
name: 'App',
setup () {
// 创建一个响应式对象obj
const obj = reactive({
msg: 'tom'
})
// 点击修改msg为'jerry
const handleClick = () => {
obj.msg = 'jerry'
}
// 侦听obj变化
watch(obj, (nVal, oVal) => {
// 最终发现新值和旧值竟然相同
console.log(nVal, oVal)
})
return { count, obj, handleClick }
}
}
原因:如果侦听对象,那么侦听器的回调函数的两个参数时一样的结果,表示最新的对象数据,此时,也可以直接读取被侦听的对象,那么得到的值也是最新的值。
所以就没必要使用形参了,直接通过对象点属性的方法去侦听即可
// 侦听obj变化
watch(obj, () => {
console.log(obj.msg)
})
侦听器的第三种场景,侦听多个值时:
<template>
<div>{{n1}}</div>
<div>{{n2}}</div>
<button @click="handleClick">侦听对象</button>
</template>
import { ref, watch} from 'vue'
export default {
name: 'App',
setup () {
// 侦听多个值
const n1 = ref(1)
const n2 = ref(2)
const handleClick = () => {
n1.value = 3
n2.value = 4
}
// 侦听多个值用数组包起来的
watch([n1, n2], (v1,v2) => {
// 侦听后得到的是新的数组值
console.log(v1,v2)
// 形参v1代表最新值
// 形参v2代表原有值
})
return { handleClick, n1, n2 }
}
}
当需要侦听多个值的时候,这就会有它的价值,侦听器的第一个参数是可以接收数组的,侦听到的结果也是数组,数据顺序一致。
侦听对象的单个属性
<template>
<div>侦听器练习</div>
<hr>
<div>{{stuInfo.uname + '===' + stuInfo.age}}</div>
<button @click="handleClick">点击</button>
</template>
import { reactive, watch } from 'vue'
export default {
name: 'App',
setup () {
const stuInfo = reactive({
uname: 'lisi',
age: 12
})
const handleClick = () => {
stuInfo.age = 13
}
// 如果被侦听的是表达式,需要用函数方式
// 侦听过多数据会影响性能,所以如果真是需要侦听对象中的一个数据,那么就按照下面这样写就可以了
watch(() => stuInfo.age, (v1, v2) => {
// 这里打印出来后发现新值和旧值都能拿得到
console.log(v1, v2)
})
return { stuInfo, handleClick }
}
}
总结:如果侦听对象中的单个属性,需要使用函数的方式,侦听更少的数据有利于提高性能。
watch方法的配置选项(watch的第二个参数)
<template>
<div>侦听器练习</div>
<hr>
<div>{{stuInfo.uname + '===' + stuInfo.age}}</div>
<div>{{stuInfo.friend.uname}}</div>
<button @click="handleClick">点击</button>
</template>
import { reactive, watch } from 'vue'
export default {
name: 'App',
setup () {
const stuInfo = reactive({
uname: 'lisi',
age: 12,
friend: {
uname: 'zhangsan'
}
})
const handleClick = () => {
stuInfo.age = 13
stuInfo.friend.uname = 'wangwu'
}
// 如果被侦听的是表达式,需要用函数方式
// 侦听过多数据会影响性能,所以如果真是需要侦听对象中的一个数据,那么就按照下面这样写就可以了
watch(() => stuInfo.age, (v1, v2) => {
// 这里打印出来后发现新值和旧值都能拿得到
console.log(v1, v2)
})
watch(() => stuInfo.friend, () => {
// 侦听器的回调不是一进来就会被调用的,如果需要一上来就被调用
console.log('stuInfo')
// 需要在回调函数后面加上{immediate: true}
// 这时去修改friend中的值,看看侦听器会不会打印
// 点击后发现,名字换了,但是没有侦听到
// 这时就需要加上另一个选项:deep:true 代表深度侦听
}, {
// 立即调用侦听器回调函数
// 组件渲染后,立即触发一次
immediate: true,
// 被侦听的内容需要是函数的写法 ()=> stuInfo.friend
deep: true
})
return { stuInfo, handleClick }
}
}
这里有个重点:当使用深度侦听时,要确保 被侦听的内容是函数的写法:
() => stuInfo.friend
总结一哈:
1.immediate:true,表示组件渲染时立即调用
2. deep:true,表示深度侦听对象里面的子属性(被侦听的内容需要是函数的写法)