Skip to content
On this page

设计一个完善的响应系统

问题一:无法获取当前副作用函数

直接使用名字( 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())
}