欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Vue2?響應(yīng)式系統(tǒng)之分支切換

 更新時(shí)間:2022年04月12日 20:39:09   作者:windliang  
這篇文章主要介紹了Vue2?響應(yīng)式系統(tǒng)之分支切換,文章圍繞Vue2的相關(guān)資料展開主題詳細(xì)內(nèi)容,具有一定的參考價(jià)值,需要的小伙伴可以參考一下

場景

我們考慮一下下邊的代碼會(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í)例,用來保存依賴它們的 對象。datatextokget、setDepWatcher

image-20220331073954801

new Watcher(updateComponent)

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

image-20220331074904131

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

data.ok = false

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

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

image-20220331080258402

重新執(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ù)中由于 為 , 對輸出沒有任何影響,這次執(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

image-20220331081754535

問題

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

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

去重

去重的話有兩種方案:

  • Dep 中的 數(shù)組換為 。subsSet
  • 每個(gè) 對象引入 , 對象中記錄所有的 的 ,下次重新收集依賴的時(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 對象標(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è)方案:

  • 全量式移除,保存 所影響的所有 對象,當(dāng)重新收集 的前,把當(dāng)前 從記錄中的所有 對象中移除。WatcherDepWatcherWatcherDep
  • 增量式移除,重新收集依賴時(shí),用一個(gè)新的變量記錄所有的 對象,之后再和舊的 對象列表比對,如果新的中沒有,舊的中有,就將當(dāng)前 從該 對象中移除。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 對象標(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)變量,全局唯一

然后是 類,我們引入 來保存所有的舊 對象,引入 來保存所有的新 對象。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;
        // 比對新舊列表,找到舊列表里有,但新列表里沒有,來移除相應(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

總結(jié)

今天這個(gè)主要就是對響應(yīng)式系統(tǒng)的一點(diǎn)優(yōu)化,避免不必要的重新執(zhí)行。所做的事情就是重新調(diào)用函數(shù)的時(shí)候,把已經(jīng)沒有關(guān)聯(lián)的 去除。Watcher

不知道看到這里大家有沒有一個(gè)疑問,我是一直沒想到說服我的點(diǎn),歡迎一起交流:

在解決去重問題上,我們是引入了 ,但如果直接用 其實(shí)就可以。在 類中是用 來存 ,用數(shù)組來存 對象,為什么不直接用 來存 對象呢?idsetWatcherSetidDepSetDep

到此這篇關(guān)于Vue2 響應(yīng)式系統(tǒng)之分支切換的文章就介紹到這了,更多相關(guān)Vue2分支切換內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • vue實(shí)現(xiàn)搜索并高亮文字的兩種方式總結(jié)

    vue實(shí)現(xiàn)搜索并高亮文字的兩種方式總結(jié)

    在做文字處理的項(xiàng)目時(shí)經(jīng)常會(huì)遇到搜索文字并高亮的需求,常見的實(shí)現(xiàn)方式有插入標(biāo)簽和貼標(biāo)簽兩種,這兩種方式適用于不同的場景,各有優(yōu)劣,下面我們就來看看他們的具體實(shí)現(xiàn)吧
    2023-11-11
  • vue封裝axios的幾種方法

    vue封裝axios的幾種方法

    在vue中最常用的應(yīng)該就是axios了,這是一個(gè)很強(qiáng)大的處理ajax的庫。今天我就分享一下我是如何封裝axios的。axios的基本api不再贅述,提前安裝一下也不用我說了吧
    2021-06-06
  • mpvue中配置vuex并持久化到本地Storage圖文教程解析

    mpvue中配置vuex并持久化到本地Storage圖文教程解析

    這篇文章主要介紹了mpvue中配置vuex并持久化到本地Storage的教程詳解,# 配置vuex和在vue中相同,只是mpvue有一個(gè)坑,就是不能直接在new Vue的時(shí)候傳入store。本文分步驟給大家介紹的非常詳細(xì),需要的朋友參考下吧
    2018-03-03
  • 最新評論