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

Vue3源碼解析計劃之計算屬性為什么比普通函數(shù)妙?

開發(fā) 前端
計算屬性是Vue開發(fā)中一個非常實用的API,它允許用戶自定義一個計算方法,然后根據(jù)一些依賴的響應(yīng)式數(shù)據(jù)計算出新值并返回。當(dāng)依賴發(fā)生變化時,計算屬性會自動重新計算獲取新值,使用方便。

[[440389]]

本文轉(zhuǎn)載自微信公眾號「前端萬有引力」,作者 一川 。轉(zhuǎn)載本文請聯(lián)系前端萬有引力公眾號。

寫在前面

計算屬性是Vue開發(fā)中一個非常實用的API,它允許用戶自定義一個計算方法,然后根據(jù)一些依賴的響應(yīng)式數(shù)據(jù)計算出新值并返回。當(dāng)依賴發(fā)生變化時,計算屬性會自動重新計算獲取新值,使用方便。我們看出計算屬性本質(zhì)上是對依賴的計算,為什么不直接使用函數(shù)呢?在Vue3中的計算屬性又是如何實現(xiàn)的呢?

計算屬性 computed

我們先簡單看個例子,我們看到再設(shè)置了計算屬性addOne后,直接改變addOne.value的值會報錯,只能通過改變原始值count.value才不會報錯。這是因為:

  • 如果傳遞給computed的是一個函數(shù),那就是一個getter函數(shù),只能獲取它的值,而不能直接修改它
  • 在getter函數(shù)中,根據(jù)響應(yīng)式對象重新計算出新值,叫做計算屬性,這個響應(yīng)式對象叫做計算屬性的依賴
  1. const count = ref(1); 
  2. const addOne = computed(()=>count.value+1); 
  3. console.log(addOne.value);//2 
  4. addOne.value++;//error 
  5. count.value++; 
  6. console.log(count.value);//3 

那么,我們應(yīng)該如何修改addOne.value值呢?那就是在computed中設(shè)置set函數(shù),進行自定義修改值。

  1. const count = ref(1); 
  2. const addOne = computed({ 
  3.  get:()=>count.value+1, 
  4.   set:val=>count.value=val-1 
  5. }); 
  6. addOne.value = 1; 
  7. console.log(count.value);//0 

我們研究源碼:

  1. export function computed<T>( 
  2.   getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T> 
  3. ) { 
  4.   let getter: ComputedGetter<T> 
  5.   let setter: ComputedSetter<T> 
  6.  
  7.   // 如果傳入是 function 說明是只讀 computed 
  8.   if (isFunction(getterOrOptions)) { 
  9.     getter = getterOrOptions 
  10.     setter = __DEV__ 
  11.       ? () => { 
  12.           console.warn('Write operation failed: computed value is readonly'
  13.         } 
  14.       : NOOP 
  15.   } else { 
  16.     // 不是方法說明是自定義的 getter setter  
  17.     getter = getterOrOptions.get 
  18.     setter = getterOrOptions.set 
  19.   } 
  20.  
  21.   let dirty = true 
  22.   let value: T 
  23.   let computed: ComputedRef<T> 
  24.  
  25.   // 創(chuàng)建 effect, 我們在看 effect 源碼時知道了傳入 lazy 代表不會立即執(zhí)行,computed 表明 computed 上游依賴改變的時候,會優(yōu)先 trigger runner effect, scheduler 表示 effect trigger 的時候會調(diào)用 scheduler 而不是直接調(diào)用 effect 
  26.   const runner = effect(getter, { 
  27.     lazy: true
  28.     // mark effect as computed so that it gets priority during trigger 
  29.     computed: true
  30.     scheduler: () => { 
  31.       // 在觸發(fā)更新時把dirty置為true, 不會立即更新  
  32.       if (!dirty) { 
  33.         dirty = true 
  34.         trigger(computed, TriggerOpTypes.SET'value'
  35.       } 
  36.     } 
  37.   }) 
  38.  
  39.   // 構(gòu)造一個 computed 返回 
  40.   computed = { 
  41.     __v_isRef: true
  42.     // expose effect so computed can be stopped 
  43.     effect: runner, 
  44.     get value() { 
  45.       // dirty為ture, get操作時,執(zhí)行effect獲取最新值 
  46.       //  
  47.       if (dirty) { 
  48.         value = runner() 
  49.         dirty = false 
  50.       } 
  51.       // dirty為false, 表示值未更新,直接返回  
  52.       track(computed, TrackOpTypes.GET, 'value'
  53.       return value 
  54.     }, 
  55.     set value(newValue: T) { 
  56.       setter(newValue) 
  57.     } 
  58.   } as any 
  59.   return computed 

computed計算屬性有兩個特點:

  • 延時計算:只有當(dāng)我們訪問計算屬性時,真正運行computed getter函數(shù)計算
  • 緩存:它的內(nèi)部會緩存上次的計算結(jié)果value,而只有dirty為true時才會重新計算,如果訪問計算屬性時dirty為false,那么直接返回這個value

那么,計算屬性的優(yōu)勢是:只要依賴不變化,就可以使用緩存的value而不用每次再渲染組件的時候都執(zhí)行函數(shù)去計算。

做個嵌套計算的小例子,我們看到:對于addOne而言,它收集的依賴是組件副作用渲染函數(shù),而對于count而言,它收集的依賴是addTwo內(nèi)部的runner函數(shù)。當(dāng)我們修改count值,會進行派發(fā)通知,先運行addOne中的setter函數(shù),此時addOne中的dirty值變成true,然后trigger函數(shù)再次派發(fā)通知;接著運行addTwo中的setter函數(shù),此時把addTwo中的dirty值設(shè)置為true;當(dāng)我們再次訪問addTwo中的值時,發(fā)現(xiàn)dirty值為true,就會執(zhí)行addTwo的computed函數(shù),會先去執(zhí)行addOne.value + 1,再去執(zhí)行addOne的computed函數(shù)中的count.value + 1。這樣就得到最后打印出來的值為2。

  1. const count = ref(0); 
  2. const addOne = computed(()=>{ 
  3.  return count.value + 1;//1 
  4. })  
  5.  
  6. const addTwo = computed(()=>{ 
  7.  return addOne.value + 1;//2 
  8. }) 
  9.  
  10. console.log(addTwo.value);//2 

得益于computed計算屬性的巧妙設(shè)計,無論嵌套多少層都能夠正常運行。

  1. import {ref,computed} from "vue"
  2. import {effect} from "@vue/reactivity"
  3.  
  4. const count = ref(0); 
  5. const addOne = computed(()=>{ 
  6.  return count.value + 1; 
  7. }) 
  8.  
  9. effect(()=>console.log(addOne.value+count.value)) 
  10.  
  11. function add(){ 
  12.  count.value++; 
  13.  
  14. add(); 

我們看到上面代碼最終輸出結(jié)果是:1 3 3

當(dāng)我們第一次執(zhí)行addOne的computed計算屬性時,count.value值還是0,而addOne.value的值為1,將會觸發(fā)并執(zhí)行effect,此時打印出來還是1。而后執(zhí)行add()函數(shù),會進行修改count.value的值,會觸發(fā)并執(zhí)行effect函數(shù),因為addOne也是effect的依賴,addOne的runners函數(shù)也是count.value的依賴,count.value值的修改會執(zhí)行runners函數(shù),會再次執(zhí)行addOne的依賴,接著會觸發(fā)effect函數(shù),因此會輸出兩次3。

computed函數(shù)返回的對象實際上劫持的是value屬性的getter和setter,但是為什么我們在組件的模板中訪問一個計算屬性變量,不用手動在后面加.value呢?

參考文章

  • 《Vue3核心源碼解析》
  • 《Vue中文社區(qū)》
  • 《Vue3中文文檔》

寫在最后

 

理解計算屬性的工作機制,能夠搞明白計算屬性嵌套場景代碼的執(zhí)行順序,知道計算屬性的兩個特點--延時計算和緩存,在組件的開發(fā)中合理使用計算屬性。

 

責(zé)任編輯:武曉燕 來源: 前端萬有引力
相關(guān)推薦

2021-12-12 18:31:35

VNode組件Vue3

2021-01-20 14:25:53

Vue3CSS前端

2021-08-23 13:25:25

Vue3CSS前端

2023-12-11 07:34:37

Computed計算屬性Vue3

2023-07-26 17:40:50

2025-03-26 10:29:22

Vue3前端API

2025-02-18 08:10:00

Vue 3JavaScrip開發(fā)

2024-05-27 08:39:17

Vue3變量響應(yīng)式

2021-09-22 07:57:23

Vue3 插件Vue應(yīng)用

2021-11-26 05:59:31

Vue3 插件Vue應(yīng)用

2021-12-13 00:54:14

組件Vue3Setup

2020-09-17 07:08:04

TypescriptVue3前端

2022-01-26 11:00:58

源碼層面Vue3

2024-07-04 08:56:35

Vue3項目Pinia

2021-12-01 08:11:44

Vue3 插件Vue應(yīng)用

2021-11-30 08:19:43

Vue3 插件Vue應(yīng)用

2023-11-28 09:03:59

Vue.jsJavaScript

2021-09-27 06:29:47

Vue3 響應(yīng)式原理Vue應(yīng)用

2022-03-24 20:42:19

Vue3API 設(shè)計Vue

2020-09-19 21:15:26

Composition
點贊
收藏

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