const state = reactive({
foo: 1,
bar: 2,
person: {
name: 'andy',
a: {
b: 1
}
}
})
- state是reactive定义的响应式对象,它是Proxy实例对象,Proxy可以对target对象上面的所有属性的增删改查等都能拦截
- state.person也是Proxy实例对象,isReactive(state.person)为true,那么它也能直接被watch
watch(state.person, () => {
console.log('state.person变化了');
})
watch(() => state.person, () => {
console.log('state.person变化了');
}, { deep: true })
// 如果是 state.person里面的属性改变了,那么watch(state.person, cb)这个cb会执行,因为reactive定义的响应式对象是强制开启deep:true
// watch( () => state.person, cb) 要加上配置项{ deep: true },cb才会执行
state.person.name = 'hehe'
state.person.a = {b: 666}
state.person.a.b = 999
// 如果把 state.person重新赋值,那么就需要watch(() => state.person, cb)这个cb才会执行
state.person = {name: 'ddd'}
state.person里面的属性改变了
- watch(state.person, cb)这个cb会执行,因为reactive定义的响应式对象是强制开启deep:true
- watch( () => state.person, cb) 要加上配置项{ deep: true },cb才会执行
state.person重新赋值
- watch(() => state.person, cb)这个cb才会执行
1. 当给state 的属性person重新赋值的时候,state.person从proxy对象变为普通对象
2. 直接watch(state.person, cb)是捕捉不到的,因为state.person已经不是reactive的了,这个watch已经失效
这和vue2中的watch不一样,因为看过vue2实现watch的源码,就是你监听state.person,即使重新赋值也是可以侦听到的,原理就是在Object.defineProperty的setter方法里面通知dep去update订阅者(watcher),而watcher就是去执行回调函数。
toRef toRefs
- toRef创建一个ref对象,value值存储的是指向源数据的引用地址,读取它的value值其实是访问源数据
const refPerson = toRef(state, 'person')
// const { person: refPerson } = toRefs(state) // 另外一种写法
console.log(refPerson); // ObjectRefImpl
console.log(refPerson.value === state.person); // true
// 等同于watch( () => state.person, cb)
watch(refPerson, () => {
console.log('state.person变化了');
}, {deep:true})
// 等同于watch(state.person, cb)
watch(refPerson.value, () => {
console.log('state.person变化了');
})
vue3 watch源码部分截图
- source即watch方法传入的第一个参,我们这儿分别是state.person 和 () => state.person
- watch Api中的watch有个变量getter,有个runner变量(用来生成副作用),代码会自动执行一下runner(),其实就是执行effect(),也就是执行getter(),执行过程势必会走到source里面的proxy的get里面,这样子这个runner副作用会被收集起来,如果deep=true,还会被traverse包装一下,对其所有子属性递归监听
-
设置deep为true后,state.person及其子属性变化后,就会通知到runner副作用,runner就会再次执行啦
1.png
网友评论