1. 程式人生 > 其它 >vue的computed實現原理

vue的computed實現原理

1.每個 computed 屬性都會生成對應的觀察者(Watcher 例項),觀察者存在 values 屬性和 get 方法。computed 屬性的 getter 函式會在 get 方法中呼叫,並將返回值賦值給 value。初始設定 dirty 和 lazy 的值為 true,lazy 為 true 不會立即 get 方法(懶執行),而是會在讀取 computed 值時執行。

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 函式

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 值,並返回,完成頁面的渲染。

5.dirty 值初始為 true,即首次讀取 computed 屬性值時,根據 setter 計算屬性值,並儲存在觀察者 value 上,然後設定 dirty 值為 false。之後讀取 computed 屬性值時,dirty 值為 false,不呼叫 setter 重新計算值,而是直接返回觀察者的 value,也就是上一次計算值。只有當 computed 屬性 setter 函式依賴的 data 發生變化時,才設定 dirty 為 true,即下一次讀取 computed 屬性值時呼叫 setter 重新計算。也就是說,computed 屬性依賴的 data 不發生變化時,不會呼叫 setter 函式重新計算值,而是讀取上一次計算值。

最近面試老是被問computed原理,原始碼又側重看initState方法,initComputed都沒留意看,看了也沒看懂,主要是Dep.target太精妙了。。。

轉載於:https://segmentfault.com/a/1190000022169550