首頁 > 軟體

Vue2 響應式系統之深度響應

2022-04-13 04:00:21

1、場景

import { observe } from "./reactive";
import Watcher from "./watcher";
const data = {
    text: {
        innerText: {
            childText: "hello",
        },
    },
};
observe(data);
const updateComponent = () => {
    console.log(data.text.innerText.childText);
};

new Watcher(updateComponent);
data.text.innerText.childText = "liang";

我們的響應式系統到現在還沒有支援屬性是物件時候的響應,因此我們改變 的時候不會有任何輸出。childText

我們只收集了 的依賴,所以如果想要響應的話必須給 整個賦值為一個新物件。data.textdata.text

import { observe } from "./reactive";
import Watcher from "./watcher";
const data = {
    text: {
        innerText: {
            childText: "hello",
        },
    },
};
observe(data);
const updateComponent = () => {
    console.log(data.text.innerText.childText);
};

new Watcher(updateComponent);
data.text = {
    innerText: {
        childText: "liang",
    },
};

我們當然不希望每次都賦值整個物件,我們需要做一些修改,把巢狀的物件也變成響應式的。

2、方案

我們只需要在給某個 重寫 和 之前,把它的 就像上邊給 呼叫 函數一樣,也呼叫一次 函數即可。keygetsetvaluedataobserveobserve

同時提供 引數,留下擴充套件,讓外界決定是否需要深度響應。shallow

/*******************新增 shallow*******************/
export function defineReactive(obj, key, val, shallow) {
/****************************************************/
    const property = Object.getOwnPropertyDescriptor(obj, key);
    // 讀取使用者可能自己定義了的 get、set
    const getter = property && property.get;
    const setter = property && property.set;
    // val 沒有傳進來話進行手動賦值
    if ((!getter || setter) && arguments.length === 2) {
        val = obj[key];
    }
    const dep = new Dep(); // 持有一個 Dep 物件,用來儲存所有依賴於該變數的 Watcher
    /*******************新增****************************/
    !shallow && observe(val);
  	/******************************************************/
    Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get: function reactiveGetter() {
            const value = getter ? getter.call(obj) : val;
            if (Dep.target) {
                dep.depend();
            }
            return value;
        },
        set: function reactiveSetter(newVal) {
            const value = getter ? getter.call(obj) : val;

            if (setter) {
                setter.call(obj, newVal);
            } else {
                val = newVal;
            }
            dep.notify();
        },
    });
}

同時,在 函數中,傳進來的 不是物件的話我們直接 。observevaluereturn

/*
util.js
export function isObject(obj) {
    return obj !== null && typeof obj === "object";
}
*/
export function observe(value) {
    if (!isObject(value)) {
        return;
    }
    let ob = new Observer(value);
    return ob;
}

3、場景2

import { observe } from "./reactive";
import Watcher from "./watcher";
const data = {
    text: {
        innerText: {
            childText: "hello",
        },
    },
};
observe(data);
const updateComponent = () => {
    console.log(data.text.innerText.childText);
};

new Watcher(updateComponent);

data.text.innerText.childText = "liang";
data.text = {
    innerText: {
        childText: "liang2",
    },
};

data.text.innerText.childText = "liang3";

可以一分鐘想一下上邊會輸出什麼。

new Watcher(updateComponent); ,執行一次 輸出 。updateComponenthello

data.text.innerText.childText = "liang"; ,我們已經解決了屬性是物件的情況,因此這裡也會輸出 。liang

data.text = {
    innerText: {
        childText: "liang2",
    },
};

上邊程式碼就是文章最開頭的方法,因此也會觸發函數執行,輸出 。liang2

data.text.innerText.childText = "liang3"; 最後這句會執行嗎?

答案是否定的了,因為我們的 賦值為了一個新物件,但這個新物件我們並沒有將其設定為響應式的。data.text

因此我們需要在 的時候把物件也設定為響應式的。set

/**
 * Define a reactive property on an Object.
 */
export function defineReactive(obj, key, val, shallow) {
    const property = Object.getOwnPropertyDescriptor(obj, key);
    // 讀取使用者可能自己定義了的 get、set
    const getter = property && property.get;
    const setter = property && property.set;
    // val 沒有傳進來話進行手動賦值
    if ((!getter || setter) && arguments.length === 2) {
        val = obj[key];
    }
    const dep = new Dep(); // 持有一個 Dep 物件,用來儲存所有依賴於該變數的 Watcher

    let childOb = !shallow && observe(val);
    Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get: function reactiveGetter() {
            const value = getter ? getter.call(obj) : val;
            if (Dep.target) {
                dep.depend();
            }
            return value;
        },
        set: function reactiveSetter(newVal) {
            const value = getter ? getter.call(obj) : val;

            if (setter) {
                setter.call(obj, newVal);
            } else {
                val = newVal;
            }
          /******新增 *************************/
            childOb = !shallow && observe(newVal);
           /************************************/
            dep.notify();
        },
    });
}

4、總結

通過遞迴解決了屬性是物件的依賴,可以為未來陣列的依賴留下基礎。

到此這篇關於Vue2 響應式系統之深度響應的文章就介紹到這了,更多相關Vue2 深度響應內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com