HTML部分
<section style="display: flex;flex-direction: column;max-width: 200px;">
<label for="r">r
<input type="range" id="r" min="0" max="255">
</label>
<label for="g">g
<input type="range" id="g" min="0" max="255">
</label>
<label for="b">b
<input type="range" id="b" min="0" max="255">
</label>
<div id="color" style="width: 100px;height: 100px;background-color: rgb(0, 0, 0);margin-top: 24px;"></div>
</section>
JavaScript部分
let obj = {
r: 0,
g: 0,
b: 0
}
let usedReactivities = [],
handlers = new Map(),
reactivities = new Map();
function reactive(obj) {
if (reactivities.has(obj)) return reactivities.get(obj)
let proxy = new Proxy(obj, {
get(obj, prop) {
usedReactivities.push([obj, prop])
if (typeof obj[prop] === "object")
return reactive(obj[prop])
return obj[prop]
},
set(obj, prop, val) {
obj[prop] = val
if (handlers.get(obj))
if (handlers.get(obj).get(prop))
for (const handler of handlers.get(obj).get(prop))
handler()
return obj[prop]
}
})
reactivities.set(obj, proxy)
return proxy
}
function effect(handler) {
usedReactivities = []
handler()
for (let [obj, prop] of usedReactivities) {
if (!handlers.has(obj)) handlers.set(obj, new Map())
if (!handlers.get(obj).has(prop)) handlers.get(obj).set(prop, [])
handlers.get(obj).get(prop).push(handler)
}
}
let p = reactive(obj)
effect(() => document.getElementById("r").value = p.r)
effect(() => document.getElementById("g").value = p.g)
effect(() => document.getElementById("b").value = p.b)
effect(() => document.getElementById("color").style.backgroundColor = `rgb(${p.r}, ${p.g}, ${p.b})`)
document.getElementById("r").addEventListener("input", e => p.r = e.target.value)
document.getElementById("g").addEventListener("input", e => p.g = e.target.value)
document.getElementById("b").addEventListener("input", e => p.b = e.target.value)
几个关键点
依赖收集与初始化
1、收集:在每次prop获取时收集对应的[obj, prop]存入usedReactivities
2、在effect中先初始化usedReactivities
3、在effect中立即调用handler(此时一般会调用get方法 依赖收集👌)
4、基于usedReactivities 遍历 其handler 并且将所有的handler存入 handlers 哈希表中 便于在set时取出依赖调用
多层嵌套属性 复用Reactivity obj(递归调用) 在reactive方法中
1、先在 reactivities 哈希表中 找是否存在 同样的obj 若有则直接返回
2、在get过程中若值为obj则递归调用 reactive
3、set时 执行对应obj prop下的所有handler (同一个porp可以挂载多个handler)