开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 16 天,点击查看活动详情 响应式数据的基本实现
什么是分支切换
假如遇到了如下问题,obj.text 的显示依赖于obj.haveData的值来确定。
我们将这种因为obj.haveData变化,执行的代码分支跟着变化的情况叫分支切换
effect(() => {
console.warn('副用用函数执行====')
document.body.innerText = obj.haveData ? obj.text : 'no data'
})
按照上一节数据结构来
{obj对象:
{ haveData: {effectFn},
text: {effectFn }
}
}
据此可以看到haveData和text的副作用函数都是effectFn
由此当haveData值为false执行副用用函数effectFn后,依赖关系就变了,因为没用到obj.text,而当obj.text变化后仍旧会执行effectFn函数,可是此时obj.text压根就用不到了。从而导致了不必要的更新。
造成这种现象的主要原因是,当obj.haveData变成false的时候。依赖关系已经变了,text对应的依赖已经不存在了
第一次执行
document.body.innerText = obj.haveData ? obj.text : 'no data'
调用了两次get函数 key分别是haveData 以及text,
target ---> haveData -----> effectFn
target ---> text -----> effectFn
解决方法
解决起来当更新依赖就好了。
obj.haveData = false -->调用set方法 --> 执行副作用函数 --> 清空所有依赖关系 --> 副作用函数调用get方法obj.haveData ? obj.text : 'no data' --> 重新收集依赖
let activeEffect
function effect(fn) {
const effectFn = () => {
cleanup(effectFn)
activeEffect = effectFn
fn()
}
effectFn.deps = []
effectFn()
}
function cleanup(effectFn) {
for (let index = 0; index < effectFn.deps.length; index++) {
const deps = effectFn.deps[index];
deps.delete(effectFn)
}
effectFn.deps.length = 0
}
let effectFuncList = new WeakMap()
const data = { text: 'hello world', ok: true }
const obj = new Proxy(data, {
get(target, key) {
if (!activeEffect) return target[key]
// 是不是根据key来保存就能够保证唯一呢。
let keyMap = effectFuncList.get(target)
if (!keyMap) effectFuncList.set(target, (keyMap = new Map()))
let deps = keyMap.get(key)
if (!deps) keyMap.set(key, (deps = new Set()))
deps.add(activeEffect)
activeEffect.deps.push(deps)
return target[key]
},
set(target, key, newValue) {
target[key] = newValue
// 根据target获取以key为键的集合
const keyMap = effectFuncList.get(target)
if (!keyMap) return
// 根据target获取以key为键的集合
const effectsSet = keyMap.get(key)
if (!effectsSet) return
// 执行副作用函数
const effectsToRun = new Set(effectsSet)
effectsToRun.forEach(fn => fn())
}
})
effect(() => {
console.warn('副用用函数执行====')
document.body.innerText = obj.ok ? obj.text : 'no data'
})
setTimeout(() => {
obj.ok = false
}, 1000);
setTimeout(() => {
obj.text = 'hello vue'
}, 3000);
开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 16 天,点击查看活动详情 响应式数据的基本实现