自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Vue.js設(shè)計(jì)與實(shí)現(xiàn)之六-computed計(jì)算屬性的實(shí)現(xiàn)

開(kāi)發(fā) 前端
計(jì)算屬性computed其實(shí)是一個(gè)懶執(zhí)行的副作用函數(shù),可以通過(guò)lazy選項(xiàng)使得副作用函數(shù)可以懶執(zhí)行,被標(biāo)記為懶執(zhí)行的副作用函數(shù)可以通過(guò)手動(dòng)執(zhí)行。

1、寫(xiě)在前面

在前面文章介紹了effect的實(shí)現(xiàn),可以用于注冊(cè)副作用函數(shù),同時(shí)允許一些選項(xiàng)參數(shù)options,可以指定調(diào)度器去控制副作用函數(shù)的執(zhí)行時(shí)機(jī)和次數(shù)等。還有用于追蹤和收集依賴的track函數(shù),以及用于觸發(fā)副作用函數(shù)重新執(zhí)行的trigger函數(shù),結(jié)合這些我們可以實(shí)現(xiàn)一個(gè)計(jì)算屬性--computed。

2、懶執(zhí)行的effect

在研究計(jì)算屬性的實(shí)現(xiàn)之前,需要先去了解下懶執(zhí)行的effect(lazy的effect)。在當(dāng)前設(shè)計(jì)的effect函數(shù)中,它會(huì)在調(diào)用時(shí)立即執(zhí)行傳遞過(guò)來(lái)的副作用函數(shù)。但是事實(shí)上,希望在某些場(chǎng)景并不希望它立即執(zhí)行,而是在需要的時(shí)候才執(zhí)行,前面了解到想要改變effect的執(zhí)行可以在options參數(shù)中設(shè)置。

const data = {
name:"pingping",
age:18,
flag:true
}
const state = new Proxy(data,{
/*...*/
})
effect(()=>{
console.log(state.name);
},{
//指定lazy選項(xiàng),這樣函數(shù)不會(huì)立即執(zhí)行
lazy: true
})

就這樣,通過(guò)設(shè)置options選項(xiàng),去修改effect函數(shù)的實(shí)現(xiàn)邏輯,當(dāng)options.lazy為true時(shí)不會(huì)立即執(zhí)行副作用函數(shù):

// effect用于注冊(cè)副作用函數(shù)
function effect(fn,options={}){
const effectFn = ()=>{
// 調(diào)用函數(shù)完成清理遺留副作用函數(shù)
cleanupEffect(effectFn)
// 當(dāng)調(diào)用effect注冊(cè)副作用函數(shù)時(shí),將副作用函數(shù)fn賦值給activeEffect
activeEffect = effectFn;
// 在副作用函數(shù)執(zhí)行前壓棧
effectStack.push(effectFn)
// 執(zhí)行副作用函數(shù)
fn();
// 執(zhí)行完畢后出棧
effectStack.pop()
activeEffect = effectStack[effectStack.length - 1]
}
// 將options掛載到effectFn函數(shù)上
effectFn.options = options
//deps是用于存儲(chǔ)所有與該副作用函數(shù)相關(guān)聯(lián)的依賴集合
effectFn.deps = [];
// 只有非lazy的時(shí)候才執(zhí)行
if(!options.lazy){
// 執(zhí)行副作用函數(shù)effectFn
effectFn()
}
//否則返回副作用函數(shù)
return effectFn
}

在上面代碼片段中,在effect函數(shù)中先判斷了是否需要懶執(zhí)行,對(duì)此會(huì)判斷options.lazy的值為true時(shí),則將effectFn副作用函數(shù)作為參數(shù)返回到effect。這樣,用戶在調(diào)用執(zhí)行effect函數(shù)時(shí),可以通過(guò)返回值去拿到對(duì)應(yīng)的effectFn函數(shù),這樣可以手動(dòng)執(zhí)行該函數(shù)。

const effectFn = effect(()=>{
console.log(state.name);
},{
//指定lazy選項(xiàng),這樣函數(shù)不會(huì)立即執(zhí)行
lazy: true
});
//手動(dòng)執(zhí)行副作用函數(shù)
effectFn();

但是僅僅實(shí)現(xiàn)手動(dòng)執(zhí)行副作用函數(shù),對(duì)于我們的使用意義并不大,如果將返回到effect的副作用函數(shù)作為getter,那么通過(guò)這個(gè)取值函數(shù)就能獲取返回任何值。

const effectFn = effect(
()=>state.name + state.age,
{
//指定lazy選項(xiàng),這樣函數(shù)不會(huì)立即執(zhí)行
lazy: true
});

//手動(dòng)執(zhí)行副作用函數(shù),可以獲取到返回的值
const value = effectFn();

這樣就可以實(shí)現(xiàn)在調(diào)用的時(shí)候,手動(dòng)執(zhí)行獲取到各種想要得到的值。在effect函數(shù)內(nèi)部只需要做出些改變,只需要在執(zhí)行副作用函數(shù)時(shí)將副作用的值返回即可:

// effect用于注冊(cè)副作用函數(shù)
function effect(fn,options={}){
const effectFn = ()=>{
// 調(diào)用函數(shù)完成清理遺留副作用函數(shù)
cleanupEffect(effectFn)
// 當(dāng)調(diào)用effect注冊(cè)副作用函數(shù)時(shí),將副作用函數(shù)fn賦值給activeEffect
activeEffect = effectFn;
// 在副作用函數(shù)執(zhí)行前壓棧
effectStack.push(effectFn)
// 執(zhí)行副作用函數(shù),將執(zhí)行結(jié)果存儲(chǔ)到res
const res = fn();
// 執(zhí)行完畢后出棧
effectStack.pop()
activeEffect = effectStack[effectStack.length - 1]
// 將res作為effectFn的返回值
return res
}
// 將options掛載到effectFn函數(shù)上
effectFn.options = options
//deps是用于存儲(chǔ)所有與該副作用函數(shù)相關(guān)聯(lián)的依賴集合
effectFn.deps = [];
// 只有非lazy的時(shí)候才執(zhí)行
if(!options.lazy){
// 執(zhí)行副作用函數(shù)effectFn
effectFn()
}
//否則返回副作用函數(shù)
return effectFn

}

現(xiàn)在,我們已經(jīng)實(shí)現(xiàn)了能夠進(jìn)行懶執(zhí)行的副作用函數(shù),能夠拿到執(zhí)行返回的結(jié)果,做后續(xù)的處理。

3、computed屬性

懶計(jì)算的computed屬性

其實(shí),基于前面的設(shè)計(jì)和代碼實(shí)現(xiàn),大概有了computed屬性函數(shù)的實(shí)現(xiàn)雛形,就是接收一個(gè)getter函數(shù)作為副作用函數(shù),用于創(chuàng)建一個(gè)懶執(zhí)行的effect。computed函數(shù)的執(zhí)行會(huì)返回包含一個(gè)訪問(wèn)器屬性的對(duì)象,只有在讀取value值的時(shí)候才會(huì)去執(zhí)行effectFn并返回結(jié)果。

function computed(getter){
const effectFn = effect(
getter,
{
//指定lazy選項(xiàng),這樣函數(shù)不會(huì)立即執(zhí)行
lazy: true
});
const state = {
//當(dāng)對(duì)value進(jìn)行讀取操作時(shí),執(zhí)行effectFn并將結(jié)果進(jìn)行返回
get value(){
return effectFn();
}
}
return state;
}

在上面代碼中,只是粗略做了懶計(jì)算處理,只有在真正對(duì)sumRes.value的值進(jìn)行讀取操作時(shí),才會(huì)去進(jìn)行計(jì)算并得到值。但是在進(jìn)行多次讀取sumRes.value的值,每次訪問(wèn)計(jì)算得到的值都是相同的,并不符合我們需要使用上次計(jì)算值的要求。『計(jì)算屬性需要有緩存機(jī)制,這樣就可以使用到上次計(jì)算的結(jié)果。』

const sumRes = computed(()=>state.name + state.age);
console.log("hello", sumRes.value);
console.log("hello", sumRes.value);
console.log("hello", sumRes.value);

運(yùn)行結(jié)果:

之所以發(fā)生這種情況,多次讀取sumRes.value的值時(shí),每次訪問(wèn)都會(huì)重新調(diào)用effectFn重新計(jì)算。

帶有緩存的computed

為了解決前面獲取不到上次計(jì)算值的問(wèn)題,需要在實(shí)現(xiàn)computed函數(shù)時(shí),添加對(duì)計(jì)算值的緩存操作。其實(shí)實(shí)現(xiàn)很簡(jiǎn)單,就是添加兩個(gè)變量value和dirty,value用于緩存上次計(jì)算的值,dirty則標(biāo)識(shí)是否需要重新計(jì)算。

function computed(getter){
let value;
let dirty = true;
const effectFn = effect(
getter,
{
//指定lazy選項(xiàng),這樣函數(shù)不會(huì)立即執(zhí)行
lazy: true,
//在調(diào)度器重置dirtytrue
scheduler(){
dirty = true
}
});
const state = {
//當(dāng)對(duì)value進(jìn)行讀取操作時(shí),執(zhí)行effectFn并將結(jié)果進(jìn)行返回
get value(){
//只有當(dāng)dirty標(biāo)識(shí)為true值時(shí),才會(huì)將計(jì)算值進(jìn)行緩存,下一次訪問(wèn)直接使用緩存的值
if(dirty){
value = effectFn();
dirty = false
}
return value
}
}
return state;
}

在上面代碼中,初始化設(shè)置dirty為true,這樣就會(huì)把計(jì)算值進(jìn)行緩存,下次進(jìn)行同樣computed計(jì)算操作時(shí),就會(huì)直接使用緩存的值,而非每次重新計(jì)算。同時(shí),在computed函數(shù)的effect中添加scheduler屬性,在函數(shù)內(nèi)部將dirty的值重置為true,在下次訪問(wèn)sumRes.value時(shí)重新調(diào)用effectFn的計(jì)算值。

const sumRes = computed(()=>state.name + state.age);
console.log("hello", sumRes.value);
console.log("hello", sumRes.value);
console.log("hello", sumRes.value);
state.age++;
console.log("hello", sumRes.value);

執(zhí)行結(jié)果為:

但是,在當(dāng)前設(shè)計(jì)的計(jì)算屬性在另一個(gè)effect函數(shù)中讀取時(shí),修改響應(yīng)數(shù)據(jù)state上的屬性值并不會(huì)觸發(fā)副作用函數(shù)的重新渲染。其實(shí)根本原因就是這里存在一個(gè)effect嵌套問(wèn)題,computed內(nèi)部是effect函數(shù)實(shí)現(xiàn)的,而在effect中讀取computed的值相當(dāng)于對(duì)effect進(jìn)行了嵌套,外層的effect不會(huì)被內(nèi)層effect的響應(yīng)式數(shù)據(jù)收集。

當(dāng)然,問(wèn)題很簡(jiǎn)單,解決方法同樣很簡(jiǎn)單。只需要在讀取計(jì)算屬性值的時(shí)候,手動(dòng)調(diào)用track函數(shù)進(jìn)行追蹤,當(dāng)計(jì)算屬性依賴的響應(yīng)式數(shù)據(jù)發(fā)生變化時(shí),手動(dòng)調(diào)用trigger函數(shù)觸發(fā)響應(yīng):

function computed(getter){
let value;
let dirty = true;

const effectFn = effect(
getter,
{
//指定lazy選項(xiàng),這樣函數(shù)不會(huì)立即執(zhí)行
lazy: true,
//在調(diào)度器重置dirtytrue
scheduler(){
dirty = true
trigger(state, "value")
}
}
);
const state = {
//當(dāng)對(duì)value進(jìn)行讀取操作時(shí),執(zhí)行effectFn并將結(jié)果進(jìn)行返回
get value(){
//只有當(dāng)dirty標(biāo)識(shí)為true值時(shí),才會(huì)將計(jì)算值進(jìn)行緩存,下一次訪問(wèn)直接使用緩存的值
if(dirty){
value = effectFn();
dirty = false
}
// 對(duì)value進(jìn)行取值操作時(shí),手動(dòng)調(diào)用track函數(shù)進(jìn)行追蹤
track(state, "value")
return value
}
}
return state;
}

寫(xiě)一段簡(jiǎn)單的demo進(jìn)行實(shí)驗(yàn):

const sumRes = computed(()=>state.name + state.age);
console.log("hello", sumRes.value);
console.log("hello", sumRes.value);
console.log("hello", sumRes.value);
effect(()=>{
console.log(sumRes.value);
})
state.age++

console.log("hello", sumRes.value);

執(zhí)行結(jié)果:

根據(jù)上面的實(shí)現(xiàn)demo可以分析出對(duì)應(yīng)的計(jì)算屬性的響應(yīng)聯(lián)系圖:

計(jì)算屬性的響應(yīng)聯(lián)系

4、寫(xiě)在最后

計(jì)算屬性computed其實(shí)是一個(gè)懶執(zhí)行的副作用函數(shù),可以通過(guò)lazy選項(xiàng)使得副作用函數(shù)可以懶執(zhí)行,被標(biāo)記為懶執(zhí)行的副作用函數(shù)可以通過(guò)手動(dòng)執(zhí)行。在讀取計(jì)算屬性的值時(shí),可以手動(dòng)執(zhí)行副作用函數(shù),在依賴的響應(yīng)式數(shù)據(jù)發(fā)生變化時(shí),通過(guò)scheduler將dirty標(biāo)記設(shè)置為true,即為臟數(shù)據(jù),在下次讀取計(jì)算屬性的值,就會(huì)重新計(jì)算得到真正的值。

責(zé)任編輯:姜華 來(lái)源: 前端一碼平川
相關(guān)推薦

2022-04-12 08:08:57

watch函數(shù)options封裝

2022-04-25 07:36:21

組件數(shù)據(jù)函數(shù)

2022-04-01 08:08:27

Vue.js框架命令式

2022-04-04 16:53:56

Vue.js設(shè)計(jì)框架

2022-04-18 08:09:44

渲染器DOM掛載Vue.js

2022-05-03 21:18:38

Vue.js組件KeepAlive

2022-04-14 09:35:03

Vue.js設(shè)計(jì)Reflect

2022-04-05 16:44:59

系統(tǒng)Vue.js響應(yīng)式

2022-04-17 09:18:11

響應(yīng)式數(shù)據(jù)Vue.js

2022-04-09 17:53:56

Vue.js分支切換嵌套的effect

2022-04-03 15:44:55

Vue.js框架設(shè)計(jì)設(shè)計(jì)與實(shí)現(xiàn)

2022-04-16 13:59:34

Vue.jsJavascript

2022-04-26 05:55:06

Vue.js異步組件

2022-04-19 23:01:54

Vue.jsDOM節(jié)點(diǎn)DOM樹(shù)

2022-04-20 09:07:04

Vue.js的事件處理

2016-11-01 19:10:33

vue.js前端前端框架

2023-12-11 07:34:37

Computed計(jì)算屬性Vue3

2024-05-17 09:51:11

2021-09-18 10:07:23

開(kāi)發(fā)技能代碼

2019-04-01 19:38:28

Vue.jsJavascript前端
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)