Vue響應式數據

Vue響應式數據

Vue中的響應式數據通過Object.defineProperty實現,但這個方法不能劫持數組。
首先我們定義一個要響應式的數據

    let obj ={
      name:'qc',
      location:{adress:'hz'},
      arr:[1]
    }

在創建一個渲染notify函數,用來提示我們的數據是否被渲染提示。

    function notify(){
      console.log("視圖更新了!")
    }

接下來開始正式寫代碼,設想當我們修改了obj對象裏的數據,命令窗口就會提示視圖更新了,並且數據已被修改。
創建一個observer函數,目的是遍歷此對象的屬性。

    function observer(obj){
    if(typeof obj == 'object'){
      for (const key in obj) {
        defineReactive(obj,key,obj[key])
      }
    }
}

創建一個defineReactive函數,目的用來劫持數據。

     function defineReactive(data,key,value){
      // console.log(data,key,value);
        Object.defineProperty(data,key,{
          get(){
            if(typeof value == 'object'){
              observer(value)
            }
            return value
          },
          set(newValue){
            notify()
            value=newValue
          }
        })
    }

Object.defineProperty()方法會直接在一個對象上定義一個新屬性,或者修改一個對象的現有屬性,並返回這個對象。
Object.defineProperty具體查看mdn文檔
通過observer函數遍歷出obj屬性,傳給defineReactive,在此函數內用Object.defineProperty,get方式拿到當前的值value,set方式可以拿到設置的新的值,新值賦給老值就完成了,數據更新。
讓我們看下效果

    observer(obj)
    obj.name='aa'
    console.log(obj.name)

結果
目前完成的只能遍歷對象一層,若出現多層的話,我們可以採取遞歸劫持。

    function defineReactive(data,key,value){
      // console.log(data,key,value);
        Object.defineProperty(data,key,{
          get(){
            if(typeof value == 'object'){
              observer(value)
            }
            return value
          },
          set(newValue){
            notify()
            value=newValue
          }
        })
    }
    obj.name='aa'
    obj.location.adress='bj'
    console.log(obj.location.adress);

結果

以上方式都不能對數組響應式,vue對數組採用了另一套方式,因爲數組本身的方法就可以讓數組更新例如push,splice
首先我們先遍歷出那些可以讓數組更新的方法

    let mothod =['pop','shift','unshift','sort','reverse','splice','push'] 

這裏爲了讓大家看到notify函數執行視圖更新了,我們copy一份數組的原型,在copy的上面去修改。

    let arrayProto=Array.prototype//先獲取到原來的原型上的方法 ,爲了修改數組裏原生方法 添加一個render函數操作
    let proto =Object.create(arrayProto)//複製一個新原型對象 跟舊的無關

    mothod.forEach(mothod=>{
      proto[mothod]=function(){
        notify()
        arrayProto[mothod].call(this,...arguments)
      }
    })

在修改observer函數

    function observer(obj){
      if(Array.isArray(obj)){
        obj.__proto__=proto
       return
      }
    if(typeof obj == 'object'){
       for (const key in obj) {
         defineReactive(obj,key,obj[key])
      }
    }
  }

修改defineReactive函數

    function defineReactive(data,key,value){
  // console.log(data,key,value);
    Object.defineProperty(data,key,{
      get(){
        if(typeof value == 'object'){
          observer(value)
        }
        if(Array.isArray(value)){
          observer(value)
        }
        return value
      },
      set(newValue){
        notify()
        value=newValue
      }
    })
}

讓我們去迫不及待的去看下效果

    observer(obj)
    obj.arr.push(22)
    console.log(obj.arr);
     //不支持 數組內容直接改變例如arr[1]=11,不支持數組length-- 都不會發生數據響應

結果
這樣我們就實現了簡易的數據響應式
注意因爲vue數據響應都是綁在data屬性裏面,所以你給一個對象添加一個新的屬性時,是不會生效數據響應的,不過vue中提供了$set方法,可以動態的添加響應式數據,我們再次也可以去實現下。

     function $set(data,key,value){
      if(Array.isArray(data)){
        return data.splice(key,1,value) //當前key 改一個 值是value 觸發 數組更新
      }

      defineReactive(data,key,value)
    }

動態添加屬性

    $set(obj,'a',1)
    obj.a=2
    console.log(obj.a);

結果
動態添加數組

    obj.arr.push(22)
    $set(obj.arr,0,2)
    console.log(obj.arr)

結果

總結

vue響應式數據就是依靠Object.defineProperty來實現的,但是缺點是數組無法實現,所以vue當遇到數組時會啓動另一套方法,這方法就是利用數組本身的方法例如 push,splice 來觸發 數組更新。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章