溫馨提示×

溫馨提示×

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

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

Vue2響應(yīng)式系統(tǒng)之分支切換怎么實(shí)現(xiàn)

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

本篇內(nèi)容介紹了“Vue2響應(yīng)式系統(tǒng)之分支切換怎么實(shí)現(xiàn)”的有關(guān)知識(shí),在實(shí)際案例的操作過程中,不少人都會(huì)遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

    場景

    我們考慮一下下邊的代碼會(huì)輸出什么。

    import { observe } from "./reactive";
    import Watcher from "./watcher";
    const data = {
        text: "hello, world",
        ok: true,
    };
    observe(data);
    
    const updateComponent = () => {
        console.log("收到", data.ok ? data.text : "not");
    };
    
    new Watcher(updateComponent); // updateComponent 執(zhí)行一次函數(shù),輸出 hello, world
    
    data.ok = false; // updateComponent 執(zhí)行一次函數(shù),輸出 not
    
    data.text = "hello, liang"; // updateComponent 會(huì)執(zhí)行嗎?

    我們來一步一步理清:

    observer(data)

    攔截了 中 和 的 ,并且各自初始化了一個(gè) 實(shí)例,用來保存依賴它們的 對(duì)象。datatextokget、setDepWatcher

    Vue2響應(yīng)式系統(tǒng)之分支切換怎么實(shí)現(xiàn)

    new Watcher(updateComponent)

    這一步會(huì)執(zhí)行 函數(shù),執(zhí)行過程中用到的所有對(duì)象屬性,會(huì)將 收集到相應(yīng)對(duì)象屬性中的 中。updateComponentWatcherDep

    Vue2響應(yīng)式系統(tǒng)之分支切換怎么實(shí)現(xiàn)

    當(dāng)然這里的 其實(shí)是同一個(gè),所以用了指向的箭頭。Watcher

    data.ok = false

    這一步會(huì)觸發(fā) ,從而執(zhí)行 中所有的 ,此時(shí)就會(huì)執(zhí)行一次 。setDepWatcherupdateComponent

    執(zhí)行 就會(huì)重新讀取 中的屬性,觸發(fā) ,然后繼續(xù)收集 。updateComponentdatagetWatcher

    Vue2響應(yīng)式系統(tǒng)之分支切換怎么實(shí)現(xiàn)

    重新執(zhí)行 函數(shù) 的時(shí)候:updateComponent

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

    因?yàn)?nbsp;的值變?yōu)?nbsp;,所以就不會(huì)觸發(fā) 的 , 的 就不會(huì)變化了。data.okfalsedata.textgettextDep

    而 會(huì)繼續(xù)執(zhí)行,觸發(fā) 收集 ,但由于我們 中使用的是數(shù)組,此時(shí)收集到的兩個(gè) 其實(shí)是同一個(gè),這里是有問題,會(huì)導(dǎo)致 重復(fù)執(zhí)行,一會(huì)兒我們來解決下。data.okgetWatcherDepWacherupdateComponent

    data.text = "hello, liang"

    執(zhí)行這句的時(shí)候,會(huì)觸發(fā) 的 ,所以會(huì)執(zhí)行一次 。但從代碼來看 函數(shù)中由于 為 , 對(duì)輸出沒有任何影響,這次執(zhí)行其實(shí)是沒有必要的。textsetupdateComponentupdateComponentdata.okfalsedata.text

    之所以執(zhí)行了,是因?yàn)榈谝淮螆?zhí)行 讀取了 從而收集了 ,第二次執(zhí)行 的時(shí)候, 雖然沒有讀到,但之前的 也沒有清除掉,所以這一次改變 的時(shí)候 依舊會(huì)執(zhí)行。updateComponentdata.textWatcherupdateComponentdata.textWatcherdata.textupdateComponent

    所以我們需要的就是當(dāng)重新執(zhí)行 的時(shí)候,如果 已經(jīng)不依賴于某個(gè) 了,我們需要將當(dāng)前 從該 中移除掉。updateComponentWatcherDepWatcherDep

    Vue2響應(yīng)式系統(tǒng)之分支切換怎么實(shí)現(xiàn)

    問題

    總結(jié)下來我們需要做兩件事情。

    • 去重, 中不要重復(fù)收集 。DepWatcher

    • 重置,如果該屬性對(duì) 中的 已經(jīng)沒有影響了(換句話就是, 中的 已經(jīng)不會(huì)讀取到該屬性了 ),就將該 從該屬性的 中刪除。DepWacherWatcherupdateComponentWatcherDep

    去重

    去重的話有兩種方案:

    • Dep 中的 數(shù)組換為 。subsSet

    • 每個(gè) 對(duì)象引入 , 對(duì)象中記錄所有的 的 ,下次重新收集依賴的時(shí)候,如果 的 已經(jīng)存在,就不再收集該 了。DepidWatcherDepidDepidWatcher

    Vue2 源碼中采用的是方案 這里我們實(shí)現(xiàn)下:2

    Dep 類的話只需要引入 即可。id

    /*************改動(dòng)***************************/
    let uid = 0;
    /****************************************/
    export default class Dep {
        static target; //當(dāng)前在執(zhí)行的函數(shù)
        subs; // 依賴的函數(shù)
      	id; // Dep 對(duì)象標(biāo)識(shí)
        constructor() {
          /**************改動(dòng)**************************/
            this.id = uid++;
          /****************************************/
            this.subs = []; // 保存所有需要執(zhí)行的函數(shù)
        }
    
        addSub(sub) {
            this.subs.push(sub);
        }
        depend() {
            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)變量,全局唯一

    在 中,我們引入 來記錄所有的 。Watcherthis.depIdsid

    import Dep from "./dep";
    export default class Watcher {
      constructor(Fn) {
        this.getter = Fn;
        /*************改動(dòng)***************************/
        this.depIds = new Set(); // 擁有 has 函數(shù)可以判斷是否存在某個(gè) id
        /****************************************/
        this.get();
      }
    
      /**
         * Evaluate the getter, and re-collect dependencies.
         */
      get() {
        Dep.target = this; // 保存包裝了當(dāng)前正在執(zhí)行的函數(shù)的 Watcher
        let value;
        try {
          value = this.getter.call();
        } catch (e) {
          throw e;
        } finally {
          this.cleanupDeps();
        }
        return value;
      }
    
      /**
         * Add a dependency to this directive.
         */
      addDep(dep) {
        /*************改動(dòng)***************************/
        const id = dep.id;
        if (!this.depIds.has(id)) {
          dep.addSub(this);
        }
        /****************************************/
    
      }
    
      /**
         * Subscriber interface.
         * Will be called when a dependency changes.
         */
      update() {
        this.run();
      }
    
      /**
         * Scheduler job interface.
         * Will be called by the scheduler.
         */
      run() {
        this.get();
      }
    }

    重置

    同樣是兩個(gè)方案:

    • 全量式移除,保存 所影響的所有 對(duì)象,當(dāng)重新收集 的前,把當(dāng)前 從記錄中的所有 對(duì)象中移除。WatcherDepWatcherWatcherDep

    • 增量式移除,重新收集依賴時(shí),用一個(gè)新的變量記錄所有的 對(duì)象,之后再和舊的 對(duì)象列表比對(duì),如果新的中沒有,舊的中有,就將當(dāng)前 從該 對(duì)象中移除。DepDepWatcherDep

    Vue2 中采用的是方案 ,這里也實(shí)現(xiàn)下。2

    首先是 類,我們需要提供一個(gè) 方法。DepremoveSub

    import { remove } from "./util";
    /*
    export function remove(arr, item) {
        if (arr.length) {
            const index = arr.indexOf(item);
            if (index > -1) {
                return arr.splice(index, 1);
            }
        }
    }
    */
    let uid = 0;
    
    export default class Dep {
        static target; //當(dāng)前在執(zhí)行的函數(shù)
        subs; // 依賴的函數(shù)
        id; // Dep 對(duì)象標(biāo)識(shí)
        constructor() {
            this.id = uid++;
            this.subs = []; // 保存所有需要執(zhí)行的函數(shù)
        }
    		
        addSub(sub) {
            this.subs.push(sub);
        }
      /*************新增************************/
        removeSub(sub) {
            remove(this.subs, sub);
        }
      /****************************************/
        depend() {
            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)變量,全局唯一

    然后是 類,我們引入 來保存所有的舊 對(duì)象,引入 來保存所有的新 對(duì)象。Watcherthis.depsDepthis.newDepsDep

    import Dep from "./dep";
    export default class Watcher {
        constructor(Fn) {
            this.getter = Fn;
            this.depIds = new Set(); // 擁有 has 函數(shù)可以判斷是否存在某個(gè) id
          	/*************新增************************/
            this.deps = [];
            this.newDeps = []; // 記錄新一次的依賴
            this.newDepIds = new Set();
          	/****************************************/
            this.get();
        }
    
        /**
         * Evaluate the getter, and re-collect dependencies.
         */
        get() {
            Dep.target = this; // 保存包裝了當(dāng)前正在執(zhí)行的函數(shù)的 Watcher
            let value;
            try {
                value = this.getter.call();
            } catch (e) {
                throw e;
            } finally {
              	/*************新增************************/
                this.cleanupDeps();
              	/****************************************/
            }
            return value;
        }
    
        /**
         * Add a dependency to this directive.
         */
        addDep(dep) {
            const id = dep.id;
          /*************新增************************/
            // 新的依賴已經(jīng)存在的話,同樣不需要繼續(xù)保存
            if (!this.newDepIds.has(id)) {
                this.newDepIds.add(id);
                this.newDeps.push(dep);
                if (!this.depIds.has(id)) {
                    dep.addSub(this);
                }
            }
          /****************************************/
        }
    
        /**
         * Clean up for dependency collection.
         */
      	/*************新增************************/
        cleanupDeps() {
            let i = this.deps.length;
            // 比對(duì)新舊列表,找到舊列表里有,但新列表里沒有,來移除相應(yīng) Watcher
            while (i--) {
                const dep = this.deps[i];
                if (!this.newDepIds.has(dep.id)) {
                    dep.removeSub(this);
                }
            }
    
            // 新的列表賦值給舊的,新的列表清空
            let tmp = this.depIds;
            this.depIds = this.newDepIds;
            this.newDepIds = tmp;
            this.newDepIds.clear();
            tmp = this.deps;
            this.deps = this.newDeps;
            this.newDeps = tmp;
            this.newDeps.length = 0;
        }
      	/****************************************/
        /**
         * Subscriber interface.
         * Will be called when a dependency changes.
         */
        update() {
            this.run();
        }
    
        /**
         * Scheduler job interface.
         * Will be called by the scheduler.
         */
        run() {
            this.get();
        }
    }

    測試

    回到開頭的代碼

    import { observe } from "./reactive";
    import Watcher from "./watcher";
    const data = {
        text: "hello, world",
        ok: true,
    };
    observe(data);
    
    const updateComponent = () => {
        console.log("收到", data.ok ? data.text : "not");
    };
    
    new Watcher(updateComponent); // updateComponent 執(zhí)行一次函數(shù),輸出 hello, world
    
    data.ok = false; // updateComponent 執(zhí)行一次函數(shù),輸出 not
    
    data.text = "hello, liang"; // updateComponent 會(huì)執(zhí)行嗎?

    此時(shí) 修改的話就不會(huì)再執(zhí)行 了,因?yàn)榈诙螆?zhí)行的時(shí)候,我們把 中 里的 清除了。data.textupdateComponentdata.textDepWatcher

    “Vue2響應(yīng)式系統(tǒng)之分支切換怎么實(shí)現(xiàn)”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

    向AI問一下細(xì)節(jié)

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

    vue
    AI