手写个vue3响应式源码

35 阅读1分钟
class Vue{
    constructor(options){
        const data=options.data;
        this._data=data;
        _proxy(this,'_data',data);
        observer(this._data);
        
        new Watch(this, function() {
            return data.name + ' create defineReactive'
        }, function() {
            console.log('watch callback: ', this.value)
        })
    }
}

const _proxy=(vm,sourceKey,data)=>{
    Object.keys(data).forEach(key=>{
        Object.defineProperty(vm,key,{
            get (){
                return vm[sourceKey][key]
            },
            set(val){
                vm[sourceKey][key]=val
            }
        })
    })
}

const observer=(data)=>{
    const ob=new Observer(data);
    
}

class Observer{
    constructor(data){
       this.walk(data);
    }
    walk(data){
       Object.keys(data).forEach(key=>{
          defineReactive(data,key);
       })
    }
}
const defineReactive=(data,key)=>{
    let val=data[key];
    const dep=new Dep();
    Object.defineProperty(data,key,{
        get(){
            dep.depend();
            return val;
        },
        set(nVal){
            val=nVal;
            dep.notfify();
        }
    })
}

class Dep{
    constructor(){
        this.uid=Dep.uid++;
        this.subs=[];
    }
    static uid=0;
    static target=null;
    addSub(wather){
        this.subs.push(wather);
    }
    depend(){
        if(Dep.target){
            Dep.target.addDep(this);
        }
    }
    removeSub(wather){
        const index=this.subs.indexOf(wather);
        this.subs.splice(index,1)
    }
   
    notfify(){
        this.subs.forEach(sub=>{
            sub.update();
        })
    }
}
class Watch{
    constructor(vm,render,cb){
        this.vm=vm;
        this.render=render;
        cb=this.cb=cb;
        this.deps=[];
        this.depsIdSet=new Set();
        this.newDeps=[];
        this.newDepsIdSet=new Set();
        this.update();
    }
    get(){
        this.newDeps=[];
        this.newDepsIdSet=new Set();
        Dep.target=this;
        const val= this.render();
        Dep.target=null;
        this.deps.forEach(oldDep=>{
            const noInNewDep=!this.newDepsIdSet.has(oldDep.uid);
            if(noInNewDep){
                oldDep.removeSub(this);
            }
        })
        this.deps=this.newDeps;
        this.depsIdSet=this.newDepsIdSet;
        return val
    }
     //假设render访问的是 a a b d => a b b c
    addDep(dep){
        if(!this.newDepsIdSet.has(dep.uid)){
            this.newDepsIdSet.add(dep.uid);
            this.newDeps.push(dep);
            if(!this.depsIdSet.has(dep.uid)){
                //例如c是新增的,则将这个wather添加到c的dep的subs中
                dep.addSub(this);
            }
        }
    }

    update(){
        this.value=this.get();
        this.cb(this.value);
    }
}

let student = new Vue({
    data: {
        name:'小明',
    }
})
console
student.name = '小红'
console.log('log=>student',student);
setTimeout(()=>{
    student.name = '小刚'
},1000)