实现VUE3中的简易式响应系统
<script>
const targetMap = new WeakMap()
const proxyMap = new WeakMap()
let activeEffect = null
function track(target, key) {
if (activeEffect) {
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(activeEffect)
}
}
function trigger(target, key) {
const depsMap = targetMap.get(target)
if (!depsMap) return
const dep = depsMap.get(key)
if (dep) {
dep.forEach(effect => effect())
}
}
function reactive(target) {
if (proxyMap.has(target)) return proxyMap.get(target)
const proxy = new Proxy(target, {
get(target, key, receiver) {
track(target, key)
return Reflect.get(target, key, receiver)
},
set(target, key, value, receiver) {
let oldValue = target[key]
const result = Reflect.set(target, key, value, receiver)
if (oldValue !== value) {
trigger(target, key)
}
return result
}
})
proxyMap.set(target, proxy)
return proxy
}
function effect(fn) {
const effectFn = () => {
activeEffect = effectFn
fn()
activeEffect = null
}
effectFn()
}
const state = reactive({ cnt: 1, name: 'nothing' })
effect(() => { console.log(`cnt: ${state.cnt}`) })
effect(() => { console.log(`name: ${state.name}`) })
state.cnt++
state.name = 'anything'
</script>
这里的targetMap的结构大致如下
WeakMap {
{ cnt: 2, name: 'anything' } => Map {
"cnt" => Set { effect1 },
"name" => Set { effect2 }
}
}
红框代表depsMap其中一个item,绿框代表depsMap的value,也就是Set中的一个item
track函数用来追踪依赖,当某个属性被读取的时候,将当前关联的副作用函数加入该属性的依赖列表。 例如上面的state.cnt,首次执行effect函数的时候(line58)执行一次get,然后会执行track函数,此时targetMap内的数据如下
WeakMap {
{ cnt: 1 } => Map {
"cnt" => Set { effect1 }
}
}
当执行state.cnt++的时候,此时触发set拦截,调用trigger函数,然后执行effectFn,此时再次访问state.cnt,触发get函数,调用track函数。
问题1:为什么在Proxy里面要调用Reflect
而不直接return target[key] 或 target[key] = value
答:确保方法调用时,this仍然指向原对象。同时在set方法中Reflect可以返回bool值。