2.1.x 计算属性computed与lazy

完整的实现代码如下:

JavaScript
let activeEffect
const effectStack = []
// 副作用函数栈
function effect(fn, options={}) {
    const effectFn = ()=>{
        cleanup(effectFn)

        activeEffect = effectFn
        effectStack.push(effectFn)

        // 保存副作用函数执行的返回值
        const res = fn()

        effectStack.pop()
        activeEffect = effectStack[effectStack.length - 1]

        return res
    }
    // 挂载配置
    effectFn.options = options
    effectFn.deps = []

    // 只有在不为lazy的时候立即执行
    if (!options.lazy) {
        effectFn()
    }
    return effectFn
}

// 清除副作用函数避免不必要的更新
function cleanup(effectFn) {
    // console.warn('cleanup', [...effectFn.deps])
    // 遍历依赖数组
    for (let i = 0; i < effectFn.deps.length; i++) {
        const deps = effectFn.deps[i]
        // 从依赖集合中删除该函数
        deps.delete(effectFn)
    }
    // 重置数组
    effectFn.deps.length = 0
}

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)
    // console.log('getter track', key, deps)

    // deps 是一个与当前副作用函数关联的 Set
    // 添加到函数的 deps 数组中
    activeEffect.deps.push(deps)
}

function trigger(target, key) {
    const depsMap = bucket.get(target)
    if (!depsMap) {
        return
    }
    const effects = depsMap.get(key)

    const effectToRun = new Set()
    effects && effects.forEach(effectFn=>{
        if (effectFn !== activeEffect) {
            effectToRun.add(effectFn)
        }
    }
    )
    effectToRun.forEach(effectFn=>{
        // 如果有调度器,则执行调度器
        if (effectFn.options.scheduler) {
            effectFn.options.scheduler(effectFn)
        } else {
            effectFn()
        }
    }
    )
}

const bucket = new WeakMap()

const data = {
    foo: 1,
    bar: 1,
}
const obj = new Proxy(data,{
    get(target, key) {
        track(target, key)
        return target[key]
    },
    set(target, key, newVal) {
        target[key] = newVal
        trigger(target, key)
    }
})

// 封装为computed函数
function computed(getter) {
    // 用来缓存上一次的值
    let value
    // 用于标记是否需要重新取值
    let dirty = true

    const effectFn = effect(getter, {
        // 让副作用函数不要立即执行
        lazy: true,
        scheduler() {
            // 数据变化,执行调度器,让dirty为true
            dirty = true

            // 当计算属性依赖的响应式数据变化时,手动调用trigger函数出发响应
            trigger(obj, 'value')
        }
    })

    const obj = {
        // getter 在取值时会自动执行
        get value() {
            // 只有当dirty为true的时候,才进行取值操作
            if (dirty) {
                value = effectFn()
                // 将dirty设置为false,下次直接使用缓存的值
                dirty = false
            }
            // 当读取value时,手动调用track函数进行追踪
            track(obj, 'value')
            return value
        }
    }

    return obj
}

const sumRes = computed(()=>{
    console.log('effect run')
    return obj.foo + obj.bar
}
)

effect(() => {
    console.log('effect sumRes', sumRes.value)
})

obj.foo++