简述vue2.0响应式 原理

2020-03-08  本文已影响0人  景元合

前言

关于vue2.0响应式原理,应该都清楚是使用了defineProperty的get()与set()属性,今天结合代码将其响应式原来重新梳理一遍。

关于defineProperty

Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。
Object.defineProperty(obj, prop, descriptor)
参数

如果一个描述符不具有value,writable,get 和 set 任意一个关键字,那么它将被认为是一个数据描述符。如果一个描述符同时有(value或writable)和(get或set)关键字,将会产生一个异常。

对于object的数据劫持

function updateView(){
    console.log('视图更新');
}
function defineReactive(target,key,value){
    Object.defineProperty(target,key,{
        get(){
            return value
        },
        set(newValue){
            if(newValue!==value){
                updateView();
                value=newValue
            }
        }
    })
}
function observe(target){
    if(typeof target!=='object'||target===null){
        return
    };
    for(let key in target){
        defineReactive(target,key,target[key])
    }
}
let data={name:'1'};
observe(data);
data.name=2;
console.log(data.name);
//视图更新
//2

以上代码是最简单的对于object的数据劫持,但是会发现当object里面还有对象时候是坚持不到变化的,这是因为此时只是对最外层数据进行了数据劫持,因此还需要在defineReactive内使用递归调用observe()方法,同时可以在set()方法内调用observe()方法

function updateView(){
    console.log('视图更新');
}
function defineReactive(target,key,value){
    observe(value);
    Object.defineProperty(target,key,{
        get(){
            return value
        },
        set(newValue){
            if(newValue!==value){
                observe(newValue)
                updateView();
                value=newValue
            }
        }
    })
}
function observe(target){
    if(typeof target!=='object'||target===null){
        return
    };
    for(let key in target){
        defineReactive(target,key,target[key])
    }
}
let data={name:'1',age:{count:2},money:{count:2}};
observe(data);
data.age.count=3;
data.money={count:3}
console.log(data.age.count);
console.log(data.money.count)
视图更新
视图更新
3
3

同时大家知道defineProperty没法对数组长度进行监控,因此需要重写Array的'push','pop','shift','unshift','unshift','splice','sort','reverse'进行重写

let oldArrayPrototype=Array.prototype;
let proto=Object.create(oldArrayPrototype);
['push','pop','shift','unshift','unshift','splice','sort','reverse'].forEach(method=>{
    proto[method]=function(){
        updateView();
        oldArrayPrototype[method].call(this,...arguments);
    }
})
function updateView(){
    console.log('视图更新');
}
function defineReactive(target,key,value){
    observe(value);
    Object.defineProperty(target,key,{
        get(){
            return value
        },
        set(newValue){
            if(newValue!==value){
                observe(newValue)
                updateView();
                value=newValue
            }
        }
    })
}
function observe(target){
    if(typeof target!=='object'||target===null){
        return
    };
    if(Array.isArray(target)){
        Object.setPrototypeOf(target,proto);
    }
    for(let key in target){
        defineReactive(target,key,target[key])
    }
}
let data={name:'1',age:[1,2]};
observe(data);
data.age.push(2);
console.log(data.age);
//视图更新
//[ [Getter/Setter], [Getter/Setter], 2 ]

写在最后

大家应该发现使用defineProperty存在的问题

上一篇 下一篇

猜你喜欢

热点阅读