溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Vue2響應(yīng)式系統(tǒng)有什么用

發(fā)布時間:2022-04-13 10:23:59 來源:億速云 閱讀:136 作者:iii 欄目:開發(fā)技術(shù)

這篇文章主要講解了“Vue2響應(yīng)式系統(tǒng)有什么用”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“Vue2響應(yīng)式系統(tǒng)有什么用”吧!

一、響應(yīng)式系統(tǒng)要干什么

回到最簡單的代碼:

data = {
    text: 'hello, world'
}

const updateComponent = () => {
    console.log('收到', data.text);
}

updateComponent()

data.text = 'hello, liang'
// 運行結(jié)果
// 收到 hello, world

響應(yīng)式系統(tǒng)要做的事情:某個依賴了 數(shù)據(jù)的函數(shù),當(dāng)所依賴的 數(shù)據(jù)改變的時候,該函數(shù)要重新執(zhí)行。datadata

我們期望的效果:當(dāng)上邊 修改的時候, 函數(shù)再執(zhí)行一次。data.textupdateComponent

為了實現(xiàn)響應(yīng)式系統(tǒng),我們需要做兩件事情:

  • 知道 中的數(shù)據(jù)被哪些函數(shù)依賴data

  • data中的數(shù)據(jù)改變的時候去調(diào)用依賴它的函數(shù)們

為了實現(xiàn)第 點,我們需要在執(zhí)行函數(shù)的時候,將當(dāng)前函數(shù)保存起來,然后在讀取數(shù)據(jù)的時候?qū)⒃摵瘮?shù)保存到當(dāng)前數(shù)據(jù)中。1

第 點就迎刃而解了,當(dāng)修改數(shù)據(jù)的時候?qū)⒈4嫫饋淼暮瘮?shù)執(zhí)行一次即可。2

讀取數(shù)據(jù)修改數(shù)據(jù)的時候需要做額外的事情,我們可以通過 重寫對象屬性的 和 函數(shù)。Object.defineProperty()getset

二、響應(yīng)式數(shù)據(jù)

我們來寫一個函數(shù),重寫屬性的 和 函數(shù)。getset

/**
 * Define a reactive property on an Object.
 */
export function defineReactive(obj, key, val) {
    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];
    }

    Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get: function reactiveGetter() {
            const value = getter ? getter.call(obj) : val;
            /*********************************************/
            // 1.這里需要去保存當(dāng)前在執(zhí)行的函數(shù)
            /*********************************************/
            return value;
        },
        set: function reactiveSetter(newVal) {
            const value = getter ? getter.call(obj) : val;

            if (setter) {
                setter.call(obj, newVal);
            } else {
                val = newVal;
            }
            /*********************************************/
            // 2.將依賴當(dāng)前數(shù)據(jù)依賴的函數(shù)執(zhí)行
            /*********************************************/
        },
    });
}

為了調(diào)用更方便,我們把第 步和第 步的操作封裝一個 類。12Dep

export default class Dep {
    static target; //當(dāng)前在執(zhí)行的函數(shù)
    subs; // 依賴的函數(shù)
    constructor() {
        this.subs = []; // 保存所有需要執(zhí)行的函數(shù)
    }

    addSub(sub) {
        this.subs.push(sub);
    }

    depend() {
        // 觸發(fā) get 的時候走到這里
        if (Dep.target) {
            // 委托給 Dep.target 去調(diào)用 addSub
            Dep.target.addDep(this);
        }
    }

    notify() {
        for (let i = 0, l = this.subs.length; i < l; i++) {
            this.subs[i].update();
        }
    }
}

Dep.target = null; // 靜態(tài)變量,全局唯一

我們將當(dāng)前執(zhí)行的函數(shù)保存到 類的 變量上。Deptarget

三、保存當(dāng)前正在執(zhí)行的函數(shù)

為了保存當(dāng)前的函數(shù),我們還需要寫一個 類,將需要執(zhí)行的函數(shù)傳入,保存到 類中的 屬性中,然后交由 類負責(zé)執(zhí)行。WatcherWatchergetterWatcher

這樣在 類中, 中保存的就不是當(dāng)前函數(shù)了,而是持有當(dāng)前函數(shù)的 對象。DepsubsWatcher

import Dep from "./dep";
export default class Watcher {
    constructor(Fn) {
        this.getter = Fn;
        this.get();
    }

    /**
     * Evaluate the getter, and re-collect dependencies.
     */
    get() {
        Dep.target = this; // 保存包裝了當(dāng)前正在執(zhí)行的函數(shù)的 Watcher
        let value;
        try {
          	// 調(diào)用當(dāng)前傳進來的函數(shù),觸發(fā)對象屬性的 get
            value = this.getter.call();
        } catch (e) {
            throw e;
        }
        return value;
    }

    /**
     * Add a dependency to this directive.
     */
    addDep(dep) {
      	// 觸發(fā) get 后會走到這里,收集當(dāng)前依賴
        // 當(dāng)前正在執(zhí)行的函數(shù)的 Watcher 保存到 dep 中的 subs 中
        dep.addSub(this);
    }

    /**
     * Subscriber interface.
     * Will be called when a dependency changes.
     */
  	// 修改對象屬性值的時候觸發(fā) set,走到這里
    update() {
        this.run();
    }

    /**
     * Scheduler job interface.
     * Will be called by the scheduler.
     */
    run() {
        this.get();
    }
}

Watcher 的作用就是將正在執(zhí)行的函數(shù)通過 包裝后保存到 中,然后調(diào)用傳進來的函數(shù),此時觸發(fā)對象屬性的 函數(shù),會收集當(dāng)前 。WatcherDep.targetgetWatcher

如果未來修改對象屬性的值,會觸發(fā)對象屬性的 ,接著就會調(diào)用之前收集到的 對象,通過 對象的 方法,來調(diào)用最初執(zhí)行的函數(shù)。setWatcherWatcheruptate

四、響應(yīng)式數(shù)據(jù)

回到我們之前沒寫完的 函數(shù),按照上邊的思路,我們來補全一下。defineReactive

import Dep from "./dep";
/**
 * Define a reactive property on an Object.
 */
export function defineReactive(obj, key, val) {
    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
    /*********************************************/

    Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get: function reactiveGetter() {
            const value = getter ? getter.call(obj) : val;
            /*********************************************/
            // 1.這里需要去保存當(dāng)前在執(zhí)行的函數(shù)
            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;
            }
            /*********************************************/
            // 2.將依賴當(dāng)前數(shù)據(jù)依賴的函數(shù)執(zhí)行
            dep.notify();
            /*********************************************/
        },
    });
}

五、Observer 對象

我們再寫一個 方法,把對象的全部屬性都變成響應(yīng)式的。Observer

export class Observer {
    constructor(value) {
        this.walk(value);
    }

    /**
     * 遍歷對象所有的屬性,調(diào)用 defineReactive
     * 攔截對象屬性的 get 和 set 方法
     */
    walk(obj) {
        const keys = Object.keys(obj);
        for (let i = 0; i < keys.length; i++) {
            defineReactive(obj, keys[i]);
        }
    }
}

我們提供一個 方法來負責(zé)創(chuàng)建 對象。observeObserver

export function observe(value) {
    let ob = new Observer(value);
    return ob;
}

六、測試

將上邊的方法引入到文章最開頭的例子,來執(zhí)行一下:

import { observe } from "./reactive";
import Watcher from "./watcher";
const data = {
    text: "hello, world",
};
// 將數(shù)據(jù)變成響應(yīng)式的
observe(data);

const updateComponent = () => {
    console.log("收到", data.text);
};

// 當(dāng)前函數(shù)由 Watcher 進行執(zhí)行
new Watcher(updateComponent);

data.text = "hello, liang";

此時就會輸出兩次了~

收到 hello, world
收到 hello, liang

說明我們的響應(yīng)式系統(tǒng)成功了。

感謝各位的閱讀,以上就是“Vue2響應(yīng)式系統(tǒng)有什么用”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對Vue2響應(yīng)式系統(tǒng)有什么用這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關(guān)知識點的文章,歡迎關(guān)注!

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

vue
AI