Vue響應式數據

Vue響應式數據

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

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函數,目的用來劫持數據。this

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方式能夠拿到設置的新的值,新值賦給老值就完成了,數據更新。
讓咱們看下效果spa

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

結果
目前完成的只能遍歷對象一層,若出現多層的話,咱們能夠採起遞歸劫持。prototype

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
首先咱們先遍歷出那些能夠讓數組更新的方法code

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

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

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

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

在修改observer函數xml

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 來觸發 數組更新。

相關文章
相關標籤/搜索