SOURCE

const bucket = new WeakMap();
let activeEffect = null;
function track(target, key){
    if(!activeEffect) return;
    let depsMap = bucket.get(target);
    if(!depsMap){
        bucket.set(target, depsMap=new Map())
    }
    let depsEffect = depsMap.get(key);
    if(!depsEffect){
        depsMap.set(key, depsEffect=new Set())
    }
    depsEffect.add(activeEffect);
    activeEffect.deps.push(depsEffect);
}
function trigger(target, key){
    const depsMap = bucket.get(target)
    if(!depsMap) return;
    const depsEffect = depsMap.get(key);
    if(!depsEffect) return;
    let effectsToRun = new Set();
    depsEffect.forEach(effect=>{
        if(effect!==activeEffect){
            effectsToRun.add(effect)
        }
    })
    effectsToRun.forEach(effect=>{
        if(effect.option.scheduler){
            effect.option.scheduler(effect)
        }else{
            effect();
        }
    })
}
function reactive(data){
    const obj = new Proxy(data,{
        get(target, key){
            track(target, key);
            return target[key]
        },
        set(target, key, value){
            target[key] = value;
            trigger(target, key);
            return true;
        }
    })
    return obj
}

function cleanup(effectFn){
    for(let i=0;i<effectFn.deps.length;i++){
        const deps = effectFn.deps[i]; //是一个set类型的数据
        deps.delete(effectFn);
    }
    effectFn.deps.length = 0;
}

const effectStack = [];

function registerEffect (fn, option={}) {
    function effectFn(){
        activeEffect=effectFn;
        effectStack.push(activeEffect);
        cleanup(effectFn);
        const res = fn()
        effectStack.pop();
        activeEffect = effectStack[effectStack.length-1];
        return res
    }
    effectFn.deps = [];
    effectFn.option = option;
    if(!option.lazy){
        effectFn();
    }
    return effectFn;
}

const data = {
    name: '李翔',
    age: 18,
    ok: true

}

const obj = reactive(data);

const jobQueue = new Set();
const p = Promise.resolve();
let isFlushing = false;
function flushJob() {
    if(isFlushing) return;
    isFlushing = true;
    p.then(()=>{
        jobQueue.forEach(job=>job())
    }).finally(()=>{
        isFlushing = false;
    })
}

// registerEffect(()=>{
//     console.log('执行effect1')
//     registerEffect(()=>{
//         console.log('执行effect2')
//         obj.age
//     })
//     obj.name
// }, {
//     scheduler(fn){
//         setTimeout(()=>{
//             fn()
//         }, 1000)
//     }
// })
// obj.name = "dora"
// obj.age = 20

registerEffect(()=>{
    console.log(obj.age)
}, {
    scheduler(fn){
        jobQueue.add(fn);
        flushJob();
    }
})

obj.age++
obj.age++
obj.age++
obj.age++

function traverse(value, seen = new Set()){
    //value可能是一个值,也可能是一个对象,先不考虑数组
    if(typeof value !=='object' || seen.has(value) || value===null){
        return
    }
    seen.add(value)
    for(let k in value){
        traverse(value[k], seen)
    }
    return value
}

function watch(source, cb, options={}){
    let getter;
    if(typeof source ==='function'){
        //watch监听的是一个getter
        getter = source;
    }else{
        getter = () => traverse(source)
    }
    let newVal, oldVal;
    let cleanup;
    const job = () => {
        newVal = effectFn();
        if(cleanup){
            cleanup();
        }
        cb(newVal, oldVal, onInvalidate)
        oldVal = newVal;
    }
    const effectFn = registerEffect(()=> getter()
    ,
    {
        lazy: true,
        scheduler(){
            if(options.flush==='post'){
                const p = Promise.resolve();
                p.then(job)
            }else{
                job();
            }
        }
    }
    )
    if(options.immediate){
        job()
    }else{
        oldVal = effectFn()
    }
}

watch(obj, (newv, oldv, onInvalidate)=>{
    let expired = false;
    onInvalidate(()=>{
        expired = true;
    }) 
    let data = 1;
    setTimeout(()=>{
        data = 2
    }, 5000)
    if(expired){
        console.log(data);
    }
})

watch(()=>obj.age, (newv, oldv)=>{
    console.log({newv, oldv})
})

obj.name = 'aaa'
obj.age = 20
console 命令行工具 X clear

                    
>
console