Vue源碼解析(三)-computed計算屬性&&lazy watcher

前言

一、Vue源碼解析(一)-模版渲染
二、Vue源碼解析(二)-MVVM雙向綁定vue

demo

官網給出的demo以下segmentfault

<div id="app"></div>

new Vue({
  el: '#app',
  template: 
  `<div>
    <p>Original message is: {{ message }}</p>
    <p>Computed reversed message:: {{ reversedMessage }}</p>
  </div>`,
  data(){
    return {
      message: 'Hello',
    }
  },
  computed:{
    reversedMessage(){
        return this.message.split('').reverse().join('')
    }
  }
})
結果:
Original message: "Hello"
Computed reversed message: "olleH"

源碼分析

//判斷參數是否包含computed屬性
if (opts.computed) { initComputed(vm, opts.computed); }

function initComputed (vm, computed) {
   var watchers = vm._computedWatchers = Object.create(null);
   //本例中key=‘reversedMessage’
   for (var key in computed) {
      //本例中userDef和getter是reversedMessage函數
      var userDef = computed[key];
      var getter = typeof userDef === 'function' ? userDef : userDef.get;
      //監聽計算屬性,設置lazy=true,延遲執行watcher的get方法
      watchers[key] = new Watcher(vm,getter,{lazy:true});
      //設置能夠經過vm[key](本例vm.reversedMessage)方式訪問計算屬性
      defineComputed(vm, key, userDef);
   }
}

一、vue對象初始化時會針對computed屬性的全部key值分別new一個watcher對象,在Vue源碼解析(二)中有詳細介紹watcher的原理,當時提到watcher初始化會當即調用一次watcher.get方法,而後實際上能夠經過傳入{lazy:true}參數來延遲watcher.get方法的執行緩存

var Watcher = function Watcher (vm,expOrFn,options){
    //延遲計算
    this.lazy = options.lazy;
    //尚未計算,因此數據是髒的
    this.dirty = options.lazy;
    this.value = this.lazy
    ? undefined
    //計算getter值和收集依賴
    : this.get();
}

二、defineComputed(vm, key, userDef),將computed屬性代理到vm上,經過vm[key]訪問computed屬性值app

function defineComputed (target,key,userDef){
    //userDef是function,getter設爲userDef或userDef的值
    if (typeof userDef === 'function') {
        //shouldCache是否緩存,這也是使用computed屬性最重要的緣由,computed值會被緩存起來,而不是每次從新執行函數生成
        sharedPropertyDefinition.get = shouldCache
          ? createComputedGetter(key)
          : userDef;
        sharedPropertyDefinition.set = null;
    //userDef是否是function,getter設爲userDef.get,setter設爲userDef.set
    } else {
        sharedPropertyDefinition.get = userDef.get
          ? shouldCache && userDef.cache !== false
            ? createComputedGetter(key)
            : userDef.get
          : null;
        sharedPropertyDefinition.set = userDef.set
          ? userDef.set
          : null;
    }
    //,將computed屬性代理到vm上,經過vm[key]訪問computed屬性值
    Object.defineProperty(target, key, sharedPropertyDefinition);
}

function createComputedGetter (key) {
  return function computedGetter () {
      //shouldCache = true時直接返回緩存值watcher.value
      var watcher = this._computedWatchers && this._computedWatchers[key];
      //存在髒數據則從新計算watcher的值
      if (watcher.dirty) {
        watcher.evaluate();
      }
      //直接返回緩存中watcher的值
      return watcher.value
    }
  }
}

三、前面提到watcher.get方法會延遲執行,那麼到底啥時執行呢?這又得提到Vue源碼解析(二)中的updateComponent方法,因爲本例引用了計算屬性{{ reversedMessage }},updateComponent中的render函數則會調用vm.reversedMessage,所以觸發第二步的sharedPropertyDefinition.get函數,調用 watcher.evaluate(),最終調用watcher.get()來計算watcher的值和收集依賴。(watcher.get方法將監聽vm.reversedMessage的watcher對象和發佈vm.message變化的dep對象綁定,所以當vm.message變化時,vm.reversedMessage值也會同步變化)
所以watcher.get是在第一次訪問vm.reversedMessage對象時調用的,因此若是模版沒有用到{{ reversedMessage }}值的話vm.reversedMessage的值是不會被計算的函數

/**
 * Evaluate the value of the watcher.
 * This only gets called for lazy watchers.
 */
Watcher.prototype.evaluate = function evaluate () {
  this.value = this.get();
  this.dirty = false;
};

雙向綁定問題

正好以前看到過一個問題vue.js使用computed計算某個屬性後,該屬性的雙向綁定沒了,看了本文的源碼後你們應該瞭解了計算屬性用在v-model上應該設置setter方法,例如本例中demo應該這麼寫:源碼分析

new Vue({
  el: '#app',
  template: 
  `<div>
    <input v-model="reversedMessage" placeholder="edit me">
    <p>Original message is: {{ message }}</p>
    <p>Computed reversed message:: {{ reversedMessage }}</p>
  </div>`,
  data(){
    return {
      message: 'jixiangwu',
    }
  },
  computed:{
    reversedMessage:{
        get(){
            return this.message.split('').reverse().join('')
        },
        set(val){
            this.message = val.split('').reverse().join('')
        }
    }
  }
})
相關文章
相關標籤/搜索