Vue | 認識 ref 與 reactive 基本運作

James Pan
Nov 18, 2020

前言

就現代前端框架而言,身受我喜愛的 magic 之一,莫過於資料與畫面之間響應式 (Reactivity) 的連動,但用歸用,對於該技術背後原理的認知也是非常重要,不然它終究只是個 magic。

這篇主要會來瞭解 Vue3 ref 與 reactive 創建的方式差異為何。但在進入正文之前如果對於 Proxy 運作不太熟悉,可以參考以下文章!

Description

ref

ref 可接受任何型別的值,如傳入的型別為物件,會轉為 reactive 方法繼續實現。所以實際上 ref 主要負責將型別為物件以外的資料包裝起來變成具有響應效果。

接著由於我們已經針對自己需要的值創建出 ref 的物件,可透過 .value 存取該值,並且其背後就是利用 set, get 方法直接劫持我們對該值的訪問而不用額外使用 Proxy 來監聽。

讓我們看一下原始碼:

第 7 行:可以看到當在創建一個 ref 時, createRef 接收兩個參數,一個是型別為 unknownrawValue ,另一個則是 shallow = false 藉以判斷是否為淺觀察。

第 9行:判斷傳入的 rawValue 是否已經為 ref,如果為 true 則直接返回當前值。

第 15 行:接著再判斷是否為淺觀察,如果為 true 就將當前傳入的參數指向 value ,反之為 false 則呼叫 convert() 並將 rawValue 傳入。

第 44 行:程式會依照剛剛所傳入的 rawValue 判斷是否為物件,如果為 true 呼叫 reactive()

第 18 行:對該傳入的值建立 ref 的結構,裡面提供 get, set 方法賦予該值有響應效果。

我們可以在上面的 get()set() 裡面找到對應的 track() 以及 trigger() ,這部分是屬於 effect 的範疇,文章最後我們會來說明,這邊我們只要先記住 ref 的程式流程即可。

附上 ref 使用方式及回傳的結果:

reactive

reactive 對於傳入的參數只接受物件型別,如果傳入非物件型別的參數則會得到 value cannot be made reactivity 的通知。

下面依舊附上原始碼:

第 3 行:如果 target 有值且 target[ReactiveFlags.IS_READONLY] = true 的話就直接返回當前物件。

第 6 行:如果上述條件都不符合則呼叫 createReactiveObject() 並帶入以下四個參數:

target: 當前傳入的物件isReadOnly: 是否唯讀mutableHandlers:baseHandlers 的分支,針對可變的普通物件的行為做代理mutableCollectionHandlers:collectionHandlers 的分支,針對可變的集合物件做代理(Map、Set、WeakMapWeakSet)

第 20 行:判斷 target 是否為物件

第 28 行:判斷 target 是否為 Reactive Object

第 35~39 行: 根據 isReadOnly 判斷 proxyMap 會等於哪個 WeakMap ,並透過 WeakMap.get() 判斷如果該 target 的 Proxy value 存在就返回該 target

第 41~44 行:判斷 target 的 type,返回的值有 COMMON, COLLECTION, INVALID,如果得出 INVALID 則直接返回該 target

第 45~50 行:經過以上層層判斷,我們可替該 target 創建 Proxy 代理,並在第 49 行透過 WeakMap.set() 存放該 target 以及其相對應的 proxy

附上 reactive 使用方式及回傳的結果:

結論

每次讀原始碼時總會有很多的延伸閱讀,但侷限於主題的範圍,只能先就當前單元做解說,等完善後我會再列清單連結將相關主題擺放在一起,方便查詢。

回到本文結論~~

儘管在 ref 帶入物件參數它還是會自動轉到 reactive,但 ref 本質上並不是要處理物件型別的資料,所以為了明確區分其功能,個人習慣還是會分開使用。當然如果有更好的做法歡迎留言~~ 因為每個人的 coding 理念都不一樣說不定會激盪出更好的寫法。

對 Reactivity 有興趣的人也可以參考官方的文件,你會發現 reactive, ref 只是冰山一角,後續我也會補上對應的文章做分享!!!

Reference

https://vue3js.cn/vue-composition-api/#reactive

https://composition-api.vuejs.org/#ref-vs-reactive

https://vue3js.cn/reactivity/

--

--