Vue的响应式理解

83 阅读4分钟
// 首先我们都知道Vue的对象的响应式原理其实通过Object.defineProperty实现的
// 所以我们就定义一个Object.definProperty,他有三个属性第一个是对象,第二个是属性,第三个是对象的配置项
Object.defineProperty(obj,data,{
    // value属性值 此属性不能和get、set一起使用
    // enumerable是否可以枚举
    // configurable是否可以删除
    // writable是否可以更改 此属性不能和get、set一起使用
    // get(){ return }访问该属性的时候触发 也叫getter 有一个返回值 其返回值就是我们访问该属性的时候返回给我们的值
    // set(newvalue){ }设置该属性的时候出发 也叫setter 有一个参数newvalue 这个newvalue就是我们要给此属性设置的值
    enumerable:true,
    configurable:true,
    get(){
        return ;//等着后期返回我们设置的属性值
    },
    set(newvalue){
        //这里面等着后期我们对我们的返回值进行一个重新赋值,赋值为newvalue
    }
})
// 我们定义一个函数叫做defeinReactive(定义响应式),我们设置三个参数obj,data,val。第一个参数是对象,第二个参数是属性,第三个参数是我们要设置的属性的值
function defineReactive(obj,data,val){
    if(arguments.length<=2){ //这一步的操作是方便我们后期的传值 后期调用这个函数的时候我们只需要传两个值就可以 不用去传第三个值 
        val = obj[data] //获取我们传的两个参数的value赋值给val方便我们下面使用
    }
    Object.defineProperty(obj,data,{
        enumerable:true,
        configurable:true,
        get(){
            return val;//对应访问该属性时要返回的值
        },
        set(newvalue){
            val = newvalue//给访问的属性重新赋值
        }
    })

}
//到此我们的函数封装完成
// 此时我们的Object.defineProperty定义完了,此时我们可以对这个对象的最外层属性进行一个响应式处理
// 我们用Object.keys()对其属性名进行一个遍历,其返回一个数组
Object.keys(obj).forEach((item)=>{
     defineReactive(obj,item)//此时我们只需要传两个值
})
// 此时我们最外层的属性定义成响应式完成我们可以打印一下对象 其会给最外层每个属性都加一个getter和setter 
// 接下来我们要对内层的属性进行一个递归 给内层的每层、每个属性都加上响应式
// 此时我们要定义一个observe函数(监听函数)用来对我们的对象进行一个处理操作,他有一个参数obj是我们的要处理的对象
function observe(obj){
    if(typeof obj !== 'object') return;  //这一步是方便我们后期的操作 传进来的不是一个对象类型的话 就直接返回空 不继续向下进行
    var ob;   //我们定义一个Ob属性方便我们后期使用
    if(obj.__ob__ !== undefined){ //查看我们的obj对象里时候有__ob__这样一个属性
        ob = obj.__ob__//如果有则将他赋给ob
    }else{
        //此时如果没有的话我们就要做一个进一步的操作,我们要定义一个Observer类来对我们内部函数进行一个监测
    }
    return ob;
}
接下来我们定义一个Observerclass Observer{ //实现他的构造器函数constructor
    constructor(value){//value为后期我们实例化这个类的时候传进来的参数
        this.walk(value)
    }//我们定义一个wlak函数方便我们后期使用
    walk(obj){
         
    }
}
//此时由于进入这个类是因为我们的对象没有__ob__属性所以我们要封装一个函数来为我们的对象添加一个__ob__的属性 我们就叫这个函数为Util函数
//Util有四个参数第一个obj是对象,第二个attribute是我们的__ob__属性,第三个是我们__ob__属性的值,第四个是我们的是否可以枚举
function Util(obj,attribute,data,isenumerable){
//我们用Object.defineProperty来添加__ob__属性
Object.defineProperty(obj,attribute,{
    value:data,
    enumerable:isenumerable
})
}
class Observer{ 
    constructor(value){//value为后期我们实例化这个类的时候传进来的参数
        Util(value,'__ob__',this,false)//这里面this代表的是我们的实例化对象,一般都不让枚举所以第四个参数给个false
        this.walk(value)
    }
    walk(obj){
        //此时我们将之前对外层属性进行一个响应式的操作放在这个walk函数里方便我们后期调用 
        Object.keys(obj).forEach((item)=>{
            defineReactive(obj,item)
       })
    }
}
//然后完善我们的observe函数对象中没有__ob__的情况
function observe(obj){
    if(typeof obj !== 'object') return; 
    var ob;   
    if(obj.__ob__ !== undefined){ 
        ob = obj.__ob__
    }else{
        ob = new Observer(obj)
    }
    return ob;
}
// 然后执行我们的observe函数
observe(obj);
// 此时分为一下几步:
// 1.先对我们的类型进行判断 发现是object
// 2.判断是否有__ob__,发现没有执行else
// 3.进入Observe类中
// 4.添加一个__ob__属性,执行walk方法
// 5.将我们对象的每个属性锦星遍历然后调用defineReactive函数
// 6.判断参数长度,对val进行赋值
// 7.此时我们要进行重要的一步
// 在函数defineReactive中我们要添加一个变量来记录一下我们的val
function defineReactive(obj,data,val){
    if(arguments.length<=2){ 
        val = obj[data] 
    }
    let children = val;//定义一个变量children记录我们的val
    observe(children);//将val传入observe中 这是一个很要的操作 他会将val传入我们的observe中然后再observe函数中判断一下类型 如果不是对象说明不是深层次对象的属性 则返回空继续向下执行
                 //如果是的话则判断是否有__ob__没有__ob__的话就走Obsever类然后在走walk函数在进入defineReactive函数中对下一层进行一个判断操作,以此循环到最里层给其添加一个setter、getter
    Object.defineProperty(obj,data,{
        enumerable:true,
        configurable:true,
        get(){
            return val;//对应访问该属性时要返回的值
        },
        set(newvalue){
            val = newvalue//给访问的属性重新赋值
        }
    })

}
//此时注意我们在设置值的时候也要給设置的值增加一个深层次的监听操作让其也变为响应式
function defineReactive(obj,data,val){
    if(arguments.length<=2){ 
        val = obj[data] 
    }
    let children = val;
    observe(children);
    Object.defineProperty(obj,data,{
        enumerable:true,
        configurable:true,
        get(){
            return val;
        },
        set(newvalue){
            val = newvalue
            observe(newvalue);//給新增加的值也进行一个监听操作
        }
    })

}
//至此我们的响应式完成 其最终代码为
var obj = {
    a:1,
    b:{
        m:{
            n:{
                j:{
                    s:1
                }
            }
        }
    },
    c:'ss'
}

function definReactive(obj,data,val) {
    if(arguments.length <=2){
        val = obj[data]
    }
    let childrenItem = val;
    observe(childrenItem)
     Object.defineProperty(obj,data,{
         //注:value 和 writable 两个属性是不能和get()、set()同时使用的
        //  value:val,   
         enumerable:true,
        //  writable:true,
         configurable:true,
         get(){
             console.log(`我正在访问${obj}${data}属性的值`);
             return val;
         },
         set(newvalue){
            console.log(`我正在设置${obj}${data}属性的值为${newvalue}`);
            val = newvalue
            observe(newvalue)
         }
     })
}

function observe(obj){
    if(typeof obj !== 'object') return;
    var ob;
    console.log(obj);
    if(obj.__ob__ !== undefined){
        ob = obj.__ob__;
    }else{
        ob = new Observer(obj);
    }
    return ob
}

class Observer{
    constructor(value){
        console.log(111);
        Util(value,'__ob__',this,false);
        this.walk(value)
 
    }
    walk(ele){
        Object.keys(ele).forEach((item)=>{
            definReactive(ele,item);
       })
    }
}


function Util(obj,data,attribute,isenmuerable){
     Object.defineProperty(obj,data,{
         value:attribute,
         enumerable:isenmuerable
     })
}
observe(obj);

//我们还可以对其进行一个抽离