這篇“Vue3響應(yīng)式系統(tǒng)如何實現(xiàn)computed”文章的知識點大部分人都不太理解,所以小編給大家總結(jié)了以下內(nèi)容,內(nèi)容詳細(xì),步驟清晰,具有一定的借鑒價值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“Vue3響應(yīng)式系統(tǒng)如何實現(xiàn)computed”文章吧。
創(chuàng)新互聯(lián)2013年開創(chuàng)至今,是專業(yè)互聯(lián)網(wǎng)技術(shù)服務(wù)公司,擁有項目成都網(wǎng)站建設(shè)、成都網(wǎng)站設(shè)計網(wǎng)站策劃,項目實施與項目整合能力。我們以讓每一個夢想脫穎而出為使命,1280元銀川做網(wǎng)站,已為上家服務(wù),為銀川各地企業(yè)和個人服務(wù),聯(lián)系電話:18980820575
首先,我們簡單回顧一下:
響應(yīng)式系統(tǒng)的核心就是一個 WeakMap --- Map --- Set 的數(shù)據(jù)結(jié)構(gòu)。
WeakMap 的 key 是原對象,value 是響應(yīng)式的 Map。這樣當(dāng)對象銷毀的時候,對應(yīng)的 Map 也會銷毀。
Map 的 key 就是對象的每個屬性,value 是依賴這個對象屬性的 effect 函數(shù)的集合 Set。然后用 Proxy 代理對象的 get 方法,收集依賴該對象屬性的 effect 函數(shù)到對應(yīng) key 的 Set 中。還要代理對象的 set 方法,修改對象屬性的時候調(diào)用所有該 key 的 effect 函數(shù)。
首先,我們把之前的代碼重構(gòu)一下,把依賴收集和觸發(fā)依賴函數(shù)的執(zhí)行抽離成 track 和 trigger 函數(shù):
邏輯還是添加 effect 到對應(yīng)的 Set,以及觸發(fā)對應(yīng) Set 里的 effect 函數(shù)執(zhí)行,但抽離出來清晰多了。
然后繼續(xù)實現(xiàn) computed。
computed 的使用大概是這樣的:
const value = computed(() => { return obj.a + obj.b; });
對比下 effect:
effect(() => { console.log(obj.a); });
區(qū)別只是多了個返回值。
所以我們基于 effect 實現(xiàn) computed 就是這樣的:
function computed(fn) { const value = effect(fn); return value }
當(dāng)然,現(xiàn)在的 effect 是沒有返回值的,要給它加一下:
只是在之前執(zhí)行 effect 函數(shù)的基礎(chǔ)上把返回值記錄下來返回,這個改造還是很容易的。
現(xiàn)在 computed 就能返回計算后的值了:
但是現(xiàn)在數(shù)據(jù)一遍,所有的 effect 都執(zhí)行了,而像 computed 這里的 effect 是沒必要每次都重新執(zhí)行的,只需要在數(shù)據(jù)變了之后執(zhí)行。
所以我們添加一個 lazy 的 option 來控制 effect 不立刻執(zhí)行,而是把函數(shù)返回讓用戶自己執(zhí)行。
然后 computed 里用 effect 的時候就添加一個 lazy 的 option,讓 effect 函數(shù)不執(zhí)行,而是返回出來。
computed 里創(chuàng)建一個對象,在 value 的 get 觸發(fā)時調(diào)用該函數(shù)拿到最新的值:
我們測試下:
可以看到現(xiàn)在 computed 返回值的 value 屬性是能拿到計算后的值的,并且修改了 obj.a. 之后會重新執(zhí)行計算函數(shù),再次拿 value 時能拿到新的值。
只是多執(zhí)行了一次計算,這是因為 obj.a 變的時候會執(zhí)行所有的 effect 函數(shù):
這樣每次數(shù)據(jù)變了都會重新執(zhí)行 computed 的函數(shù)來計算最新的值。
這是沒有必要的,effect 的函數(shù)是否執(zhí)行應(yīng)該也是可以控制的。所以我們要給它加上調(diào)度的功能:
可以支持傳入 schduler 回調(diào)函數(shù),然后執(zhí)行 effect 的時候,如果有 scheduler 就傳給它讓用戶自己來調(diào)度,否則才執(zhí)行 effect 函數(shù)。
這樣用戶就可以自己控制 effect 函數(shù)的執(zhí)行了:
然后再試一下剛才的代碼:
可以看到,obj.a 變了之后并沒有執(zhí)行 effect 函數(shù)來重新計算,因為我們加了 sheduler 來自己調(diào)度。這樣就避免了數(shù)據(jù)變了以后馬上執(zhí)行 computed 函數(shù),可以自己控制執(zhí)行。
現(xiàn)在還有一個問題,每次訪問 res.value 都要計算:
能不能加個緩存呢?只有數(shù)據(jù)變了才需要計算,否則直接拿之前計算的值。
當(dāng)然是可以的,加個標(biāo)記就行:
scheduler 被調(diào)用的時候就說明數(shù)據(jù)變了,這時候 dirty 設(shè)置為 true,然后取 value 的時候就重新計算,之后再改為 false,下次取 value 就直接拿計算好的值了。
我們測試下:
我們訪問 computed 值的 value 屬性時,第一次會重新計算,后面就直接拿計算好的值了。
修改它依賴的數(shù)據(jù)后,再次訪問 value 屬性會再次重新計算,然后后面再訪問就又會直接拿計算好的值了。
至此,我們完成了 computed 的功能。
但現(xiàn)在的 computed 實現(xiàn)還有一個問題,比如這樣一段代碼:
let res = computed(() => { return obj.a + obj.b; }); effect(() => { console.log(res.value); });
我們在一個 effect 函數(shù)里用到了 computed 值,按理說 obj.a 變了,那 computed 的值也會變,應(yīng)該觸發(fā)所有的 effect 函數(shù)。
但實際上并沒有:
這是為什么呢?
這是因為返回的 computed 值并不是一個響應(yīng)式的對象,需要把它變?yōu)轫憫?yīng)式的,也就是 get 的時候 track 收集依賴,set 的時候觸發(fā)依賴的執(zhí)行:
我們再試一下:
現(xiàn)在 computed 值變了就能觸發(fā)依賴它的 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)如何實現(xiàn)computed”這篇文章的內(nèi)容,相信大家都有了一定的了解,希望小編分享的內(nèi)容對大家有幫助,若想了解更多相關(guān)的知識內(nèi)容,請關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。
當(dāng)前題目:Vue3響應(yīng)式系統(tǒng)如何實現(xiàn)computed
轉(zhuǎn)載來于:http://chinadenli.net/article20/ihjpjo.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站內(nèi)鏈、品牌網(wǎng)站設(shè)計、建站公司、微信公眾號、做網(wǎng)站、微信小程序
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)