天天看點

1、手寫vue2.0之data添加觀察

本文是要實作對象使用Object.defineProperty重寫對象,例

//例把下面對象obj
let obj={a:1};
//修改成以下寫法
let aValue=1;
Object.defineProperty(obj,a,{
  get(){
    return aValue;
  },
  set(newValue){
    if(aValue===newValue) return;
    aValue=newValue;
  }
})
這樣在對對象屬性進行擷取、修改時,都能在get、set中監測到對象屬性變化,但不能對對象屬性值為數組進行監測,需要重寫數組方法,下一篇      

首先介紹Object.defineProperty:

  作用是給對象的屬性設定get、set、value、writable(可重寫)、configurable(可配置、删除)、enumerable(可周遊)等

  其中writable不能和get、set一起出現,get、set不能和value一塊出現

Object.defineProperty(obj,key,{
  value,//屬性值
  writable,//boolean
  configurable,//boolean
  enumerable,//boolean
  get(){},//不能和writable、value一塊配置
  set(newValue){}//不能和writable、value一塊配置
})      

******************************正文開始********************************

在使用vue時

//在這裡隻對vue的data進行處理
let vm = new Vue({
  data:function(){
    return {
      a:1,
      b:{c:2}
    }
  }
})
可以把以上改成
let options={
  data:function(){
  return {      
a:1,
      b:{c:2}
    }
  }
}
let vm = new Vue(options)      

模仿以上,建立一個函數命名為Vue,

function Vue(options){
}      

問題:Vue如何将options裡的data轉成Object.defineProperty的寫法

  解剖問題:

    1、在new  Vue()時即完成了到Object.defineProperty的轉換

    解決:在new Vue()時執行了一個方法,命名為_init

function Vue(options){
  this._init(options);
}
Vue.prototype._init=function(options){
  console.log("new的時候執行了_init")
}
解析 new Vue()會将所有的屬性執行個體化,是以this_init()也會被執行,執行的時候會在對象的原型鍊中查找同名方法,是以會找到Vue.prototype._init方法,我們可以在_init中對options進行處理      

開始正題

1、封裝Object.defineProperty

function defineReactive(obj,key,value){
  Object.defineProperty(obj,key,{
    get(){
      return value;
    },
    set(newValue){
      if(newValue===value) return;
      value=newValue;
    },
    configurable:true,
    enumerable:true
  })
}      

2、封裝判讀目前變量是不是需要被觀察

function observe(data){
  if(typeof data!=="object"||data===null){return}
  walk(data);
}      

3、周遊對象并設定Object.defineProperty

function walk(data){
  let keys=Object.keys(data);
  for(let i=0;i<keys.length;i++){
    defineReactive(data,keys[i],data[keys[i]])
  }
}      

以上會有一個問題,如果是{a:1}這種則沒問題,如果{a:1,b:{c:2}},則不能周遊到底層,是以需要在設定get前對設定的值進行判斷目前值需不需要被觀察,如果需要被觀察,先觀察,不需要直接給目前屬性指派

function defineReactive(obj,key,value){
  observe(value);//判斷目前值需不需要被觀察,需要觀察,先觀察再進行下面的處理
  Object.defineProperty(obj,key,{
    get(){
      return value;
    },
    set(newValue){
      if(newValue===value) return;
      value=newValue;
    },
    configurable:true,
    enumerable:true
  })
}      
function defineReactive(obj,key,value){
  observe(value);//判斷目前值需不需要被觀察,需要觀察,先觀察再進行下面的處理
  Object.defineProperty(obj,key,{
    get(){
      return value;
    },
    set(newValue){
      if(newValue===value) return;
      value=newValue;
      observe(newValue);
    },
    configurable:true,
    enumerable:true
  })
}      
obj ={
  a:[2,3]
}
obj.a.push(4);
//并不能監測數組屬性的改變,需要重寫數組方法,下一章介紹