簡述vue2.0響應(yīng)式 原理

前言

關(guān)于vue2.0響應(yīng)式原理,應(yīng)該都清楚是使用了defineProperty的get()與set()屬性,今天結(jié)合代碼將其響應(yīng)式原來重新梳理一遍。

關(guān)于defineProperty

Object.defineProperty() 方法會直接在一個對象上定義一個新屬性,或者修改一個對象的現(xiàn)有屬性, 并返回這個對象。
Object.defineProperty(obj, prop, descriptor)
參數(shù)

  • obj
    要在其上定義屬性的對象。
  • prop
    要定義或修改的屬性的名稱。
  • descriptor
    將被定義或修改的屬性描述符。
    其中descriptor有兩種主要形式:數(shù)據(jù)描述符和存取描述符。數(shù)據(jù)描述符是一個具有值的屬性,該值可能是可寫的,也可能不是可寫的。存取描述符是由getter-setter函數(shù)對描述的屬性。

如果一個描述符不具有value,writable,get 和 set 任意一個關(guān)鍵字,那么它將被認(rèn)為是一個數(shù)據(jù)描述符。如果一個描述符同時有(value或writable)和(get或set)關(guān)鍵字,將會產(chǎn)生一個異常。

對于object的數(shù)據(jù)劫持

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的數(shù)據(jù)劫持,但是會發(fā)現(xiàn)當(dāng)object里面還有對象時候是堅持不到變化的,這是因為此時只是對最外層數(shù)據(jù)進(jìn)行了數(shù)據(jù)劫持,因此還需要在defineReactive內(nèi)使用遞歸調(diào)用observe()方法,同時可以在set()方法內(nèi)調(diào)用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沒法對數(shù)組長度進(jìn)行監(jiān)控,因此需要重寫Array的'push','pop','shift','unshift','unshift','splice','sort','reverse'進(jìn)行重寫

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 ]

寫在最后

大家應(yīng)該發(fā)現(xiàn)使用defineProperty存在的問題

  • 一上來會使用遞歸便利所有屬性,如果嵌套很深,那效率會比較低。
  • 對于沒有定義的數(shù)據(jù),沒法檢測其變化
  • 無法監(jiān)控到數(shù)組下標(biāo)的變化,導(dǎo)致直接通過數(shù)組的下標(biāo)給數(shù)組設(shè)置值,不能實時響應(yīng)
    為了彌補(bǔ)defineProperty存在的問題,vue3.0使用proxy替代defineProperty,一會再寫一篇關(guān)于vue3.0雙向綁定的原理文章。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容