Object.defineProperty數(shù)據(jù)劫持

image.png
  • observer用來實(shí)現(xiàn)對(duì)每個(gè)vue中的data中定義的屬性循環(huán)用Object.defineProperty()實(shí)現(xiàn)數(shù)據(jù)劫持,以便利用其中的setter和getter,然后通知訂閱者,訂閱者會(huì)觸發(fā)它的update方法,對(duì)視圖進(jìn)行更新。

  • 我們介紹為什么要訂閱者,在vue中v-model,v-name,{{}}等都可以對(duì)數(shù)據(jù)進(jìn)行顯示,也就是說假如一個(gè)屬性都通過這三個(gè)指令了,那么每當(dāng)這個(gè)屬性改變的時(shí)候,相應(yīng)的這個(gè)三個(gè)指令的html視圖也必須改變,于是vue中就是每當(dāng)有這樣的可能用到雙向綁定的指令,就在一個(gè)Dep中增加一個(gè)訂閱者,其訂閱者只是更新自己的指令對(duì)應(yīng)的數(shù)據(jù),也就是v-model='name'和{{name}}有兩個(gè)對(duì)應(yīng)的訂閱者,各自管理自己的地方。每當(dāng)屬性的set方法觸發(fā),就循環(huán)更新Dep中的訂閱者。

Object.defineProperty(data,key,descriptor)

vue3.0之前的版本實(shí)現(xiàn)數(shù)據(jù)雙向綁定的主要實(shí)現(xiàn)方法:數(shù)據(jù)劫持(監(jiān)控屬性變化)

//  首先創(chuàng)建一個(gè)輸入框和一個(gè)展示數(shù)據(jù)的視圖
<input type="text" id="demo">
<div id="show"></div>
const oInput = document.elementById('#demo')
const oDiv = document.elementById('#show')

// 給定一個(gè)對(duì)象用于存放數(shù)據(jù),并監(jiān)聽輸入事件
const oData = {
  value: '初始值'
}
oInput.oninput = function() {
  oData.value = this.value
}

// 更新視圖
function update() {
  oDiv.innerText = oData.value
}

// defineRective 方法真正對(duì)對(duì)象的屬性進(jìn)行監(jiān)控,使用 Object.defineProperty 給屬性設(shè)置 get 和 set 監(jiān)聽屬性的讀寫,即數(shù)據(jù)劫持。同時(shí)在寫入數(shù)據(jù)的時(shí)候判斷新數(shù)據(jù)對(duì)比原數(shù)據(jù)是否發(fā)生改變,如果沒有發(fā)生改變則不進(jìn)行額外操作,避免性能浪費(fèi)。
//在寫入更新的數(shù)據(jù)后使用 upDate 對(duì)視圖進(jìn)行更新
function defineRective(data,key,value) {
  Object.defineProperty(data,key,{
    get:function () {
      return value
    },
    set:function(newValue) {
       if(newValue === value) return
       value = newValue
       update()
    }
  })
}

// Observer 方法用于監(jiān)控對(duì)象中的任意屬性是否發(fā)生改變。首先判斷數(shù)據(jù)是否存在并且是否為對(duì)象,然后使用 Object.keys() 獲取數(shù)據(jù)對(duì)象中的所有屬性,用 forEach 進(jìn)行遍歷為屬性添加defineRective 監(jiān)控 
function Observer(data) {
  if(!data || typeof data != 'object') return data
  Object.keys(data).forEach(key => {
    defineRective(data,key,data[key])
  })
}

對(duì)深層次的數(shù)據(jù)進(jìn)行監(jiān)控,即數(shù)據(jù)在數(shù)據(jù)對(duì)象內(nèi)的對(duì)象中時(shí),需要適應(yīng)遞歸的方式添加監(jiān)控
// 創(chuàng)建數(shù)據(jù)
const data = {
  valueObj: {
    value: '初始值',
    name: 'Jaraiya'
  }
}
// 監(jiān)聽輸入
oInput.oninput = function() {
  oData.valueObj.value = this.value
}
// 調(diào)用時(shí)更新視圖
update() {
  oDiv.innerText = oData.valueOf.value
}
// 真正利用defineProperty 進(jìn)行對(duì)象監(jiān)控的函數(shù)
function defineRective(data,key,value) {
  Observer(value);      # 主要是這一步
  Object.defineProperty(data,key,{
    get:function() {
      return value
    },  
    set:function(newValue) {
       if(newValue === value) return
       value = newValue
       update()       
    }
  })
}

資料來源

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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