Vue3 中 ref、toRef 與 toRefs

前言

ref、toRef 與 toRefs 對於撰寫 Vue3 Composition API 的開發者來講,應該算是非常常見的功能,但是這三者卻各自都有差異,因此寫一篇文章紀錄一下運作。

ref

Vue3 Composition API 後我們最優先接觸的絕對是 ref 與另一個 reactive 這兩個方法,這兩個方法可以幫助我們將變數轉換為雙向綁定的變數

1
2
3
4
5
6
7
8
9
10
11
Vue.createApp({
setup() {
const item = Vue.ref(10);

const add = () => (item.value++);
return {
item,
add,
}
}
}).mount('#app');

修改 ref 的值時必須使用 .value,因為 ref 會將該變數轉換成一個響應式的物件(意指 Proxy 物件),因此若要修改的話則必須透過 .value 的方式。

如果你不想透過這種方式去取得變數值的話,則可以使用 reactive,但不能直接改成 Vue.reactive(10);,因為 reactive 只能針對物件或陣列做雙向綁定監聽,因此必須修改成以下

1
2
3
4
5
6
7
8
9
10
11
Vue.createApp({
setup() {
const item = Vue.reactive({ num: 10 });

const add = () => (item.num++);
return {
item,
add,
}
}
}).mount('#app');

這邊要稍微注意一下 reactive 會針對傳入的資料採用深層監聽的方式,在官方文件中也有提到這一點

该 API 返回一个响应式的对象状态。该响应式转换是“深度转换”

See the Pen ref by Ray (@hsiangfeng) on CodePen.

除此之外這邊好玩的事情是,雖然 reactive 有說到會採用深層監聽的方式,但是如果你改成以下的話則會有一個很妙的事情發生

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Vue.createApp({
setup() {
const item = Vue.ref(10);
const item2 = Vue.reactive({item});

const add = () => (item.value++);
const add2 = () => (item2.item++);
return {
item,
item2,
add,
add2,
}
}
}).mount('#app');

See the Pen Vue3 ref example by Ray (@hsiangfeng) on CodePen.

你會發現 reactive 會維持與 ref 的監聽。

但這邊值得一提的事情是,如果你有一個響應式的變數不想被更動的話,則可以使用 readonly 來避免更動,詳情可見官方文件

toRef

那麼 toRef 稍微有一點跟 ref 很類似,但它可以做到一件事情,也就是針對 reactive 中特定屬性轉換成 ref

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Vue.createApp({
setup() {
const item = Vue.reactive({
myName: 'Ray',
num: 0,
});

const newNum = Vue.toRef(item, 'num');

const add = () => newNum.value++;

return {
newNum,
add
}
}
}).mount('#app');

但要注意的是,它會保持原有的雙向綁定,因此當你修改 newNum 時,item.num 同時也會被修改,反之 item.num 若被修改時 newNum 也會被修改。

See the Pen Vue3 ref to reactive by Ray (@hsiangfeng) on CodePen.

因此 toRef 概念類似將特定屬性拉出來作為一個 ref

toRefs

那麼 toRefs 則是複數的概念,是屬於不指定特定屬性直接整體拉出來作為一個新的 ref,因此會是一個物件形式,如果認真看 console 你會發現結構是一個物件包著 ref

See the Pen Vue3 reactive toRef by Ray (@hsiangfeng) on CodePen.

而這個方式很適合將 reactive 解構後不失去雙向綁定

1
2
3
4
5
6
7
8
9
10
11
12
Vue.createApp({
setup() {
const item = Vue.reactive({
myName: 'Ray',
num: 0,
});

return {
...Vue.toRefs(item),
}
}
}).mount('#app');

這樣你就可以直接在畫面渲染使用 myName 而不是 item.myName

參考文獻

Liker 讚賞

這篇文章如果對你有幫助,你可以花 30 秒登入 LikeCoin 並點擊下方拍手按鈕(最多五下)免費支持與牡蠣鼓勵我。
或者你也可以考慮請我喝一杯咖啡

Google AD

撰寫一篇文章其實真的很花時間,如果你願意「關閉 Adblock (廣告阻擋器)」來支持我的話,我會非常感謝你 ヽ(・∀・)ノ