自从专注于React之后,已经有段时间没有用过Vue了,因此最近在学习Vue3.0,Vue的更新机制与React的更新机制是不同的,React的更新机制说的简单点就是根据场景调用不同方法触发状态更新,从而再次进行渲染,而对于Vue的话,我认为是在发生的数据更新时,根据情况进行渲染的。说的专业点就是Vue拥有一个响应式系统,可以让它在数据更新的时候自动进行视图的更新。在Vue3.0中,可以使用reactive声明响应式状态 ,官方例子:
import { reactive } from 'vue'
// 响应式状态
const state = reactive({
count: 0
})
也可以创建独立的响应式值:
import { reactive } from 'vue'
// 响应式状态
import { ref } from 'vue'
const count = ref(0)
那么本文重点来了,在学习到reactive方法的时候,看到文档说不要解构reactive创建的响应式对象,为什么?,因为会失去响应式的功能,那为什么会失去,因为…(并没有解释),而且在观看一个尤大讲解Vue3的视频中,尤大也说了不要解构reactive创建的响应式对象,为什么?,因为会失去响应式的功能,那为什么会失去,因为…(并没有解释)。这个问题隔了几天直到我调试Vue3.0的reactivity之后,我才有了一丝想法,可以简单的看看在调用reactivity方法之后发生了什么:
①先有个例子:
<div id="app">
<p @click="change">
{{refCount}}
{{count}}
</p>
</div>
//js代码
const { reactive, toRefs } = Vue;
const vm = Vue.createApp({
setup(props) {
//创建响应式对象
const rObj = reactive({
count: 0
});
//创建点击事件
const change = () => {
console.log(rObj.count)
rObj.count++;
}
//利用ref保留与源对象的响应式关联
const { count:refCount } = toRefs(rObj);
const { count } = rObj;
return {
//具有响应式
refCount,
//响应式丢失
count,
change
}
}
}).mount('#app');
可以看出当点击元素的时候,refCount是随着进行变化的,而count是解构出来的,而它并没有随着变化的。那么调试进入reactive方法:
可以看到它在内部调用了createReactiveObject方法,这名字很好理解就是创建响应式对象,调试进去:
它在这里做了一些处理,细节暂时不探究,重点在这:
创建了一个代理对象将其保存在内部并返回,学习Vue3或多或少都听过Vue3如何进行数据视图更新的,Proxy对数据进行了拦截,当数据发生变化,通知Vue进行视图变化,在new Proxy的第二个参数,它通常使用的baseHanderls:
const baseHandlers = {
get(target, key, receiver) {},
set(target, key, value, receiver) {},
deleteProxy: (target, key) {},
has: (target, key) {},
ownKey: (target) {}
};
绕了这么久可以看出reactive方法其实就是创建了一个Proxy对象,以及进行了一系列处理,其中并没有找到它可能会失去响应式的情况,也就是说它失去响应式不在于Vue而是在于Proxy对象本身,那么可以简化一下:
const obj = {
count: 1
};
const proxy = new Proxy(obj, {
get(target, key, receiver) {
console.log("这里是get");
return Reflect.get(target, key, receiver);
},
set(target, key, value, receiver) {
console.log("这里是set");
return Reflect.set(target, key, value, receiver);
}
});
打印出来的效果:
这时想一下如何会因为解构而失去响应式(在这里就当作是打印文字),先试试直接赋值会打印啥:
proxy.count = 2;
是因为解构相当于重新赋值给另一个变量的原因吗,也就是说它变成了一个新值:
let {count} = proxy;
count = 2;
这里会打印什么,因为失去响应,所以它只打印出来:
好像是这样的感觉,那试试对象:
const obj = {
a: {
count: 1
}
};
function reactive(obj) {
return new Proxy(obj, {
get(target, key, receiver) {
console.log("这里是get");
if (typeof target[key] === "object") {
return reactive(target[key]);
};
return Reflect.get(target, key, receiver);
},
set(target, key, value, receiver) {
console.log("这里是set");
return Reflect.set(target, key, value, receiver);
}
});
};
const proxy = reactive(obj);
直接赋值:
proxy.a.count = 2;
console.log(proxy);
先只解构一次看看
let { a } = proxy;
a.count = 3;
console.log(proxy);
解构到底
let { a } = proxy;
let { count } = a;
count = 3;
console.log(proxy);
为什么会不同,其实很好理解,只解构一次,其实新的对象的count仍然是被代理的,
而解构两次,直接获取了count,相当于绕过了代理a。
那么就很好理解了,解构为什么会失去响应,用这个图就可以解释:
因为解构赋值相当于直接跳过了代理那一层,在下面直接获取值,所以get和set无法被调用。