這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 從computed的特性出發 computed最耀眼的幾個特性是啥? 1. 依賴追蹤 import { reactive, computed } from 'vue' const state = reactive({ a: 1, b: 2 ...
這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助
從computed的特性出發
computed
最耀眼的幾個特性是啥?
1. 依賴追蹤
import { reactive, computed } from 'vue' const state = reactive({ a: 1, b: 2, c: 3, }) const sum = computed(() => { return state.a + state.b })
我們定義了一個響應式數據state
和一個計算屬性sum
, Vue會自動追蹤sum
依賴的數據state.a
和state.b
,並建立相應的依賴關係。
也就是只有state.a
和state.b
發生變化的時候,sum
才會重新計算而state.c
任由它怎麼變,sum
都將絲毫不受影響。
2. 緩存
還是上面的例子,如果state.a
和state.b
打死都不再改變值了,那麼我們讀取sum
的時候,它將會返回上一次計算的結果,而不是重新計算。
3. 懶計算
這個特性比較容易被忽略,簡單地說只有計算屬性真正被使用(讀取)的時候才會進行計算,否則咱就僅僅是定義了一個變數而已。
import { reactive, computed } from 'vue' const state = reactive({ a: 1, b: 2, c: 3 }) const sum = computed(() => { console.log('執行計算') return state.a + state.b }) setTimeout(() => { // 沒有讀取sum.value之前,sum不會進行計算 console.log('1-sum', sum.value) // 我們改變了a的值,但是sum並不會立刻進行計算 state.a = 4 setTimeout(() => { // 而是要等到再次讀取的時候才會觸發重新計算 console.log('2-sum', sum.value) }, 1000) }, 1000)
挨個實現computed特性
1. 懶計算
我們依舊圍繞effect
函數來搞事情,到目前為止,effect
註冊的回調都是立刻執行。
const state = reactive({ a: 1, b: 2, c: 3 }) // 有沒有很像計算屬性的感覺 const sum = effect(() => { console.log('執行計算') // 立刻被列印 const value = state.a + state.b return value }) console.log(sum) // undefined
想要實現computed
的懶執行,咱們可以參考上篇文章Vue3:原來你是這樣的“非同步更新”的思路,添加一個額外的參數lazy
。
它要實現的功能是:如果傳遞了lazy
為true
,副作用函數將不會立即執行,而是將執行的時機交還給用戶,由用戶決定啥時候執行。
當然啦!回調的結果我們也應該一併返回(例如上面的value值)
你能想象,我們僅僅需要改造幾行代碼就能離computed
近了一大步。
const effect = function (fn, options = {}) { const effectFn = () => { // ... 省略 // 新增res存儲fn執行的結果 const res = fn() // ... 省略 // 新增返回結果 return res } // ... 省略 // 新增,只有lazy不為true時才會立即執行 if (!options.lazy) { effectFn() } // 新增,返回副作用函數讓用戶執行 return effectFn }
測試一波
const state = reactive({ a: 1, b: 2, c: 3, }); // 有沒有很像計算屬性的感覺 const sum = effect(() => { console.log("執行計算"); // 調用sum函數後被列印 const value = state.a + state.b; return value; }, { lazy: true }); // 不執行sum函數,effect註冊的回調將不會執行 console.log(sum()); // 3
2. 依賴追蹤
咱們初步實現了懶執行的特性,為了更像computed
一點,我們需要封裝一個函數。
function computed (getter) { const effectFn = effect(getter, { lazy: true, }) const obj = { get value () { return effectFn() } } return obj }
這就有點那麼味道啦!
測試一波
可以看到computed
只會依賴state.a
和state.b
,而不會依賴state.c
,這得益於我們前面幾篇文章實現的響應式系統,所以到了計算屬性這裡,我們不用改動任何代碼,天然就支持。
不過還是有點小問題,我們讀取了兩次sum.value
,sum卻被執行了兩次,這和computed
緩存的特性就不符了。
別急,馬上就要實現了這個最重要的特性了。
const state = reactive({ a: 1, b: 2, c: 3 }) const sum = computed(() => { console.log('執行計算') return state.a + state.b }) console.log(sum.value) console.log(sum.value)
3. 緩存
回顧一下computed
的緩存特性:
- 只有當其依賴的東西發生變化了才需要重新計算
- 否則就返回上一次執行的結果。
為了緩存上一次計算的結果,咱們需要定義一個value變數,現在的關鍵是怎麼才能知道其依賴的數據發生變化了呢?
function computed (getter) { const effectFn = effect(getter, { lazy: true, }) let value let dirty = true const obj = { get value () { // 2. 只有數據發生變化了才去重新計算 if (dirty) { value = effectFn() dirty = false } return value } } return obj }測試一波
const state = reactive({ a: 1, b: 2, c: 3 }) const sum = computed(() => { console.log('執行計算') return state.a + state.b }) console.log(sum.value) // 3 console.log(sum.value) // 3 state.a = 4 console.log(sum.value) // 3 答案是錯誤的
寄上任務調度
不得不說,任務調度實在太強大了,不僅僅可以實現數組的非同步批量更新、在computed
和watch
中也是必不可少的。
function computed (getter) { const effectFn = effect(getter, { lazy: true, // 數據發生變化後,不執行註冊的回調,而是執行scheduler scheduler () { // 數據發生了變化後,則重新設置為dirty,那麼下次就會重新計算 dirty = true } }) let value let dirty = true const obj = { get value () { // 2. 只有數據發生變化了才去重新計算 if (dirty) { value = effectFn() dirty = false } return value } } return obj }
測試一波
const state = reactive({ a: 1, b: 2, c: 3 }) const sum = computed(() => { console.log('執行計算') return state.a + state.b }) console.log(sum.value) // 3 console.log(sum.value) // 3 state.a = 4 console.log(sum.value) // 3 答案是錯誤的
完美!!!這下麵試官再也難不倒我了!!!