Vue3實(shí)現(xiàn)計(jì)算屬性的代碼詳解
代碼解釋
import { ReactiveEffect } from "./effect" class computedImpl { private _fn: any private stop: boolean = true private _value: any private _effect: any constructor(fn) { this._fn = fn // (1) this._effect = new ReactiveEffect(fn, () => { // (2) if (!this.stop) { this.stop = true } }) } get value() { if(this.stop) { this.stop = false this._value = this._effect.run() } return this._value } } export function computed(fn) { return new computedImpl(fn) }
可以看到代碼其實(shí)很簡單,創(chuàng)建了一個computedImpl類,這個類接收一個函數(shù)作為其參數(shù),最下面將實(shí)例化的computedImpl對象拋出即可。
看著很簡單,那我們來介紹下計(jì)算屬性在這個類里面做了什么?
一、實(shí)例化的時候會將傳進(jìn)去函數(shù)緩存起來(1)
二、創(chuàng)建一個ReactiveEffect對象,將傳入的fn作為參數(shù)傳入,第二個參數(shù)處接收一個scheduler函數(shù)(2)
() => { if (!this.stop) { this.stop = true } }
這里介紹一下這一步的作用:
Vue3 有一個ReactiveEffect這樣的類,接收兩個參數(shù),一個是默認(rèn)函數(shù),另一個是scheduler函數(shù)。
創(chuàng)建這樣的類有什么作用呢?首先我們知道傳入的函數(shù)里面肯定有響應(yīng)式的數(shù)據(jù),因?yàn)橛?jì)算屬性的作用是當(dāng)響應(yīng)式數(shù)據(jù)更新時重新計(jì)算我們傳入的計(jì)算屬性函數(shù)。
,意味著我們要給給這些響應(yīng)式數(shù)據(jù)去收集一個依賴,用于告訴我們計(jì)算屬性響應(yīng)式數(shù)據(jù)是否有發(fā)生變動。
那我們Vue3是怎么去收集依賴的呢?那就不得不提到ReactiveEffect了,實(shí)例化ReactiveEffect對象有一個run函數(shù),當(dāng)我們執(zhí)行run函數(shù)的時候會執(zhí)行一遍我們傳入的fn并且將自身指向一個全局變量activeEffect
,當(dāng)我們響應(yīng)式數(shù)據(jù)發(fā)生取值操作的時候就會去收集依賴,就是去判斷activeEffect
是否存在,如果存在收集起來。
當(dāng)響應(yīng)式數(shù)據(jù)發(fā)生set操作的時候,就會去觸發(fā)依賴,那么我們之前存進(jìn)去的實(shí)例化ReactiveEffect對象就會被拿出來執(zhí)行,這樣我們就可以知道數(shù)據(jù)發(fā)生了改變,默認(rèn)情況下會執(zhí)行ReactiveEffect傳進(jìn)的默認(rèn)函數(shù),但是如果scheduler函數(shù)存在那就會執(zhí)行scheduler函數(shù)。
三、計(jì)算屬性函數(shù)執(zhí)行與緩存
上面已經(jīng)知道如何實(shí)現(xiàn)依賴收集的了,那么講解下函數(shù)執(zhí)行和緩存的實(shí)現(xiàn)。
因?yàn)?code>我們已經(jīng)創(chuàng)建了一個實(shí)例化ReactiveEffect對象,在執(zhí)行這個對象的run函數(shù)時會執(zhí)行我們傳入的函數(shù),所以當(dāng)我們get Value的時候就會執(zhí)行一遍函數(shù),因?yàn)閳?zhí)行函數(shù)的時候會,觸發(fā)響應(yīng)式對象的取值操作,ReactiveEffect對象被存儲到依賴中,并且返回函數(shù)的計(jì)算結(jié)果。
這樣我們每次執(zhí)行g(shù)et Value都會執(zhí)行函數(shù)拿到函數(shù)結(jié)果。因?yàn)閏omputed會有緩存功能,所以這里又設(shè)置了一個_value和stop用在結(jié)果緩存和開關(guān),默認(rèn)get Value操作走一遍stop的判斷函數(shù)執(zhí)行完之后關(guān)閉開關(guān),防止每次執(zhí)行都會走一遍函數(shù),然后將函數(shù)結(jié)果緩存到_value中,下次直接拿_value就行
if(this.stop) { this.stop = false this._value = this._effect.run() } return this._value
上面的代碼可以看到我們這個開關(guān)一關(guān)閉就不會打開了,那我們豈不是每次都會拿到老的值?
回到上面的實(shí)例化ReactiveEffect對象中,這里傳入了一個scheduler函數(shù)。
() => { if (!this.stop) { this.stop = true } }
當(dāng)響應(yīng)式數(shù)據(jù)發(fā)生改變的時候scheduler函數(shù)執(zhí)行,stop開關(guān)打開,那我們下一次執(zhí)行g(shù)et Value就會重新執(zhí)行函數(shù)更新計(jì)算屬性的結(jié)果
總結(jié)
在computed實(shí)際上是返回了一個實(shí)例化的對象,這個對象在實(shí)例化的時候接收了一個參數(shù)就是computed內(nèi)部的回調(diào)。
因?yàn)閏omputed是緩存的,所以返回的實(shí)例化對象中有一個get方法,取值的時候就會將傳入的回調(diào)給執(zhí)行了,并且把結(jié)果設(shè)置為實(shí)例化對象的內(nèi)部屬性,并返回出去。
所以computed在取值的時候?qū)嶋H上就讀取了存在在對象中的回調(diào)函數(shù)的執(zhí)行結(jié)果。
因?yàn)閏omputed會緩存所以在執(zhí)行g(shù)et方法的時候會做一個開關(guān),比如說lazy默認(rèn)為true,只有當(dāng)lazy為true才能執(zhí)行回調(diào)函數(shù),當(dāng)函數(shù)執(zhí)行完lazy改成false,所以后續(xù)的取值操作都不執(zhí)行函數(shù),實(shí)現(xiàn)了緩存的功能。
因?yàn)槿绻鹀omputed里面的響應(yīng)式對象如果發(fā)生了改變,computed的結(jié)果是需要更新的,所以需要對computed里面的響應(yīng)式對象進(jìn)行一個依賴設(shè)置。
vue在進(jìn)行依賴收集的時候會用一個全局的變量去存儲回調(diào)函數(shù),在響應(yīng)式對象get操作的時候就會判斷這個全局變量是否存在,如果存在則加到依賴中,后續(xù)set操作的時候就拿這些存儲的依賴處理執(zhí)行。
所以computed在實(shí)例化對象的時候,會構(gòu)造一個這樣一個全局變量然后里面的回調(diào)函數(shù)的作用是如果執(zhí)行l(wèi)azy置為true,即開關(guān)打開。
然后因?yàn)槿ト≈档臅r候會執(zhí)行一次函數(shù),所以響應(yīng)式對象get被觸發(fā),把lazy置為true的回調(diào)存入依賴中,下次響應(yīng)式對象更新值即set的時候,computed返回的實(shí)例對象內(nèi)部的開關(guān)就會被打開了。
所以我們下一次執(zhí)行computed的value的時候,因?yàn)殚_關(guān)是開的所以會重新執(zhí)行一遍函數(shù)并更新實(shí)例化對象的值,然后開關(guān)關(guān)上緩存起來,返回新的值。
以上就是Vue3實(shí)現(xiàn)計(jì)算屬性的代碼詳解的詳細(xì)內(nèi)容,更多關(guān)于Vue3計(jì)算屬性的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Vue二次封裝el-select實(shí)現(xiàn)下拉滾動加載效果(el-select無限滾動)
el-select默認(rèn)是不支持虛擬滾動的,需要使用第三方插件來實(shí)現(xiàn)虛擬滾動功能,下面這篇文章主要給大家介紹了關(guān)于Vue二次封裝el-select實(shí)現(xiàn)下拉滾動加載效果的相關(guān)資料,需要的朋友可以參考下2024-04-04Element Plus 日期選擇器獲取選中的日期格式(當(dāng)前日期/時間戳格式)
如果想要獲取選中的日期時間就需要通過,Element Plus 日期選擇器?format屬性和value-format屬性,format指定輸入框的格式,value-format?指定綁定值的格式,本篇文章就給大家介紹Element Plus 日期選擇器獲取選中的日期格式(當(dāng)前日期/時間戳格式),感興趣的朋友一起看看吧2023-10-10Vue 實(shí)現(xiàn)把表單form數(shù)據(jù) 轉(zhuǎn)化成json格式的數(shù)據(jù)
今天小編就為大家分享一篇Vue 實(shí)現(xiàn)把表單form數(shù)據(jù) 轉(zhuǎn)化成json格式的數(shù)據(jù),具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-10-10基于vue-draggable 實(shí)現(xiàn)三級拖動排序效果
這篇文章主要介紹了基于vue-draggable 實(shí)現(xiàn)三級拖動排序效果,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下2020-01-01Vue生成隨機(jī)卡密并綁定到表單輸入框的實(shí)現(xiàn)技巧
在現(xiàn)代Web應(yīng)用中,隨機(jī)生成驗(yàn)證碼、卡密等是一項(xiàng)常見需求,對于開發(fā)人員來說,如何在表單中快速生成一個符合格式的隨機(jī)卡密,并與其他表單字段一同提交,是一個常見的場景,本篇文章將詳細(xì)介紹如何在Vue.js框架中實(shí)現(xiàn)一個隨機(jī)卡密生成功能,需要的朋友可以參考下2024-11-11