# Vue3 toRef 和 toRefs 函數
Vue3 toRef 和 toRefs 函數
上一篇博文介紹了 vue3 裡面的 ref 函數和 reactive 函數,實現響應式數據,今天主要來說一下 toRef 函數和 toRefs 函數的基本使用。
toRef 函數
通過上一篇部落格,我們知道,ref 函數可以創建一個響應式的數據,那 toRef 函數同樣也是創建一個響應式的數據,那麼他們之間的區別是什麼呢?
首先一點,ref 函數他的本質其實是去拷貝一份數據,脫離了與源數據的交互。什麼意思呢?就是 ref 函數可以將對象裡面的屬性值變成響應式的數據,修改響應式數據,是不會影響到源數據,但是視圖層上的數據會被更新。但是 toRefs 函數的本質是引用,也就是說,toRef 函數會與源數據交互,修改響應式數據會造成源數據的修改,但是他的修改不會造成視圖層數據的更新。
上面這段話理解嗎?不理解的話沒關係,下面通過幾個案例就可以明白了。
toRef 函數使用
首先呢, toRef 函數有兩個參數。
toRef(操作對象, 對象屬性)
好,接下來我們使用 toRef 函數寫一個案例,還是和以前一樣,頁面展示一個用戶的名稱和年紀。
<template>
<div>
<h1>toRef toRefs 函數</h1>
<p>姓名:{{boy_toRef}}</p>
<p>年齡:{{boy.age}}</p>
</div>
</template>
<script>
import { toRef } from 'vue'
export default {
setup() {
const boy = { // 創建一個用戶對象
name: '我是𝒆𝒅.', // 用戶名稱
age: 10 // 用戶年齡
}
// 使用 toRef 函數包裹,操作 boy 對象的 name 屬性
const boy_toRef = toRef(boy, 'name')
console.log(boy_toRef) // 我們直接列印看一下包裹後的數據格式
return { boy, boy_toRef }
}
}
</script>
保存程式碼,刷新頁面。

我們可以看到數據的結構,在 value 裡面直接就是 boy 下面 name 的屬性值,所以說,接下來我們編寫一個按鈕,點擊按鈕,修改這個 name 值。
<template>
<div>
<h1>toRef toRefs 函數</h1>
<p>姓名:{{boy_toRef}}</p>
<p>年齡:{{boy.age}}</p>
<el-button type="primary" @click="btn">修改 name</el-button>
</div>
</template>
<script>
import { toRef } from 'vue'
export default {
setup() {
const boy = {
name: '我是𝒆𝒅.',
age: 10
}
// 這個 boy_toRef 就是被 toRef 函數操作過的 boy 的 name 值
const boy_toRef = toRef(boy, 'name')
const btn = () => {
boy_toRef.value = '𝒆𝒅.' // 把 name 修改成 𝒆𝒅.
console.log(boy_toRef) // 修改完成列印一下結果
}
return { boy, btn, boy_toRef }
}
}
</script>
保存程式碼刷新頁面,然後點擊按鈕看一下頁面效果。

通過截圖展示的效果我們可以發現,boy_toRef 的值確實被修改了,但是呢,頁面並沒有改變,而且頁面也沒有出現錯誤。
這是什麼原因呢? 其實這不是 Bug 哈,在本篇博文開始就說過,toRef 函數會與源數據交互,修改響應式數據會造成源數據的修改,但是他的修改不會造成視圖層數據的更新,所以說,這就是 toRef 函數的功能。確實,視圖沒有數據更新我們通過上面的截圖看到了,但是源數據修改這個怎麼看呢?沒關係,在回答這個問題之前,我們首先得知道,什麼是源數據。
就像上面的程式碼:
const boy = {
name: '我是𝒆𝒅.',
age: 10
}
const boy_toRef = toRef(boy, 'name')
toRef 函數將 boy 對象給包裹了起來,所以說,boy 對象就是源數據。
所以說,想知道源數據有沒有改變,在點擊按鈕之後,列印一下 boy 對象,看一下 boy 有沒有被改變。
<template>
<div>
<h1>toRef toRefs 函數</h1>
<p>姓名:{{boy_toRef}}</p>
<p>年齡:{{boy.age}}</p>
<el-button type="primary" @click="btn">修改 name</el-button>
</div>
</template>
<script>
import { toRef } from 'vue'
export default {
setup() {
const boy = {
name: '我是𝒆𝒅.',
age: 10
}
const boy_toRef = toRef(boy, 'name') // 這個 boy_toRef 就是被 toRef 函數操作過的 boy 的 name 值
const btn = () => {
boy_toRef.value = '𝒆𝒅.' // 把 name 修改成 𝒆𝒅.
console.log(boy_toRef) // 修改完成列印一下結果
console.log(boy) // 修改完成列印一下boy結果
}
return { boy, btn, boy_toRef }
}
}
</script>
保存程式碼,刷新頁面,在點擊按鈕修改 name 值,然後查看一下控制台列印的 boy 對象。

發現 boy 對象的 name 值已經從 我是𝒆𝒅. 改為 𝒆𝒅.了,但是頁面依舊沒有更新。
記住了!
toRef 函數會與源數據交互,修改響應式數據會造成源數據的修改,但是他的修改不會造成視圖層數據的更新。
ref 函數驗證
那 ref 函數可以將對象裡面的屬性值變成響應式的數據,修改響應式數據,是不會影響到源數據,但是視圖層上的數據會被更新 這句話是正確的嘛?上一節我們沒測試,所以說在這裡我們也測試一下。
我們還是寫一個案例,頁面展示一個名稱,點擊按鈕,修改頁面名稱。
<template>
<div>
<h1>ref reactive 函數</h1>
<p>姓名:{{name_ref}}</p>
<el-button type="primary" @click="btn">修改資訊</el-button>
</div>
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
const name = '我是𝒆𝒅.'
const name_ref = ref(name)
const btn = () => {
name_ref.value = '𝒆𝒅.'
console.log(name_ref) // 列印一下被ref包裹的數據
console.log(name) // 列印一下源數據
}
return { name_ref, btn }
}
}
</script>
保存程式碼,刷新頁面,點擊按鈕查看頁面控制台列印的結果,主要是看一下被 ref 函數包裹後的數據有沒有修改成功,源數據有沒有修改成功,最後頁面有沒有修改,下面看截圖。

OK,通過上面截圖,顧忌大家都理解了吧!
所以再記住!
ref 函數可以將對象裡面的屬性值變成響應式的數據,修改響應式數據,是不會影響到源數據,但是視圖層上的數據會被更新
toRefs 函數
toRefs 函數的使用呢,其實和 toRef 函數類似的哈。
- toRefs 函數用於批量設置多個數據為相應是數據。
- toRefs 函數與原始數據相交互,修改響應式數據會影響到源數據,但是不會更新視圖層。
- toRefs 函數還可以與其他響應式數據相交互,更加方便處理視圖層數據。
toRefs 函數使用
老樣子,創建一個對象,然後使用 toRefs 函數包裹,在頁面展示一下。
<template>
<div>
<h1>toRef toRefs 函數</h1>
<p>姓名:{{boy_toRefs.name}}</p>
<p>年齡:{{boy_toRefs.age}}</p>
</div>
</template>
<script>
import { toRefs } from 'vue'
export default {
setup() {
const boy = {
name: '我是𝒆𝒅.',
age: 10
}
const boy_toRefs = toRefs(boy) // 將 boy 用 toRefs 包裹
console.log(boy_toRefs) // 列印一下結果
return { boy_toRefs }
}
}
</script>
保存程式碼,刷新頁面查看。

所以說,我們修改修改一下程式碼,在渲染的時候除了 .屬性 之外,還需要 .value。
<p>姓名:{{boy_toRefs.name.value}}</p>
<p>年齡:{{boy_toRefs.age.value}}</p>
把視圖層程式碼修改一下,然後查看效果。

誒,現在就是正常的啦!
有人可能會疑問,那這玩意兒整的不是越來越複雜了嗎?本來直接點屬性就可以,現在還得點屬性點value,不是多此一舉,脫褲子放P嗎? 嘿嘿嘿!我覺得也是。
為什麼呢說是多此一舉也很正常,因為前面的博文講過,這種複雜結構數據我們完全可以使用 reactive 函數來處理呀,渲染最多點一次就可以,但是 toRefs 函數卻需要點兩次。
<template>
<div>
<h1>toRef toRefs 函數</h1>
<p>姓名:{{boy_toRefs.name}}</p>
<p>年齡:{{boy_toRefs.age}}</p>
</div>
</template>
<script>
import { toRefs, reactive } from 'vue'
export default {
setup() {
const boy = {
name: '我是𝒆𝒅.',
age: 10
}
const boy_toRefs = reactive(boy)
return { boy_toRefs }
}
}
</script>
我們不使用 toRefs 函數,而是用之前說的 reactive 函數處理數據。

我們可以看到,頁面是可以正常解析的,那為什麼我們還有捨近求遠的使用 toRefs 函數呢?
其實是有原因的呀!
其實 toRefs 函數最大的用處在這裡!
我們這個 boy 對象裡面只有兩個參數比較少,如果我們這個對象裡面有十個參數或者是更多的話,每次展示的時候都得寫那麼多遍的 boy 點,是不是很麻煩呢?所以說使用 toRefs 函數就可以解決這個問題,看下面的程式碼。
<template>
<div>
<h1>toRef toRefs 函數</h1>
<p>姓名:{{name}}</p>
<p>年齡:{{age}}</p>
</div>
</template>
<script>
import { toRefs } from 'vue'
export default {
setup() {
const boy = {
name: '我是𝒆𝒅.',
age: 10
}
return { boy_toRefs , ...toRefs(boy)}
}
}
</script>
在 return 拋出 reactive 的時候,使用擴展運算符和 toRefs 函數,就可以實現直接寫屬性的方式展示數據了。

但是呢,深層次的對象依舊需要通過點來實現。
也許你還有疑問,直接擴展運算 reactive 函數也行啊,為啥要套上 toRefs 函數,記住一點呀!
toRefs 函數修改,原始數據被改變,頁面不會被觸發。
看下面程式碼:
<template>
<div>
<h1>toRef toRefs 函數</h1>
<p>姓名:{{name}}</p>
<p>年齡:{{age}}</p>
<el-button type="primary" @click="btn">修改 name</el-button>
</div>
</template>
<script>
import { toRefs, reactive } from 'vue'
export default {
setup() {
const boy = {
name: '我是𝒆𝒅.',
age: 10
}
const new_toRefs = toRefs(boy)
const btn = () => {
new_toRefs.name.value = '𝒆𝒅.'
console.log(boy)
}
return { btn, ...toRefs(boy) }
}
}
</script>
列印一下結果:

從列印結果中可以看出,原始數據被改變,頁面沒有被觸發。但從我的寫法上應該可以注意到,toRefs 返回的對象,隨便解、隨便構,絲毫不會影響值的響應性。
總結
有的小夥伴可能還是不太理解這兩個函數,稍微總結一下子。
- 如果想讓響應式數據和以前的數據關聯起來,並且想在更新響應式數據的時候不更新視圖,那麼就使用 toRef 函數。
- 如果希望將對象的多個屬性都變成響應式數據,並且要求響應式數據和原始數據關聯,並且更新響應式數據的時候不更新視圖,就使用 toRefs 函數用於批量設置多個數據為響應式數據。因為 toRef 函數一次僅能設置一個數據。
- toRefs 函數接收一個對象作為參數,它會遍歷對象身上的所有屬性,然後挨個調用 toRef 函數執行。
好了,今天的內容大體就是這些了,晚安寶子們,明天見!


