《Vue.js设计与实现》读书笔记(三)

watch实现

const data = {text: 'hello world', ok: true, foo: 1, bar: 2};

const bucket = new WeakMap();
let activeEffect;
let effectStack = [];

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 target[key];
    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);
    activeEffect.deps.push(deps);
}
function trigger(target, key) {
    const depsMap = bucket.get(target);
    if(!depsMap)return;
    const effects = depsMap.get(key);
    const effectsSet = new Set();
    effects&& effects.forEach(fn => {
        if(activeEffect !== fn) {
            effectsSet.add(fn);
        }
    })
    effectsSet.forEach(fn => {
        fn.options.scheduler ? fn.options.scheduler(fn) : fn();
    });
}
function cleanup(effectFn) {
    for(let i=0; i< effectFn.deps.length; i++) {
        const deps = effectFn.deps[i];
        deps.delete(effectFn);
    }
    effectFn.deps.length = 0;
}
const 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 = [];
    if(!options.lazy) {
        effectFn();
    }
    return effectFn;
}

function watch(source, cb, options= {}) {
    let getter, newVal, oldVal;
    if(typeof source === 'function'){
        getter = source;
    }else {
        getter = ()=>traverse(source);
    }
    const job = ()=>{
        newVal = effectFn();
        cb(newVal, oldVal);
        oldVal = newVal;
    }

    const effectFn = effect(()=> getter(), {
        lazy: true,
        scheduler: job
    })
    if(options.immediate) {
        job();
    }else{
        oldVal = effectFn();
    }
}
function traverse(value, seen = new Set()) {
    if(typeof value !== "object" || value === null || seen.has(value)) return;
    seen.add(value); // 防止死循环
    for (const key in value) {
        traverse(value[key], seen);
    }
    return value;
}

watch(()=>obj.text, (newVal, oldVal)=>{console.log(newVal)});
obj.text = 'hello i';
obj.text = 'hello j';

执行结果
在这里插入图片描述
加上immediate参数让其立即执行

watch(()=>obj.text, (newVal, oldVal)=>{console.log(newVal)}, {immediate: true});

执行结果
《Vue.js设计与实现》读书笔记(三)_第1张图片

你可能感兴趣的:(vue3解析,前端,vue.js,javascript,前端)