vue響應式原理

vue做爲一個MVVM框架,是若是對數據屬性實現響應式的呢?經過深刻研究,發現它是經過Object.defineProperty(只支持純對象)綁定get,set來實現的,下面就來探究一下其中的原理。vue

Object.definePropety()

/**
 * @param {[Object]} obj 目標對象
 * @param {[String]} prop 目標對象的屬性
 * @param {[String]} descriptor [屬性描述符:數據描述符和存取描述符兩種形式]
 */
var obj = {};
var descriptor = Object.create(null) // 沒有繼承的屬性
// 默認沒有enumerable,沒有configurabel,沒有writeable
descriptor.value  = 'static';
object.definePorperty(obj, 'key', descriptor);

// 屬性描述符
object.definePorperty(obj, 'key', {
  enumerable: false,  // 定義對象的屬性是不是可枚舉
  writeable: faslse,  // 定義對象的屬性是否可修改
  configurable: fase, // 定義對象屬性是否可刪除
  value: "static"
})

// 存取描述符
var bValue
object.defineProperty(obj, 'key', {
  get: function() {
    return bValue;
  },
  set: function(newValue) {
    bValue = newValue
  },
  eumerable: true,
  configurable: true
})

vue在denfineProperty的方法上進行進一步的封裝數組

function defineReactive(data,key,value) {
  Object.defineProerty(data,key,{
    get: function() {
      return value
    },
    set: function(newValue) {
      if(newValue === value) return;
      value = newValue;
    },
    eumerable: true,
    writeable: true
  })
}

怎麼觀察Observer

咱們之因此要觀察Observer,是爲了當數據屬性發生變化的時候,通知那些使用到該屬性的地方。
這要咱們就能夠先把全部使用到該屬性依賴所有收集起來,當屬性改變的時候,循環通知全部的屬性依賴進行更新框架

依賴蒐集

在defineReative的基礎上進一步實現ui

/**
 * [defineReactive description]
 * @param  {[Object]} data  [對象obj]
 * @param  {[String]} key   [對象屬性]
 * @param  {[String]} value [對象屬性的值]
 * @return {[type]}       [description]
 */
function defineReactive(data,key,value) {
  let dep = [];
  Object.defineProperty(data,key,{
    eumerable: true,
    configurable: true,
    get: function() {
      dep.push(watcher);
    },
    set: function(newValue) {
      if(value === newValue) return;
      for(let i = 0; i < dep.length; i++ ) {
        watcher.update(newValue, value);
      }
      value = newValue
    }
  })
}


// 進一步把收集依賴的部分封裝起來
class Dep {
  static target: ?Watcher;
  id: Number;
  subs: String<watcher>;

  construstor() {
    this.id = uid++;
    this.subs = [];
  }

  addSub(sub: Watcher) {
    this.subs.push(sub)
  }

  removeSub(sub: Watcher) {
    remove(this.subs, sub)
  }

  depend() {
    if(Dep.target == Watcher) {
      this.addSub(Dep.target)
    }
  }

  notify() {
    const subs = this.subs.splice();
    for(let i = 0; i < subs.length; i++) {
      subs[i].update();
    }
  }
}

// 修改後的代碼
function defineReactive(data,key,value) {
  let dep = New Dep();
  Object.defineProperty(data,key, {
    eumerable: true,
    configaruable: true,
    get: function() {
      dep.depend();
      return value;
    },
    set: function(NewValue) {
      if(value === NewValue) return;
      dep.notify();
      value = NewValue;
    }
  })
}

總結: 要觀察就要收集依賴到Dep用於專門存儲依賴, 上面咱們將全部存儲到dep數組的叫作watcher。致這些watcher依賴是在vue中的templete,watch,computed中都要是收集的,當數據改變的時候就去通知到這些地方,這樣就須要抽象出一個可以處理不一樣狀況的類,而後咱們在依賴收集的時候只須要收集這些封裝好的類進來,通知也是通知到它一個,而後它負責通知其餘地方使用到該屬性的地方,這個類咱們叫它watcherthis

watcher

class Watcher {
  constructor(expOrFn, callback) {
    this.getter = parsePath(expOrFn);
    this.callback = callback;
    this.value = this.get();
  }

  get() {   // 執行get, 將本身注入到dep.target
    Dep.target = this;
    this.getter.call(vm);   // 觸發getter,getter裏面觸發depend, depend執行addSub,將watcher放到dep依賴中
    Dep.tabget = undefined;
  }

  update() {
    const oldValue = this.value;
    this.value = this.get();
    this.callback(this.value, oldValue)
  }
}

上面的代碼就是一個完整的一個watcher,所作的事情就是獲取和更新要用到的屬性code

至此,單個屬性監測響應的整個過程就實現了,對於多個屬性的監測代碼實現以下,遍歷對象的全部屬性server

function observe(obj) {
  if(obj.constructor !== Object) return;
  const keys = obj.keys();
  for(let i = 0; i < keys.length; i++) {
    defineReactive(obj, keys[i], obj[keys[i]])
  }
}

function defineReactive(data,key,value) {
  observe(val);
  let dep = new Dep();
  Object.defineProperty(data,key, {
    eumerable: true,
    configurable: true,
    get: function() {
      dep.depend();
      return value;
    },
    set: function(newValue) {
      if(value === newValue) return;
      dep.notify();
      value = newValue;
    }
  })
}

說明

  • 屬性的可枚舉性: 能夠經過for-in循環來進行遍歷
  • Object.keys() : 該方法會返回一個由一個給定對象的自身可枚舉屬性組成的數組
相關文章
相關標籤/搜索