中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

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

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

Vue3響應式系統如何實現computed

發布時間:2023-05-17 15:34:47 來源:億速云 閱讀:112 作者:zzz 欄目:編程語言

這篇“Vue3響應式系統如何實現computed”文章的知識點大部分人都不太理解,所以小編給大家總結了以下內容,內容詳細,步驟清晰,具有一定的借鑒價值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“Vue3響應式系統如何實現computed”文章吧。

首先,我們簡單回顧一下:

響應式系統的核心就是一個 WeakMap --- Map --- Set 的數據結構。

Vue3響應式系統如何實現computed

WeakMap 的 key 是原對象,value 是響應式的 Map。這樣當對象銷毀的時候,對應的 Map 也會銷毀。

Map 的 key 就是對象的每個屬性,value 是依賴這個對象屬性的 effect 函數的集合 Set。然后用 Proxy 代理對象的 get 方法,收集依賴該對象屬性的 effect 函數到對應 key 的 Set 中。還要代理對象的 set 方法,修改對象屬性的時候調用所有該 key 的 effect 函數。

實現 computed

首先,我們把之前的代碼重構一下,把依賴收集和觸發依賴函數的執行抽離成 track 和 trigger 函數:

Vue3響應式系統如何實現computed

邏輯還是添加 effect 到對應的 Set,以及觸發對應 Set 里的 effect 函數執行,但抽離出來清晰多了。

然后繼續實現 computed。

computed 的使用大概是這樣的:

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

對比下 effect:

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

區別只是多了個返回值。

所以我們基于 effect 實現 computed 就是這樣的:

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

當然,現在的 effect 是沒有返回值的,要給它加一下:

Vue3響應式系統如何實現computed

只是在之前執行 effect 函數的基礎上把返回值記錄下來返回,這個改造還是很容易的。

現在 computed 就能返回計算后的值了:

Vue3響應式系統如何實現computed

但是現在數據一遍,所有的 effect 都執行了,而像 computed 這里的 effect 是沒必要每次都重新執行的,只需要在數據變了之后執行。

所以我們添加一個 lazy 的 option 來控制 effect 不立刻執行,而是把函數返回讓用戶自己執行。

Vue3響應式系統如何實現computed

然后 computed 里用 effect 的時候就添加一個 lazy 的 option,讓 effect 函數不執行,而是返回出來。

computed 里創建一個對象,在 value 的 get 觸發時調用該函數拿到最新的值:

Vue3響應式系統如何實現computed

我們測試下:

Vue3響應式系統如何實現computed

可以看到現在 computed 返回值的 value 屬性是能拿到計算后的值的,并且修改了 obj.a. 之后會重新執行計算函數,再次拿 value 時能拿到新的值。

只是多執行了一次計算,這是因為 obj.a 變的時候會執行所有的 effect 函數:

Vue3響應式系統如何實現computed

這樣每次數據變了都會重新執行 computed 的函數來計算最新的值。

這是沒有必要的,effect 的函數是否執行應該也是可以控制的。所以我們要給它加上調度的功能:

Vue3響應式系統如何實現computed

可以支持傳入 schduler 回調函數,然后執行 effect 的時候,如果有 scheduler 就傳給它讓用戶自己來調度,否則才執行 effect 函數。

這樣用戶就可以自己控制 effect 函數的執行了:

Vue3響應式系統如何實現computed

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

Vue3響應式系統如何實現computed

可以看到,obj.a 變了之后并沒有執行 effect 函數來重新計算,因為我們加了 sheduler 來自己調度。這樣就避免了數據變了以后馬上執行 computed 函數,可以自己控制執行。

現在還有一個問題,每次訪問 res.value 都要計算:

Vue3響應式系統如何實現computed

能不能加個緩存呢?只有數據變了才需要計算,否則直接拿之前計算的值。

當然是可以的,加個標記就行:

Vue3響應式系統如何實現computed

scheduler 被調用的時候就說明數據變了,這時候 dirty 設置為 true,然后取 value 的時候就重新計算,之后再改為 false,下次取 value 就直接拿計算好的值了。

我們測試下:

Vue3響應式系統如何實現computed

我們訪問 computed 值的 value 屬性時,第一次會重新計算,后面就直接拿計算好的值了。

修改它依賴的數據后,再次訪問 value 屬性會再次重新計算,然后后面再訪問就又會直接拿計算好的值了。

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

但現在的 computed 實現還有一個問題,比如這樣一段代碼:

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

我們在一個 effect 函數里用到了 computed 值,按理說 obj.a 變了,那 computed 的值也會變,應該觸發所有的 effect 函數。

但實際上并沒有:

Vue3響應式系統如何實現computed

這是為什么呢?

這是因為返回的 computed 值并不是一個響應式的對象,需要把它變為響應式的,也就是 get 的時候 track 收集依賴,set 的時候觸發依賴的執行:

Vue3響應式系統如何實現computed

我們再試一下:

Vue3響應式系統如何實現computed

現在 computed 值變了就能觸發依賴它的 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()
        }
    })
}

以上就是關于“Vue3響應式系統如何實現computed”這篇文章的內容,相信大家都有了一定的了解,希望小編分享的內容對大家有幫助,若想了解更多相關的知識內容,請關注億速云行業資訊頻道。

向AI問一下細節

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

AI

营山县| 罗山县| 尖扎县| 光泽县| 柳州市| 依兰县| 黎川县| 黑龙江省| 阿瓦提县| 兴安盟| 宁陕县| 胶州市| 凤庆县| 广德县| 百色市| 甘南县| 凤翔县| 凤山市| 沧源| 大同市| 峡江县| 巫溪县| 汽车| 新竹县| 垣曲县| 卫辉市| 桃江县| 杭锦旗| 贵溪市| 大荔县| 桓仁| 化州市| 塔河县| 太湖县| 浮梁县| 云阳县| 南通市| 九寨沟县| 于都县| 通渭县| 杭锦旗|