vue源碼解析系列-compute實現機制

原本vue的響應式應該纔是重中之重。可是網上的文章不少不少。在看computed的實現以前。確定仍是要把vue的響應式如何實現好好看一下。或者說二者根本就是同樣的東西。這邊推薦幾篇文章關於vue的響應式。javascript

vue響應式簡單實現html

vue慕課響應式手記vue

仍是看看官網對於響應式的解釋:
圖片描述java

總的來講。vue實現響應式的關鍵有三個:watcher,dep,observe;app

  1. observe:遍歷data中的屬性。在get,set方法中設置核心數據劫持異步

  2. dep:每一個屬性都有一個本身的dep(消息訂閱起)用於訂製該屬性上的全部觀察者函數

  3. watcher:觀察者,經過dep實現對響應屬性的監聽觀察。觀察獲得結果後,主動觸發本身的回調oop

能夠去看看vue2.3的這三部分源碼。中間仍是有不少精美的設計。好比一個全局惟一的Dep.target,在任什麼時候候都是惟一的值。以確保同一時間只有一個觀察者在訂閱。再好比,watcher中也會存下相關的訂閱器,實現去重和實現同一個觀察者的分組(這裏是實現computed的關鍵),再如。watcher中的id也會惟一。用於異步更新的時候不一樣時出發相同的訂閱。仔細看看會收穫不小。改天我把全部的響應式的代碼也整理一下。
在理解了響應式的狀況下。咱們來看看computed的實現。最簡單的一個demo以下:ui

<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<div id="app">
  <div name="test">{{computeA}}</div>

</div>
</body>
<script src="vue.js"></script>
<script type="text/javascript">
  new Vue({
  el: '#app',
  data: function () {
    return {
      firstName: 111,
      lastName: 222
    }
  },
  computed: {
    computeA: function () {
      return this.firstName + ' ' + this.lastName
    }
  },
  created(){
    setTimeout(
      () => {
        this.firstName = 333;
      },1000
    )
  }
})
</script>
</html>

咱們來從源碼的角度看看發生了什麼:this

  1. 在初始化實例建立響應式的時候。對options中的computed作了特殊處理:

function initComputed (vm, computed) {
  var watchers = vm._computedWatchers = Object.create(null);

  for (var key in computed) {
    var userDef = computed[key];
    var getter = typeof userDef === 'function' ? userDef : userDef.get;
    {
      if (getter === undefined) {
        warn(
          ("No getter function has been defined for computed property \"" + key + "\"."),
          vm
        );
        getter = noop;
      }
    }
    // create internal watcher for the computed property.
    watchers[key] = new Watcher(vm, getter, noop, computedWatcherOptions);//爲每個computed項目訂製一個watcher

    // component-defined computed properties are already defined on the
    // component prototype. We only need to define computed properties defined
    // at instantiation here.
    if (!(key in vm)) {
      defineComputed(vm, key, userDef);
    } else {
      if (key in vm.$data) {
        warn(("The computed property \"" + key + "\" is already defined in data."), vm);
      } else if (vm.$options.props && key in vm.$options.props) {
        warn(("The computed property \"" + key + "\" is already defined as a prop."), vm);
      }
    }
  

function defineComputed (target, key, userDef) {
  if (typeof userDef === 'function') {
    sharedPropertyDefinition.get = createComputedGetter(key);
    sharedPropertyDefinition.set = noop;
  } else {
    sharedPropertyDefinition.get = userDef.get
      ? userDef.cache !== false
        ? createComputedGetter(key)
        : userDef.get
      : noop;
    sharedPropertyDefinition.set = userDef.set
      ? userDef.set
      : noop;
  }
  Object.defineProperty(target, key, sharedPropertyDefinition);
}

function createComputedGetter (key) {//構造該computed的get函數
  return function computedGetter () {
    var watcher = this._computedWatchers && this._computedWatchers[key];
    if (watcher) {
      if (watcher.dirty) {
        watcher.evaluate();//收集該watcher的訂閱
      }
      if (Dep.target) {
        watcher.depend();//同一爲這一組訂閱再加上組件re-render的訂閱(該訂閱負責更新組件)
      }
      return watcher.value
    }
  }
}

總的來講。理解了響應式的構建以後。再來看computed的實現仍是很直觀的。組件初始化的時候。computed項和data中的分別創建響應式。data中的數據直接對屬性的get,set作數據攔截。而computed則創建一個新的watcher,在組件渲染的時候。先touch一下這個computed的getter函數。將這個watcher訂閱起來。這裏至關於這個computed的watcher訂閱了firstname和lastname。touch完後。Dep.target此時又變爲以前那個用於更新組件的。再經過watcher.depend()將這個組統一加上這個訂閱。這樣一旦firstname和lastname變了。同時會觸發兩個訂閱更新。其中一個即是更新組件。從新re-render的函數。感受看的還不夠細啊

相關文章
相關標籤/搜索