从定义数据角度对比:
从原理角度对比:
3. ref通过Object.defineProperty()的get和set来实现响应式(数据劫持)
4. reactive通过Proxy来实现响应式(数据劫持),并通过Reflect操作源对象内部的数据
从使用角度对比:
6. ref定义数据:操作数据需要 .value ,读取数据时模板中直接读取不需要 .value
7. reactive 定义的数据: 操作数据和读取数据均不需要 .value
1.监视reactive定义的响应式数据的时候:oldValue无法获取到正确的值,强制开启了深度监视(deep配置无效)
2.监视reactive定义的响应式数据中某个属性的时候:deep配置有效
具体请看下面代码以及注释
watch([sum,msg], (newValue,oldValue)=>{
console.log(‘新的值’,newValue); //[‘sum的newValue’, ‘msg的newValue’]
console.log(‘旧的值’,oldValue); //[‘sum的oldValue’, ‘msg的oldValue’]
},{immediate: true,deep:true})
immediate:初始化的时候执行一次; deep:深度监听,慎用,很浪费资源
<template>
<h1>当前求和为: {{sum}}</h1>
<button @click="sum++">点我+1</button>
<hr>
<h1>当前信息为: {{msg}}</h1>
<button @click="msg+='!' ">修改信息</button>
<hr>
<h2>姓名: {{person.name}}</h2>
<h2>年龄: {{person.age}}</h2>
<button @click="person.name += '~' ">修改姓名</button> <button @click="person.age++">增长年龄</button>
</template>
<script>
//使用setup的注意事项
import { watch,ref,reactive } from 'vue'
export default {
name: 'test5',
props: ['msg'],
emits:['hello'],
setup(){
let sum = ref(0)
let msg = ref('你好啊')
let person = reactive({
name: '张三',
age: 18,
job:{
salary: '15k'
},
})
//由于这里的this是指的是undefined,所以使用箭头函数
//情况一:监视ref所定义的一个响应式数据
// watch(sum, (newValue,oldValue)=>{
// console.log('新的值',newValue);
// console.log('旧的值',oldValue);
// })
//情况二:监视ref所定义的多个响应式数据
watch([sum,msg], (newValue,oldValue)=>{
console.log('新的值',newValue); //['sum的newValue', 'msg的newValue']
console.log('旧的值',oldValue); //['sum的oldValue', 'msg的oldValue']
},{immediate: true,deep:true}) //这里vue3的deep是有点小问题的,可以不用deep,(隐式强制deep)
//情况三:监视reactive定义的所有响应式数据,
//1.此处无法获取正确的oldValue(newValue与oldValue是一致值),且目前无法解决
//2.强制开启了深度监视(deep配置无效)
/**
* 受到码友热心评论解释: 此处附上码友的解释供大家参考:
* 1. 当你监听一个响应式对象的时候,这里的newVal和oldVal是一样的,因为他们是同一个对象【引用地址一样】,
* 即使里面的属性值会发生变化,但主体对象引用地址不变。这不是一个bug。要想不一样除非这里把对象都换了
*
* 2. 当你监听一个响应式对象的时候,vue3会隐式的创建一个深层监听,即对象里只要有变化就会被调用。
* 这也解释了你说的deep配置无效,这里是强制的。
*/
watch(person, (newValue,oldValue)=>{
console.log('新的值',newValue);
console.log('旧的值',oldValue);
})
//情况四:监视reactive对象中某一个属性的值,
//注意: 这里监视某一个属性的时候可以监听到oldValue
watch(()=>person.name, (newValue,oldValue)=>{
console.log('新的值',newValue);
console.log('旧的值',oldValue);
})
//情况五:监视reactive对象中某一些属性的值
watch([()=>person.name,()=>person.age], (newValue,oldValue)=>{
console.log('新的值',newValue);
console.log('旧的值',oldValue);
})
//特殊情况: 监视reactive响应式数据中深层次的对象,此时deep的配置奏效了
watch(()=>person.job, (newValue,oldValue)=>{
console.log('新的值',newValue);
console.log('旧的值',oldValue);
},{deep:true}) //此时deep有用
return {
sum,
msg,
person,
}
},
}
</script>
watchEffect( ()=>{这里面你用到了谁就监视谁,里面就发生回调} )
watchEffect特点
不需要手动传入依赖(不用指定监听对象)
无法获取原始值,只能获取更新后的值
立即执行(在onMounted前调用)
一些异步操作放里面更加的合适
const count = ref(0)
watchEffect(() =>{
console.log(count.value)
})
setTimeout(() => {
count.value++
}, 1000)
</script>
停止监听和watch一样的
const stop = watchEffect(() => {
/* ... */
})
// 停止监听
stop()
watchEffect两个参数(副作用回调函数,{flush:‘pre’,onTrack(){},onTrigger(){}})
flush取值:
pre (默认)
post (在组件更新后触发,这样你就可以访问更新的 DOM。这也将推迟副作用的初始运行,直到组件的首次渲染完成。)
sync (与watch一样使其为每个更改都强制触发侦听器,然而,这是低效的,应该很少需要)
副作用是指一个函数在返回值时还干了其它事(该函数称之为副作用函数)如:修改一个变量、设置一个对象的成员、终端打印日志、修改DOM、时间监听或订阅、等等
watchEffect可在第一个参数(函数)里传入一个参数onInvalidate 用来清除副作用
onInvalidate 执行时间:
副作用即将重新执行时(监听对象改变时)
侦听器被停止 (如果在 setup() 或生命周期钩子函数中使用了 watchEffect,则在组件卸载时)
let stop = watchEffect((onInvalidate) => {
console.log(count.value++,);
// onInvalidate(() => {
// stop()
// console.log('c');
// })
}, {
flush: 'pre',
onTrack(e) {
console.log('a', e);
},
onTrigger(e) {
console.log('b', e);
}
})