Vue3響應(yīng)式系統(tǒng)如何實(shí)現(xiàn)computed

這篇“Vue3響應(yīng)式系統(tǒng)如何實(shí)現(xiàn)computed”文章的知識(shí)點(diǎn)大部分人都不太理解,所以小編給大家總結(jié)了以下內(nèi)容,內(nèi)容詳細(xì),步驟清晰,具有一定的借鑒價(jià)值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來(lái)看看這篇“Vue3響應(yīng)式系統(tǒng)如何實(shí)現(xiàn)computed”文章吧。

創(chuàng)新互聯(lián)2013年開(kāi)創(chuàng)至今,是專(zhuān)業(yè)互聯(lián)網(wǎng)技術(shù)服務(wù)公司,擁有項(xiàng)目成都網(wǎng)站建設(shè)、成都網(wǎng)站設(shè)計(jì)網(wǎng)站策劃,項(xiàng)目實(shí)施與項(xiàng)目整合能力。我們以讓每一個(gè)夢(mèng)想脫穎而出為使命,1280元銀川做網(wǎng)站,已為上家服務(wù),為銀川各地企業(yè)和個(gè)人服務(wù),聯(lián)系電話(huà):18980820575

首先,我們簡(jiǎn)單回顧一下:

響應(yīng)式系統(tǒng)的核心就是一個(gè) WeakMap --- Map --- Set 的數(shù)據(jù)結(jié)構(gòu)。

Vue3響應(yīng)式系統(tǒng)如何實(shí)現(xiàn)computed

WeakMap 的 key 是原對(duì)象,value 是響應(yīng)式的 Map。這樣當(dāng)對(duì)象銷(xiāo)毀的時(shí)候,對(duì)應(yīng)的 Map 也會(huì)銷(xiāo)毀。

Map 的 key 就是對(duì)象的每個(gè)屬性,value 是依賴(lài)這個(gè)對(duì)象屬性的 effect 函數(shù)的集合 Set。然后用 Proxy 代理對(duì)象的 get 方法,收集依賴(lài)該對(duì)象屬性的 effect 函數(shù)到對(duì)應(yīng) key 的 Set 中。還要代理對(duì)象的 set 方法,修改對(duì)象屬性的時(shí)候調(diào)用所有該 key 的 effect 函數(shù)。

實(shí)現(xiàn) computed

首先,我們把之前的代碼重構(gòu)一下,把依賴(lài)收集和觸發(fā)依賴(lài)函數(shù)的執(zhí)行抽離成 track 和 trigger 函數(shù):

Vue3響應(yīng)式系統(tǒng)如何實(shí)現(xiàn)computed

邏輯還是添加 effect 到對(duì)應(yīng)的 Set,以及觸發(fā)對(duì)應(yīng) Set 里的 effect 函數(shù)執(zhí)行,但抽離出來(lái)清晰多了。

然后繼續(xù)實(shí)現(xiàn) computed。

computed 的使用大概是這樣的:

const value = computed(() => {
    return obj.a + obj.b;
});

對(duì)比下 effect:

effect(() => {
    console.log(obj.a);
});

區(qū)別只是多了個(gè)返回值。

所以我們基于 effect 實(shí)現(xiàn) computed 就是這樣的:

function computed(fn) {
    const value = effect(fn);
   return value
}

當(dāng)然,現(xiàn)在的 effect 是沒(méi)有返回值的,要給它加一下:

Vue3響應(yīng)式系統(tǒng)如何實(shí)現(xiàn)computed

只是在之前執(zhí)行 effect 函數(shù)的基礎(chǔ)上把返回值記錄下來(lái)返回,這個(gè)改造還是很容易的。

現(xiàn)在 computed 就能返回計(jì)算后的值了:

Vue3響應(yīng)式系統(tǒng)如何實(shí)現(xiàn)computed

但是現(xiàn)在數(shù)據(jù)一遍,所有的 effect 都執(zhí)行了,而像 computed 這里的 effect 是沒(méi)必要每次都重新執(zhí)行的,只需要在數(shù)據(jù)變了之后執(zhí)行。

所以我們添加一個(gè) lazy 的 option 來(lái)控制 effect 不立刻執(zhí)行,而是把函數(shù)返回讓用戶(hù)自己執(zhí)行。

Vue3響應(yīng)式系統(tǒng)如何實(shí)現(xiàn)computed

然后 computed 里用 effect 的時(shí)候就添加一個(gè) lazy 的 option,讓 effect 函數(shù)不執(zhí)行,而是返回出來(lái)。

computed 里創(chuàng)建一個(gè)對(duì)象,在 value 的 get 觸發(fā)時(shí)調(diào)用該函數(shù)拿到最新的值:

Vue3響應(yīng)式系統(tǒng)如何實(shí)現(xiàn)computed

我們測(cè)試下:

Vue3響應(yīng)式系統(tǒng)如何實(shí)現(xiàn)computed

可以看到現(xiàn)在 computed 返回值的 value 屬性是能拿到計(jì)算后的值的,并且修改了 obj.a. 之后會(huì)重新執(zhí)行計(jì)算函數(shù),再次拿 value 時(shí)能拿到新的值。

只是多執(zhí)行了一次計(jì)算,這是因?yàn)?obj.a 變的時(shí)候會(huì)執(zhí)行所有的 effect 函數(shù):

Vue3響應(yīng)式系統(tǒng)如何實(shí)現(xiàn)computed

這樣每次數(shù)據(jù)變了都會(huì)重新執(zhí)行 computed 的函數(shù)來(lái)計(jì)算最新的值。

這是沒(méi)有必要的,effect 的函數(shù)是否執(zhí)行應(yīng)該也是可以控制的。所以我們要給它加上調(diào)度的功能:

Vue3響應(yīng)式系統(tǒng)如何實(shí)現(xiàn)computed

可以支持傳入 schduler 回調(diào)函數(shù),然后執(zhí)行 effect 的時(shí)候,如果有 scheduler 就傳給它讓用戶(hù)自己來(lái)調(diào)度,否則才執(zhí)行 effect 函數(shù)。

這樣用戶(hù)就可以自己控制 effect 函數(shù)的執(zhí)行了:

Vue3響應(yīng)式系統(tǒng)如何實(shí)現(xiàn)computed

然后再試一下剛才的代碼:

Vue3響應(yīng)式系統(tǒng)如何實(shí)現(xiàn)computed

可以看到,obj.a 變了之后并沒(méi)有執(zhí)行 effect 函數(shù)來(lái)重新計(jì)算,因?yàn)槲覀兗恿?sheduler 來(lái)自己調(diào)度。這樣就避免了數(shù)據(jù)變了以后馬上執(zhí)行 computed 函數(shù),可以自己控制執(zhí)行。

現(xiàn)在還有一個(gè)問(wèn)題,每次訪問(wèn) res.value 都要計(jì)算:

Vue3響應(yīng)式系統(tǒng)如何實(shí)現(xiàn)computed

能不能加個(gè)緩存呢?只有數(shù)據(jù)變了才需要計(jì)算,否則直接拿之前計(jì)算的值。

當(dāng)然是可以的,加個(gè)標(biāo)記就行:

Vue3響應(yīng)式系統(tǒng)如何實(shí)現(xiàn)computed

scheduler 被調(diào)用的時(shí)候就說(shuō)明數(shù)據(jù)變了,這時(shí)候 dirty 設(shè)置為 true,然后取 value 的時(shí)候就重新計(jì)算,之后再改為 false,下次取 value 就直接拿計(jì)算好的值了。

我們測(cè)試下:

Vue3響應(yīng)式系統(tǒng)如何實(shí)現(xiàn)computed

我們?cè)L問(wèn) computed 值的 value 屬性時(shí),第一次會(huì)重新計(jì)算,后面就直接拿計(jì)算好的值了。

修改它依賴(lài)的數(shù)據(jù)后,再次訪問(wèn) value 屬性會(huì)再次重新計(jì)算,然后后面再訪問(wèn)就又會(huì)直接拿計(jì)算好的值了。

至此,我們完成了 computed 的功能。

但現(xiàn)在的 computed 實(shí)現(xiàn)還有一個(gè)問(wèn)題,比如這樣一段代碼:

let res = computed(() => {
    return obj.a + obj.b;
});
effect(() => {
    console.log(res.value);
});

我們?cè)谝粋€(gè) effect 函數(shù)里用到了 computed 值,按理說(shuō) obj.a 變了,那 computed 的值也會(huì)變,應(yīng)該觸發(fā)所有的 effect 函數(shù)。

但實(shí)際上并沒(méi)有:

Vue3響應(yīng)式系統(tǒng)如何實(shí)現(xiàn)computed

這是為什么呢?

這是因?yàn)榉祷氐?computed 值并不是一個(gè)響應(yīng)式的對(duì)象,需要把它變?yōu)轫憫?yīng)式的,也就是 get 的時(shí)候 track 收集依賴(lài),set 的時(shí)候觸發(fā)依賴(lài)的執(zhí)行:

Vue3響應(yīng)式系統(tǒng)如何實(shí)現(xiàn)computed

我們?cè)僭囈幌拢?/strong>

Vue3響應(yīng)式系統(tǒng)如何實(shí)現(xiàn)computed

現(xiàn)在 computed 值變了就能觸發(fā)依賴(lài)它的 effect 了。至此,我們的 computed 就很完善了。

完整代碼如下:

const data = {
    a: 1,
    b: 2
}
let activeEffect
const effectStack = [];
function effect(fn, options = {}) {
  const effectFn = () => {
      cleanup(effectFn)
      activeEffect = effectFn
      effectStack.push(effectFn);
      const res = fn()
      effectStack.pop()
      activeEffect = effectStack[effectStack.length - 1]
      return res
  }
  effectFn.deps = []
  effectFn.options = options;
  if (!options.lazy) {
    effectFn()
  }
  return effectFn
}
function computed(fn) {
    let value
    let dirty = true
    const effectFn = effect(fn, {
        lazy: true,
        scheduler(fn) {
            if(!dirty) {
                dirty = true
                trigger(obj, 'value');
            }
        }
    });
    const obj = {
        get value() {
            if (dirty) {
                value = effectFn()
                dirty = false
            }
            track(obj, 'value');
            console.log(obj);
            return value
        }
    }
    return obj
}
function cleanup(effectFn) {
    for (let i = 0; i < effectFn.deps.length; i++) {
        const deps = effectFn.deps[i]
        deps.delete(effectFn)
    }
    effectFn.deps.length = 0
}
const reactiveMap = new WeakMap()
const obj = new Proxy(data, {
    get(targetObj, key) {
        track(targetObj, key);

        return targetObj[key]
   },
   set(targetObj, key, newVal) {
        targetObj[key] = newVal

        trigger(targetObj, key)
    }
})
function track(targetObj, key) {
    let depsMap = reactiveMap.get(targetObj)
    if (!depsMap) {
    reactiveMap.set(targetObj, (depsMap = new Map()))
    }
    let deps = depsMap.get(key)
    if (!deps) {
    depsMap.set(key, (deps = new Set()))
    }
    deps.add(activeEffect)
    activeEffect.deps.push(deps);
}
function trigger(targetObj, key) {
    const depsMap = reactiveMap.get(targetObj)
    if (!depsMap) return
    const effects = depsMap.get(key)
    const effectsToRun = new Set(effects)
    effectsToRun.forEach(effectFn => {
        if(effectFn.options.scheduler) {
            effectFn.options.scheduler(effectFn)
        } else {
            effectFn()
        }
    })
}

以上就是關(guān)于“Vue3響應(yīng)式系統(tǒng)如何實(shí)現(xiàn)computed”這篇文章的內(nèi)容,相信大家都有了一定的了解,希望小編分享的內(nèi)容對(duì)大家有幫助,若想了解更多相關(guān)的知識(shí)內(nèi)容,請(qǐng)關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。

當(dāng)前題目:Vue3響應(yīng)式系統(tǒng)如何實(shí)現(xiàn)computed
轉(zhuǎn)載來(lái)于:http://muchs.cn/article20/ihjpjo.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站內(nèi)鏈、品牌網(wǎng)站設(shè)計(jì)、建站公司微信公眾號(hào)、做網(wǎng)站微信小程序

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話(huà):028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)