1.每一個 computed 屬性都會生成對應的觀察者(Watcher 實例),觀察者存在 values 屬性和 get 方法。computed 屬性的 getter 函數會在 get 方法中調用,並將返回值賦值給 value。初始設置 dirty 和 lazy 的值爲 true,lazy 爲 true 不會當即 get 方法(懶執行),而是會在讀取 computed 值時執行。javascript
function initComputed (vm, computed) { var watchers = vm._computedWatchers = Object.create(null);// 存放computed的觀察者 var isSSR = isServerRendering(); for (var key in computed) { var userDef = computed[key]; var getter = typeof userDef === 'function' ? userDef : userDef.get; ... watchers[key] = new Watcher(// 生成觀察者(Watcher實例) vm, getter || noop,// getter將在觀察者get方法中執行 noop, computedWatcherOptions // { lazy: true }懶執行,暫不執行get方法,當讀取computed屬性值執行 ); ... defineComputed(vm, key, userDef); ... } }
2.將 computed 屬性添加到組件實例上,並經過 get、set 獲取或者設置屬性值,而且重定義 getter 函數,java
function defineComputed(target, key, userDef) { var shouldCache = !isServerRendering(); ... sharedPropertyDefinition.get = shouldCache // 重定義getter函數 ? createComputedGetter(key) : createGetterInvoker(userDef); ... Object.defineProperty(target, key, sharedPropertyDefinition); // 將computed屬性添加到組件實例上 }
// 重定義的getter函數 function createComputedGetter(key) { return function computedGetter() { var watcher = this._computedWatchers && this._computedWatchers[key]; if (watcher) { if (watcher.dirty) { // true,懶執行 watcher.evaluate(); // 執行watcher方法後設置dirty爲false } if (Dep.target) { watcher.depend(); } return watcher.value; //返回觀察者的value值 } }; }
3.頁面初始渲染時,讀取 computed 屬性值,觸發重定義後的 getter 函數。因爲觀察者的 dirty 值爲 true,將會調用 get 方法,執行原始 getter 函數。getter 函數中會讀取 data(響應式)數據,讀取數據時會觸發 data 的 getter 方法,會將 computed 屬性對應的觀察者添加到 data 的依賴收集器中(用於 data 變動時通知更新)。觀察者的 get 方法執行完成後,更新觀察者的 value 值,並將 dirty 設置爲 false,表示 value 值已更新,以後在執行觀察者的 depend 方法,將上層觀察者(該觀察者包含頁面更新的方法,方法中讀取了 computed 屬性值)也添加到 getter 函數中 data 的依賴收集器中(getter 中的 data 的依賴器收集器包含 computed 對應的觀察者,以及包含頁面更新方法(調用了 computed 屬性)的觀察者),最後返回 computed 觀察者的 value 值。函數
4.當更改了 computed 屬性 getter 函數依賴的 data 值時,將會根據以前依賴收集的觀察者,依次調用觀察者的 update 方法,先調用 computed 觀察者的 update 方法,因爲 lazy 爲 true,將會設置觀察者的 dirty 爲 true,表示 computed 屬性 getter 函數依賴的 data 值發生變化,但不調用觀察者的 get 方法更新 value 值。再調用包含頁面更新方法的觀察者的 update 方法,在更新頁面時會讀取 computed 屬性值,觸發重定義的 getter 函數,此時因爲 computed 屬性的觀察者 dirty 爲 true,調用該觀察者的 get 方法,更新 value 值,並返回,完成頁面的渲染。oop
5.dirty 值初始爲 true,即首次讀取 computed 屬性值時,根據 setter 計算屬性值,並保存在觀察者 value 上,而後設置 dirty 值爲 false。以後讀取 computed 屬性值時,dirty 值爲 false,不調用 setter 從新計算值,而是直接返回觀察者的 value,也就是上一次計算值。只有當 computed 屬性 setter 函數依賴的 data 發生變化時,才設置 dirty 爲 true,即下一次讀取 computed 屬性值時調用 setter 從新計算。也就是說,computed 屬性依賴的 data 不發生變化時,不會調用 setter 函數從新計算值,而是讀取上一次計算值。this