在js这门垃圾语言中没有指针,所以我们引入响应式。
指针在好多种场景下都有极好的效应。如果可以确保指针安全的使用,
那么可以在递归时不必考虑返回值来向上传递递归结果,而可以操作指针来从外部获取变量的值。
同理,在vue中,如何进行数据驱动显示是一门难题。
vue创造性地利用观察者模式实现了一种类似于指针的效应...
原理图
说明
obj=>一个组件所需要的所有数据。
a:1,b:2=>只是列举例子。
有一只眼睛在观察着是否有人往map里面添加数据,
它总是负责管理和维护这个列表->观察者模式。
一旦这个属性的值发生改变,它就会通知所有view实现自己的update方法去更新自己的状态。
但是这种订阅是结点去订阅一个数据的一个属性。针对同一个对象发起的订阅未免也太片面了。假如所我们有多个数据,又该如何管理呢?
这个时候可以向上层去考虑。
function effect(){
}
const targetMap = new WeekMap();
function track(target,key){
let depsMap = targetMap.get(target);
if(!depsMap){
targetMap.set(target,depsMap=new Map());
}
let dep = depsMap.get(key);
if(!dep){
depsMap.set(key,dep = new Set());
}
dep.add(effect);
}
function trigger(target,key){
let depsMap = targetMap.get(target);
if(!depsMap) return;
let dep = depsMap.get(key);
dep.forEach(effect=>effect());
}
因为es6引入了proxy和Reflect,所以vue3可以不用Object.defineProperty的方式去监听底层对象。我们之前的对象就可以使用proxy监听。
let obj = reactive({a:1,b:2});
function reactive(obj){
let handler = {
get(o,prop,receive){
return Reflect.get(o,prop,receive);
}
set(o,prop,value,receive){
Reflect.set(o,prop,value,receive);
}
};
return new Proxy(obj,handler);
}
这是基本的写法。 查看上图我们发现,我们想要在set时产生一个trigger,在get时产生一个track。 obj.a=1,先get,产生一个track。再set,产生一个trigger 。 console.log(obj.a)产生一个track。因为上文已经对其做过追踪,所以不会重复设置。
const targetMap = new WeakMap();
function track(target, key) {
let depsMap = targetMap.get(target);
if (!depsMap) targetMap.set(target, depsMap = new Map());
let dep = depsMap.get(key);
if (!dep) depsMap.set(key, dep = new Set());
dep.add(effect);
}
function trigger(target, key) {
let depsMap = targetMap.get(target);
if (!depsMap) return;
let dep = depsMap.get(key);
dep.forEach(effect => effect());
}
let res = 0;
function effect() {
res = obj.a + obj.b;
}
var obj = reactive({
a: 1,
b: 2
});
init();
function init() {
effect();
console.log(res);
obj.a = 11;
console.log(res);
obj.b = 22;
console.log(res);
}
function reactive(obj) {
let handler = {
get(o, prop, receive) {
track(o, prop);
return Reflect.get(o, prop, receive);
},
set(o, prop, value, receive) {
Reflect.set(o, prop, value, receive);
trigger(o, prop);
}
};
return new Proxy(obj, handler);
}
|