91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

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

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

Vue2響應式系統之分支切換怎么實現

發布時間:2022-04-13 10:24:28 來源:億速云 閱讀:211 作者:iii 欄目:開發技術

本篇內容介紹了“Vue2響應式系統之分支切換怎么實現”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

    場景

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

    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 執行一次函數,輸出 hello, world
    
    data.ok = false; // updateComponent 執行一次函數,輸出 not
    
    data.text = "hello, liang"; // updateComponent 會執行嗎?

    我們來一步一步理清:

    observer(data)

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

    Vue2響應式系統之分支切換怎么實現

    new Watcher(updateComponent)

    這一步會執行 函數,執行過程中用到的所有對象屬性,會將 收集到相應對象屬性中的 中。updateComponentWatcherDep

    Vue2響應式系統之分支切換怎么實現

    當然這里的 其實是同一個,所以用了指向的箭頭。Watcher

    data.ok = false

    這一步會觸發 ,從而執行 中所有的 ,此時就會執行一次 。setDepWatcherupdateComponent

    執行 就會重新讀取 中的屬性,觸發 ,然后繼續收集 。updateComponentdatagetWatcher

    Vue2響應式系統之分支切換怎么實現

    重新執行 函數 的時候:updateComponent

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

    因為 的值變為 ,所以就不會觸發 的 , 的 就不會變化了。data.okfalsedata.textgettextDep

    而 會繼續執行,觸發 收集 ,但由于我們 中使用的是數組,此時收集到的兩個 其實是同一個,這里是有問題,會導致 重復執行,一會兒我們來解決下。data.okgetWatcherDepWacherupdateComponent

    data.text = "hello, liang"

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

    之所以執行了,是因為第一次執行 讀取了 從而收集了 ,第二次執行 的時候, 雖然沒有讀到,但之前的 也沒有清除掉,所以這一次改變 的時候 依舊會執行。updateComponentdata.textWatcherupdateComponentdata.textWatcherdata.textupdateComponent

    所以我們需要的就是當重新執行 的時候,如果 已經不依賴于某個 了,我們需要將當前 從該 中移除掉。updateComponentWatcherDepWatcherDep

    Vue2響應式系統之分支切換怎么實現

    問題

    總結下來我們需要做兩件事情。

    • 去重, 中不要重復收集 。DepWatcher

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

    去重

    去重的話有兩種方案:

    • Dep 中的 數組換為 。subsSet

    • 每個 對象引入 , 對象中記錄所有的 的 ,下次重新收集依賴的時候,如果 的 已經存在,就不再收集該 了。DepidWatcherDepidDepidWatcher

    Vue2 源碼中采用的是方案 這里我們實現下:2

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

    /*************改動***************************/
    let uid = 0;
    /****************************************/
    export default class Dep {
        static target; //當前在執行的函數
        subs; // 依賴的函數
      	id; // Dep 對象標識
        constructor() {
          /**************改動**************************/
            this.id = uid++;
          /****************************************/
            this.subs = []; // 保存所有需要執行的函數
        }
    
        addSub(sub) {
            this.subs.push(sub);
        }
        depend() {
            if (Dep.target) {
                // 委托給 Dep.target 去調用 addSub
                Dep.target.addDep(this);
            }
        }
    
        notify() {
            for (let i = 0, l = this.subs.length; i < l; i++) {
                this.subs[i].update();
            }
        }
    }
    
    Dep.target = null; // 靜態變量,全局唯一

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

    import Dep from "./dep";
    export default class Watcher {
      constructor(Fn) {
        this.getter = Fn;
        /*************改動***************************/
        this.depIds = new Set(); // 擁有 has 函數可以判斷是否存在某個 id
        /****************************************/
        this.get();
      }
    
      /**
         * Evaluate the getter, and re-collect dependencies.
         */
      get() {
        Dep.target = this; // 保存包裝了當前正在執行的函數的 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;
        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();
      }
    }

    重置

    同樣是兩個方案:

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

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

    Vue2 中采用的是方案 ,這里也實現下。2

    首先是 類,我們需要提供一個 方法。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; //當前在執行的函數
        subs; // 依賴的函數
        id; // Dep 對象標識
        constructor() {
            this.id = uid++;
            this.subs = []; // 保存所有需要執行的函數
        }
    		
        addSub(sub) {
            this.subs.push(sub);
        }
      /*************新增************************/
        removeSub(sub) {
            remove(this.subs, sub);
        }
      /****************************************/
        depend() {
            if (Dep.target) {
                // 委托給 Dep.target 去調用 addSub
                Dep.target.addDep(this);
            }
        }
    
        notify() {
            for (let i = 0, l = this.subs.length; i < l; i++) {
                this.subs[i].update();
            }
        }
    }
    
    Dep.target = null; // 靜態變量,全局唯一

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

    import Dep from "./dep";
    export default class Watcher {
        constructor(Fn) {
            this.getter = Fn;
            this.depIds = new Set(); // 擁有 has 函數可以判斷是否存在某個 id
          	/*************新增************************/
            this.deps = [];
            this.newDeps = []; // 記錄新一次的依賴
            this.newDepIds = new Set();
          	/****************************************/
            this.get();
        }
    
        /**
         * Evaluate the getter, and re-collect dependencies.
         */
        get() {
            Dep.target = this; // 保存包裝了當前正在執行的函數的 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;
          /*************新增************************/
            // 新的依賴已經存在的話,同樣不需要繼續保存
            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;
            // 比對新舊列表,找到舊列表里有,但新列表里沒有,來移除相應 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 執行一次函數,輸出 hello, world
    
    data.ok = false; // updateComponent 執行一次函數,輸出 not
    
    data.text = "hello, liang"; // updateComponent 會執行嗎?

    此時 修改的話就不會再執行 了,因為第二次執行的時候,我們把 中 里的 清除了。data.textupdateComponentdata.textDepWatcher

    “Vue2響應式系統之分支切換怎么實現”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    vue
    AI

    浪卡子县| 富民县| 上栗县| 东安县| 托克逊县| 襄汾县| 澳门| 呈贡县| 尉犁县| 静乐县| 长阳| 北流市| 图们市| 鄄城县| 石林| 萝北县| 六盘水市| 永康市| 瓮安县| 大宁县| 德兴市| 长武县| 铁岭县| 松阳县| 南丹县| 乌什县| 杂多县| 大同县| 呼和浩特市| 家居| 卢湾区| 永春县| 新昌县| 商丘市| 海阳市| 凭祥市| 龙口市| 依安县| 金塔县| 红原县| 西峡县|