设计一个完善的响应系统
问题一:无法获取当前副作用函数
直接使用名字( effect )来获取副作用函数,这种硬编码方式很不灵活
js
const obj = new Proxy(data, {
get(target, key) {
bucket.add(effect)
return target[key]
},
set(target, key, newVal) {
target[key] = newVal
bucket.forEach(fn => fn())
}
})
我们需要提供一个用来注册当前副作用函数的机制:
js
let activeEffect
function effect(fn) {
activeEffect = fn
fn()
}
effect(() => {
document.body.innerText = obj.text
})
js
const obj = new Proxy(data, {
get(target, key) {
if (activeEffect) {
bucket.add(activeEffect)
}
return target[key]
},
set(target, key, newVal) {
target[key] = newVal
bucket.forEach(fn => fn())
}
})
问题二:副作用函数重复执行
当我们设置一个没被副作用函数使用的属性时,理论上不需要执行副作用函数,实际上从”桶“中取出所有副作用函数并执行
js
setTimeout(() => {
obj.notExist = 'hello vue3'
}, 1000)
想解决这个问题需要我们重新设计“桶”的数据结构
target
└── key
└── effectFn1
└── effectFn2
js
const bucket = new WeakMap()
const data = {text: 'hello world'}
const obj = new Proxy(data, {
get(target, key) {
track(target, key)
return target[key]
},
set(target, key, newVal) {
target[key] = newVal
trigger(target, key)
}
})
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)
}
function trigger(target, key) {
const depsMap = bucket.get(target)
if (!depsMap) return
const effects = depsMap.get(key)
effects && effects.forEach(fn => fn())
}