在上篇我們提到:
當用 ref
綁定響應式的數據,會產生一個 RefImpl
的物件,而它提供了 value
屬性讓我們存取其中的值。
不過使用的兩個範例都是綁定了數字,今天我們一起來試試看將 ref
綁定「物件」。
但在這之前~請先關注一下它是怎麼運作的!
開始!
官方文件:這是通過一個基於依賴追蹤的響應式系統實現的。當一個組件首次渲染時,Vue 會追蹤在渲染過程中使用的每一個 ref。然後,當一個 ref 被修改時,它會觸發追蹤它的組件的一次重新渲染。
在標準的 JavaScript 中,檢測普通變量的訪問或修改是行不通的。然而,我們可以通過 getter 和 setter 方法來攔截對象屬性的 get 和 set 操作。
該 .value 屬性給予了 Vue 一個機會來檢測 ref 何時被訪問或修改。在其內部,Vue 在它的 getter 中執行追蹤,在它的 setter 中執行觸發。
從概念上講,你可以將 ref 看作是一個像這樣的對象:
// 偽代碼,並非真正的實現
const myRef = {
_value: 0,
get value() {
track()
return this._value
},
set value(newValue) {
this._value = newValue
trigger()
}
}
這邊的 getter、setter 是原生 JS 的用法,而我們來解釋一下這段偽程式碼想表達什麼:
_value
:這裡儲存當前的值。get value()
:存取、紀錄響應式狀態依賴的值。ref
的值時,getter
函式會被觸發。track()
來記得所有依賴這個值的地方。_value
。set value(newValue)
:更新任何有關 getter
記錄下來的所有位置。ref
的值時,setter
函式會被觸發。_value
的值。trigger()
通知系統,去觸發 getter
中紀錄的所有依賴這個 value
的地方,去進行重新渲染和更新(例如資料或介面)。getter
:是「依賴追蹤」的攔截器。getter
會被觸發,紀錄所有依賴這個數據的地方,並回傳值。setter
:是「重新渲染」的觸發機制。setter
會更新值,並通知系統重新渲染依賴這個數據的 UI。昨天 ++ 量完體重後,去諮詢了營養師!
營養師要求 ++ 在「體重紀錄表上記錄他的體重」,且營養師會「根據這個體重,即時制定鏟肉計畫」。
可以用這個情境模擬響應式系統的工作模式:
getter
):紀錄體重(_value
),當 ++ 想知道自己的體重的時候,就可以來這邊查閱(就像使用 getter
獲取 _value
)。setter
):營養師知道了 ++ 的體重(來自 getter
中紀錄的 _value
),會根據這個體重的情況調整飲食建議和計畫(就像使用 setter
更新數據和介面)。以上的偽程式碼,是官方文件用原生 JS 的用法 getter
、setter
來比喻資料變動後,是如何更改介面的響應式原理。
在 Vue 2 中主要就是用此種實現方式,而在 Vue 3 中主要使用 Proxy
物件的操作。
想了解更細的先坐火箭到這裡!
官方文件:Ref 可以持有任何類型的值,包括深層嵌套的對象、數組或者 JavaScript 內置的數據結構,例如 Map。
非原始值將通過 reactive() 轉換為響應式代理,該函數將在後面討論。
上篇範例一、二的操作,是使用「字串、數字等等」這種「原始值」綁定的值。
而「深層」指的是:使用「物件」這種類型來綁定的值。
MDN:除了物件以外的所有值,都是原始定義的值(值意味著不能被改變)。
因此 ref
的深層響應性,就是 ref
綁定一個「物件」的這個行為。
我們馬上用「物件」來做範例:
<script setup>
import { ref } from "vue";
const obj = { title: "今天是鐵人賽第 12 天" };
const refObj = ref(obj);
console.log(`refObj`, refObj);
</script>
瀏覽器上會看到:
_value
處,有個 Proxy
,然後它看起來是個物件⋯⋯
(迷惑中)
如上篇所證實的:
在 <template>
使用 ref
綁定的值時, Vue 會幫我們解包,讓我們直接拿到 RefImpl
中 _value
的值。
但現在 _value
的值是一個 Proxy
物件耶?
先以原預期的方式操作試試看:
<script setup>
import { ref } from "vue";
const obj = { title: "今天是鐵人賽第 12 天" };
const refObj = ref(obj);
console.log(`refObj`, refObj);
</script>
<template>{{ refObj }}</template>
哭了,拿不到。
不過⋯⋯如果 Proxy
是個物件⋯⋯那我應該可以 .title
拿到值?
<script setup>
import { ref } from "vue";
const obj = { title: "今天是鐵人賽第 12 天" };
const refObj = ref(obj);
console.log(`refObj`, refObj);
</script>
<template>
<p>用 refObj 取值:{{ refObj }}</p>
<p>用 refObj.title 取值:{{ refObj.title }}</p>
</template>
拿到了!
因此證實了:當 ref
綁定了「物件」,是不會解包這個物件中的 Proxy
物件喔。
難道是上述說的
非原始值將通過 reactive() 轉換為響應式代理
其中的轉換工具嗎(這不是故意要營造氣氛,是我真的也還不知道 XD
相信大家看到這邊會覺得怎麼有點爛尾 QQ
配速的小蝸牛明天就來講 Proxy
到底是什麼囉!
https://github.com/Jamixcs/2024iThome-jamixcs/tree/main/src/components/day12