【Vue 3】ref 跟 reactive 我該怎麼選!?

Mike
I am Mike
Published in
7 min readOct 7, 2020

隨著 Vue3 的release,大家趨之若鶩的投入Vue3的懷抱,這次Vue3的Composition API 在定義我們的資料的時候要透過 ref 或是 reactive 來定義資料,只不過我們該如何選擇 ref 還是 reactive 呢?

我們先來看看這兩個方法的差異

  • ref : 可以接受任何型態的資料,但是不會對 ref 物件或陣列內部的屬性變動做監聽,因為需要使用 .value 才能取得實際資料物件去監聽。
  • reactive : 只能接受 Object 或 Array,會對內部的屬性變動做深層的監聽,取資料時不需要 .value。

2023/4/15補充: reactive 只能接受物件型別的內容,如果塞入非物件型別的值會出現警告跟你說該值是無法進行響應式的,所以陣列跟物件都算是物件型別喔。

reactive 只能接受物件型別

看到這邊你就可以知道 ref 對於物件或是陣列的改動是不會去監聽的,我們來看看下面的例子…

你會發現我對於 ref 跟 reactive 的兩個物件都透過 setTimeout 去改變物件的內容,然後我針對兩個不同的物件去做watch監控,當 time out 的時候你就會發現實際上有執行到的只有 reactive 的 log

只有執行 reactive 的 watch 的 log

以及我們在使用上面 ref 的資料都需要用 .value 來取得, reactive則不用

所以你可以從這邊去推論出 reactive 會去監控物件內的每一個屬性,而 ref 對於內部的改變並不會每一個屬性都去監控變化,我們也可以看到官方文件上面說

The reactive conversion is "deep": it affects all nested properties. A reactive object also deeply unwraps any properties that are refs while maintaining reactivity.

我們已經知道他在語法上面以及執行上面的差異了,接下來我們來討論下我們在開發上面該怎麼選擇 !

我們先用一個簡單的小例子來說明一般使用上面會如何選擇,首先假設我們這個頁面上面有許多的資料,有的是控制選單開關,有的是紀錄目前某個 tab 的索引…

const isMenuOpen = ref(false);
const tabIdx = ref(0)

像是這樣的資料透過 ref 的包裝可以很簡單的透過 .value 知道說目前的這筆資料是正在進行響應式的操作。

接下來我還有一個給使用者填的表單是需要送出 User 的名字、年齡跟電話,然後還需要把這些資料組起來 render 到另外一個區塊

const name = ref('Mike');
const age = ref(12);
const tel = ref(0910xxxxxx);
const concatData = computed(() => {
return `名稱: ${name.value}, 年齡:${age.value}, 電話: ${tel.value}`
});

上面這樣的操作也是ok的,只是這邊你仔細思考一下,目前定義在組件的相關的資料太過分散了,再加上還有其他的狀態的也會全部放在一起,不覺得整個組件的資料有點亂,造成閱讀上的困擾…

如果這樣的例子我們改透過 reactive 的話會怎麼做呢? 來看看以下例子

const user = reactive({
name: 'Mike',
age: 12,
tel: 0910xxxxxx,
concatData: computed(() => {
return `名稱: ${user.name}, 年齡:${user.age}, 電話: ${user.tel}`
})
})

看到透過 reactive 包裝後的資料連 computed 都包裝了進去,是不是覺得非常的方便,不需要再另外切開去寫computed ,在尋找相依性的資料也變簡單。

你可能會問說,前面有提到 reactive 會去監控物件內的每一個屬性,那放入了一個 computed 不就變成這個屬性又被包了一層?

不~不~不~請聽我娓娓道來,因為 computed 再 return 的時候就是一個 ref 物件,所以當今天放入了reactive 內時,就會被 reactive 自動地的 unwrapped(文件上面中翻叫做解包,只是看起來有點怪怪的)

const count = ref(1)
const obj = reactive({ count })

// ref 解包(unwrapped)
console.log(obj.count === count.value) // true

我們剛剛上面貼出來官方文件的那段話中,其中有一段就是在講這件事。

A reactive object also deeply unwraps any properties that are refs while maintaining reactivity.

這樣是不是對於 ref 跟 reactive 的使用上面有點感覺了呢~

2023/04/16 補充

蠻多人有問到說是不是因為watch沒有加上 { deep : true } 所以 ref 才沒有針對底細物件的變動去做監控 ?

如果需要知道 ref 跟 reactive 的差異,就要在同樣的條件下去比較。

  1. 首先來談談 watch ,官方文件上面是這樣寫的

所以如果今天直接把 ref 跟 reactive 放進去做比較,不需要 { deep : true }就直接能比較出差異。

2. 所以今天如果你直接對 ref 裡面塞的是物件,它因為多包一層 RefImpl.才會因為需要使用 { deep : true }。

結論

我自己現在在選擇的時候多半也是使用ref比較多.只是在很多資料需要統整或是要對 objet 內部的資料去做監控的時候就會選擇用 reactive

其實使用 ref 或是 reactive 並沒有一定的對錯,這兩個方法都將創建響應式資料,你也可以全部用 ref 包也沒有問題,只要你理解如何使用這些方法還有差異性以及如何在模板上面調用,都是 ok 的!

如果對於 ref 跟 reactive 的用法有其他的想發也歡迎留言給我讓我知道!

最近我推出全新的課程 【Nuxt3 高效入門全攻略 】 這是整合了我工作經驗以及過往教學經驗所累積的實用課程,除了滿滿的 Nuxt3 +Vue3 以外,本課程即將推出一個目前市面上所有中文課程都沒有的章節,我特別邀請了 Vue、Vite、Nuxt 的核心團隊成員 Anthony Fu,為我們帶來一個專門探討 Nuxt3 DevTools 的主題,讓你能夠更加深入地了解 Nuxt3 DevTools 相關的開發技巧,詳情可以去課程介紹頁觀看。這是一個非常難得的學習機會,無論你是初學者還是有經驗的開發者,都能從中獲益良多。 有興趣的朋友現在課程在盲鳥預購階段,在這邊我直接提供群內朋友一個直接折抵500元的優惠卷,有任何課程的問題都可以在串貼文詢問我~

Nuxt3 高效入門全攻略 : https://thecodingpro.com/courses/nxut3
折扣碼 500元 : nuxtmike500

最後

我有開設一個 Youtube 的頻道,最近開始了新的系列直播,主要是帶帶各位從目前主流的開發方式直接入手 Vue3,快速學習 Vue3 的使用以及開發,如果是對 Vue3不理解或是目前只有使用 Vue2 的朋友,本直播系列非常適合你服用 ! 那如果喜歡的話不要忘記訂閱我還有打開小鈴鐺及分享給你的朋友~

--

--

Mike
I am Mike

如果有一行code無法解決的bug,那就寫兩行!