vue之initComputed模塊源碼說明

要想理解原理就得看源碼,最近網上也找了好多vue初始化方法(8個init惡魔。。。)

由於也是按部就班的理解,對initComputed計算屬性的初始化有幾處看得不是很明白,網上也都是含糊其辭的(要想深刻必須深刻。。。),因此debug了好幾天,纔算是有點頭緒,如今寫出來即幫本身再次理下思路,也可讓大佬指出錯誤 html

首先,基本的雙向綁定原理就不說了,能夠去搜下相關教程,仍是要先理解下簡單的例子vue

進入正題,先來看下initComputed的源碼結構,這以前仍是先放一個例子也好說明

圖片描述

function initComputed (vm, computed) {
  console.log('%cinitComputed','font-size:20px;border:1px solid black')
  var watchers = vm._computedWatchers = Object.create(null);
  // computed properties are just getters during SSR
  var isSSR = isServerRendering();

  for (var key in computed) {
    var userDef = computed[key];
    var getter = typeof userDef === 'function' ? userDef : userDef.get;
    if ("development" !== 'production' && getter == null) {
      warn(
        ("Getter is missing for computed property \"" + key + "\"."),
        vm
      );
    }
    //minxing---console
    console.log('%cinitComputed 定義watchers[key]=new Watcher(vm getter noop computedWatcherOptions)','color:white;padding:5px;background:black');
   
    if (!isSSR) {
      // create internal watcher for the computed property.
      /**
      * 熟悉的newWatcher,建立一個訂閱者,爲了以後收集依賴
      * 將例子中的num、lastNum和計算屬性comNum進行綁定
      * 也就是說在一個deps中有兩個dep,其中的subs分別是
      * dep1.subs:[watcher(num),watcher(comNum)]
      * dep2.subs:[watcher(lastNum),watcher(comNum)]
      * dep3........
      * 請看前面的例子,頁面html中並無渲染{{lastNum}};按理說就不會執行lastNum的getter
      * 從而就不會和計算屬性進行關聯綁定,若是更改lastNum就不會觸發dep2的notify()發佈
      * 天然也就不會在頁面看到comNum有所變化,可是運行後卻不是這樣,爲何呢
      * 這就引出這個initComputed的下面方法了---依賴收集(watcher.prototype.depend)!
      * 當時也是看了很久才知道這個depend方法的做用,後面再說
      * 首先先來提個頭,就是下面代碼中watcher中這個getter
      * 其實就是function comNum() {return this.num+"-computed-"+this.lastNum;}}
      * 而這個getter何時執行呢,會在Watcher.prototype.evaluate()方法中執行
      * 因此watcher中的evaluate()與depend()兩個方法都與initComputed相關
      */
      watchers[key] = new Watcher(
        vm,
        getter || noop,
        noop,
        computedWatcherOptions
      );
    }

    // component-defined computed properties are already defined on the
    // component prototype. We only need to define computed properties defined
    // at instantiation here.
    // 通過判斷後定義計算屬性---(關聯到vm的data上面)
    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);
      }
    }
  }
}

defineComputed方法

這個方法比較簡單主要就是將計算屬性綁定到vm上,重要的下面的createComputedGetter方法數組

function defineComputed (
  target,
  key,
  userDef
) {
  console.log('%cdefineComputed','font-size:20px;border:1px solid black')
  var shouldCache = !isServerRendering();
  if (typeof userDef === 'function') {
    sharedPropertyDefinition.get = shouldCache
      ? createComputedGetter(key)
      : userDef;
    sharedPropertyDefinition.set = noop;
  } else {
    sharedPropertyDefinition.get = userDef.get
      ? shouldCache && userDef.cache !== false
        ? createComputedGetter(key)
        : userDef.get
      : noop;
    sharedPropertyDefinition.set = userDef.set
      ? userDef.set
      : noop;
  }
  if ("development" !== 'production' &&
      sharedPropertyDefinition.set === noop) {
    sharedPropertyDefinition.set = function () {
      warn(
        ("Computed property \"" + key + "\" was assigned to but it has no setter."),
        this
      );
    };
  }
  Object.defineProperty(target, key, sharedPropertyDefinition);
}
function createComputedGetter (key) {
  console.log('createComputedGetter key',key);
  return function computedGetter () {
    var watcher = this._computedWatchers && this._computedWatchers[key];
    if (watcher) {
      if (watcher.dirty) {
        watcher.evaluate();
      }
      if (Dep.target) {
        watcher.depend();
      }
      return watcher.value
    }
  }
}

createComputedGetter方法,主要作了兩件事

1 求計算屬性的值---利用上面說的調用watcher.evalute()方法,執行watcher中的getter

2 依賴收集綁定,這點最重要,就是上面說過的若是沒有在html中對計算屬性相關聯的data屬性(lastNum)進行頁面渲染的話,watcher.depend()此方法就會執行這個依賴收集綁定的做用dep.subs[watcher(計算屬性),watcher(計算關聯屬性1),...],這樣的話當你更改lastNum就會觸發對應的dep.notify()方法發佈通知訂閱者執行update,進行數據更新了,而若是將watcher.depend()方法註釋掉,而頁面中將lastNum渲染({{lastNum}}),此時watcher.evalute()會執行watcher.get從而將此計算watcher推入dep.target中,而渲染lastNum執行getter的時候就會將此watcher加入依賴,因此也會將lastNum和計算屬性關聯到dep中

function createComputedGetter (key) {
  console.log('createComputedGetter key',key);
  return function computedGetter () {
    var watcher = this._computedWatchers && this._computedWatchers[key];
    if (watcher) {
      if (watcher.dirty) {
        console.log('createComputedGetter watcher evaluate===========');
        //求值
        watcher.evaluate();
      }
      if (Dep.target) {
        console.log('createComputedGetter watcher depend===========');
        //依賴收集
        watcher.depend();
      }
      console.log('%ccreateComputedGetter watcher.value is','color:blue;font-size:40px',watcher.value);
      return watcher.value
    }
  }
}

爲了更好的說明下,截兩張圖(都是基於最上面的html配置哦)oop

圖組一

註釋掉watcher.depend()方法,此時deps中沒有dep:id4
其實dep:id4在內存中已經定義好了可是沒有加入到deps中(由於沒有進行依賴收集)
而dep:id5和id6就是上面的數組和遞歸數組中元素的dep
圖片描述this

dep:id3 就是
圖片描述
這下是否是很清楚了lua

圖組二

進行依賴收集後的deps
圖片描述spa

綜上,計算屬性基本的原理就是這樣了,主要是本身的理解,有不對的地方還請指出,哎,仍是寫代碼舒服點,打字描述太累。。。

相關文章
相關標籤/搜索