Vue3 Reactive響應式原理邏輯詳解
前言
本篇文章主要講解vue
響應式原理的邏輯,也就是vue
怎麼從最開始一步步推導出響應式的結構框架。 先從頭構建一個簡單函數推導出Vue3的Reactive原理,最後再進行源碼的驗證。
一、怎麼實現變量變化
怎麼實現變量變化,相關依賴的結果也跟著變化
當原本price=5
變為price=20
後total
應該變為40
,但是實際total
並不會改變。 解決辦法可以這樣,當變量改變瞭,重新計算一次,那麼結果就會改變為最新的結果。
如果需要重新計算,我們需要將total
語句存儲為一個函數,才能實現依賴的變量改變就進行一次依賴項計算。這裡就用effect
表示函數名。
來,試一下:
👌,實現瞭變量price
改變,依賴變量price quantity
的變量total
也發生改變。
下一步,我們要解決的問題是:應該怎麼把effect
存儲起來,讓代碼更加有通用性,而不是一直復寫effect
,分離出其他的功能的函數各司其職,也就是大傢常說的解耦。
二、怎麼實現變量變化
怎麼實現變量變化,變量改變後就取出effect執行
用什麼存儲effect
呢?當然是用Set,因為Set會過濾出重復的元素,所以能夠保證存儲在Set中的函數不是重復的。 這裡定義一個存儲effect
依賴的變量為dep = new Set()
,定義track
函數表示存儲的過程。 定義trigger
函數用以取出dep
中相關的effect
函數執行(這裡定義的函數與Vue3源碼同名同意義)。
effect
: 會影響結果的函數(要實現響應式的依賴語句)track
:保存所有的effecttrigger
: 當變量改變重新執行代碼
👌,解耦之後代碼結構更清晰瞭。
下面需要解決的一個問題:一個object通常有多個屬性,比如product = { price: 5, quantity: 2 }
,在保存依賴時隻創建瞭一個dep
的集合,應該給price
和quantity
都創建dep
,因為total
的最終結果依賴這兩個屬性,其中任何一個改變都要觸發trigger
函數。創建瞭兩個dep
就需要一個容器將dep
存儲起來。
三、將多個dep存儲在Map中
因為不同的屬性名有自己對應的dep
,所以我們用Map結構(鍵值對形式)來保存不同dep
。
👌,一個object的多個屬性依賴問題解決,更具有通用性瞭。
下一個問題是:不可能隻有一個對象,多個對象又怎麼辦?let product = { price: 5, quantity: 2 }
let user = { firstName: "Joe", lastName: "Smith" }
,比如兩個對象的時候就需要進一步修改上面的代碼瞭。
四、將多個object的depsMap繼續存儲起來
這裡用WeakMap
數據結構去存儲多個需要響應式的object的depsMap
。WeakMap
的基本使用和Map
差不多,隻不過WeakMap
隻接受對象為鍵值,而depsMap
是一個Map
結構剛好(必須是)是對象類型。targetMap
作為存儲多個depsMap
的容器名。
👌,到這裡已經基本實現瞭通用性的響應式代碼瞭,但是還有最後一個問題就是:我們的代碼都需要手動執行(自己添加trigger
運行),不能自動運行。怎麼讓它能夠自動檢測變量改變,然後自動修改結果呢?
五、核心
通過Reflect和Proxy解決自執行問題
在JavaScript中,自動檢測變量不就是get
、自動修改變量不就是set
嗎?在Vue2.x版本中用ES5的Obeject.defineProperty()
自帶的getter/setter
去解決這個問題。ES6中Proxy
也能解決這個問題,但是Proxy
不兼任IE瀏覽器,當時大傢還討論過說不知道尤大怎麼去考慮這個問題,現在問題的答案就是——不考慮。也就是根本不考慮IE兼不兼容🐮🍺。
Proxy
就是代理的意思,任何對真實數據的操作它都能攔截並且代理操作,也就是說Object
上一些能實現的方法,Proxy
也能實現。Proxy
使用語法是new Proxy(target, hanler)
,handler
是你想實現什麼樣的代理功能配置。 而Reflect
就更神奇瞭,它的作用是取代Object
類上的一些方法讓Obeject
類更純粹的代表一個類,不要附加太多方法在上面,比如a in obj
表示判斷obj
中是否有a
,在Reflect
中用Reflect.has(a)
比較語義化的方式就可以代替之前的方法。
正是因為這樣,Proxy
和Reflect
就對應上瞭,都有Object
上的方法。
稍微封裝一下我們的函數,名叫Reactive
👌,至此,Vue3基本的響應式原理就解析完瞭。
六、源碼解析(TypeScript)
return
瞭createReactiveObject
函數,所以去看createReactiveObject
。
前面的代碼都是判斷各種情況,我們就看最後幾行
const observed = new Proxy( target, collectionTypes.has(target.constructor) ? collectionHandlers : baseHandlers )
可以看到Proxy
的handler
為collectionHandlers
或者 baseHandlers
,繼續選擇一個看一看。
在 baseHandlers
中可以看到導出瞭get/set/deleteProperty
等屬性配置:
我們看一下set
:
和我們之前的邏輯差不多,隻不過真正實現就很復雜,因為有很多復雜條件需要去處理。
其他的get
等方法也一樣,做瞭很多條件判斷處理,完善瞭每一種會出現的情況。
到此這篇關於Vue3 Reactive響應式原理邏輯詳解的文章就介紹到這瞭,更多相關Vue3 Reactive響應式 內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- 詳解vue3 響應式的實現原理
- vue3.0響應式函數原理詳細
- vue3響應式實現readonly從零開始教程
- 源碼分析Vue3響應式核心之reactive
- vue3響應式Proxy與Reflect的理解及基本使用實例詳解