理解proxy
vue3是基于proxy代理实现的响应,那么什么事proxy代理,简单的说就是使用proxy可以实现对象的基本语义的代理,基本语义就是对象的基本属性的一些操作,比如读,写,修改,删除等;从而使用proxy对这些操作进行拦截,然而proxy只能对基本的语义进行操作,一些非基本操作,比如函数的调用,我们称之为复合操作,比如obj.fn(),是由两个基本语义组成的,首先读取get到obj.fn这个属性,第二就是函数调用;
理解Reflect
Reflect是一个全局对象,它里面有很多方法比如,get,set,apply等,在proxy拦截器中的方法在Reflect中都能找到,那这个函数的作用是什么?它的功能主要是提供访问对象属性的默认行为,比如以下两个行为是等价的:
obj={foo:1}
//访问属性
console.log(obj.foo) // 1
//Reflect访问
console.log(Reflect.get(obj,'foo')) // 1
既然是等价操作,那么它存在的意义是什么?
主要是Reflect.get可以接收第三个参数receiver,receiver这个参数和我们要实现的相应系统密切相关,receiver可以理解为函数调用中的this,例如上面代码如果填入receiver参数:
console.log(Reflect.get(obj,'foo',{foo:2})) // 输出是2,而不是1
如下代码实现对象的响应代理,对象的读,取,删除,for..in属性操作
const ITERATE_KEY = Symbol()
let activeEffect;
const bucket = new WeakMap()
const effectStack = []
function effect(fn,options={}){
const effectFn = ()=>{
cleanup(effectFn)
activeEffect = effectFn
effectStack.push(effectFn)
let res = fn()
effectStack.pop()
activeEffect = effectStack[effectStack.length-1]
return res
}
effectFn.deps=[]
effectFn.options = options
if(!effectFn.options.lazy){
effectFn()
}
return effectFn
}
function cleanup(effectFn){
for(let i=0;i<effectFn.deps.length;i++){
effectFn.deps[i].delete(effectFn)
}
effectFn.deps.length = 0;
}
function track(target,key){
if(!activeEffect) return
let depsMap = bucket.get(target)
if(!depsMap){bucket.set(target,depsMap=new Map())}
let deps = depsMap.get(key)
if(!deps){depsMap.set(key,deps=new Set())}
deps.add(activeEffect)
activeEffect.deps.push(deps)
}
function trigger(target,key,type){
let depsMap = bucket.get(target)
if(!depsMap) return
let effects = depsMap.get(key)
const effectsToRun = new Set()
effects && effects.forEach(effectFn=>{
if(activeEffect!==effectFn){
effectsToRun.add(effectFn)
}
})
if(type==='ADD' || type ==='DELETE'){
const iterateEffects = depsMap.get(ITERATE_KEY)
// 将与ITERATE_KEY相关联的副作用函数也添加到effectsToRun
iterateEffects && iterateEffects.forEach(effectFn=>{
if(activeEffect!==effectFn){
effectsToRun.add(effectFn)
}
})
}
effectsToRun.forEach(effectFn=>{
if(effectFn.options.scheduler){
effectFn.options.scheduler()
}else{
effectFn()
}
})
}
function reactive(obj){
return new Proxy(obj,{
// has(target,key){
// track(target,key)
// return Reflect.has(target,key)
// }
ownKeys(target){
track(target,ITERATE_KEY)
return Reflect.ownKeys(target)
},
get(target,key,receiver){
// 代理对象可以通过raw属性访问原始数据
if(key==='raw'){
return target
}
track(target,key)
return Reflect.get(target,key,receiver)
},
set(target,key,newVal,receiver){
// 先取旧值
const oldValue = target[key]
const type = Object.prototype.hasOwnProperty.call(target,key)?'SET':'ADD'
const res = Reflect.set(target,key,newVal,receiver)
if(target===receiver.raw){
if(oldValue!==newVal&&(oldValue===oldValue||newVal===newVal)){
trigger(target,key,type)
}
}
return res
},
deleteProperty(target,key){
const hadKey = Object.prototype.hasOwnProperty.call(target,key)
const res = Reflect.deleteProperty(target,key)
if(hadKey && res){
trigger(target,key,'DELETE')
}
return res
}
})
}
const obj = {}
const proto = {bar:1}
const child = reactive(obj)
const parent = reactive(proto)
// 使用parent作为child的原型
Object.setPrototypeOf(child,parent)
effect(()=>{
// console.log(child)
console.log(child.bar)
})
child.bar = 2