vue3中ref和reactive的區(qū)別舉例詳解
一、核心機(jī)制對(duì)比
| 特性 | ref | reactive |
|---|---|---|
| 包裝對(duì)象 | RefImpl類實(shí)現(xiàn) | Proxy代理對(duì)象 |
| 響應(yīng)式原理 | 通過 .value 的 getter/setter 攔截 | 深度代理對(duì)象的屬性訪問 |
| 數(shù)據(jù)類型支持 | 任意類型(推薦基本類型) | 僅對(duì)象/數(shù)組/集合類型 |
| 深層響應(yīng)式 | 自動(dòng)展開(對(duì)象會(huì)轉(zhuǎn)為 reactive) | 默認(rèn)深層響應(yīng) |
| 模板自動(dòng)解包 | 支持(頂層屬性自動(dòng)解包) | 不支持(需保持對(duì)象引用) |
| TS類型推斷 | Ref 類型包裹 | 原始類型推斷 |
二、底層實(shí)現(xiàn)剖析
1. reactive簡單實(shí)現(xiàn)
const reactive = (target) => {
return new Proxy(target, {
get(target, key) {
// 依賴收集
track(target, key)
return Reflect.get(target, key)
},
set(target, key, value, receiver) {
const oldValue = target[key]
const result = Reflect.set(target, key, value, receiver)
if (oldValue !== value) {
// 觸發(fā)更新
trigger(target, key)
}
return result
}
// 其他攔截操作...
})
}
- 依賴收集(Track):
- 通過 track 函數(shù)將當(dāng)前正在執(zhí)行的副作用(effect)與目標(biāo)對(duì)象的屬性關(guān)聯(lián)。
- 使用 WeakMap 存儲(chǔ)依賴關(guān)系,結(jié)構(gòu)為:
targetMap = WeakMap({
target: Map({
key: Set(effect1, effect2, ...)
})
});
- 觸發(fā)更新(Trigger):
- 當(dāng)屬性變化時(shí),通過 trigger 函數(shù)找到所有關(guān)聯(lián)的副作用并執(zhí)行。
2. ref 實(shí)現(xiàn)原理
class RefImpl {
constructor(value) {
this._value = isObject(value) ? reactive(value) : value;
this.dep = new Set(); // 依賴集合
}
get value() {
track(this.dep); // 依賴收集
return this._value;
}
set value(newVal) {
if (this._value !== newVal) {
this._value = isObject(newVal) ? reactive(newVal) : newVal;
trigger(this.dep); // 觸發(fā)更新
}
}
}
const ref = (value) => new RefImpl(value);
- 依賴收集與觸發(fā)更新:
- 每個(gè) ref 實(shí)例內(nèi)部維護(hù)一個(gè) dep 集合(類似 reactive 的依賴管理)。
- 當(dāng)通過 .value 訪問時(shí),觸發(fā) track;修改時(shí)觸發(fā) trigger。
- 關(guān)鍵設(shè)計(jì):
- 值類型包裝: 通過 .value 訪問值,解決基本類型無法被 Proxy 直接代理的問題。
- 自動(dòng)解包: 在模板中使用 ref 時(shí),Vue 會(huì)自動(dòng)解包(無需寫 .value)。
三、實(shí)戰(zhàn)場景對(duì)比
1. 基本類型處理
// 正確用法 const count = ref(0) // ? 自動(dòng)類型推斷為 Ref<number> // 錯(cuò)誤嘗試 const count = reactive(0) // ? 參數(shù)必須是對(duì)象類型
2. 對(duì)象類型處理
// ref 處理對(duì)象(自動(dòng)解包)
const user = ref({
name: 'John',
address: {
city: 'New York'
}
})
// 等效于 reactive 寫法
const user = reactive({
name: 'John',
address: reactive({
city: 'New York'
})
})
// 訪問方式對(duì)比
console.log(user.value.name) // ref 需要 .value
console.log(user.name) // reactive 直接訪問
3. 數(shù)組處理
// ref 數(shù)組 const listRef = ref([1, 2, 3]) listRef.value.push(4) // ? 正確修改方式 // reactive 數(shù)組 const listReactive = reactive([1, 2, 3]) listReactive.push(4) // ? 直接操作
四、高級(jí)特性差異
1. 響應(yīng)式丟失問題
// reactive 的解構(gòu)問題
const state = reactive({ count: 0 })
const { count } = state // ? 失去響應(yīng)性
// ref 的 解構(gòu)
const countRef = ref(0)
const count = countRef // ? 仍需通過 .value 訪問
// 正確解構(gòu)方式(reactive)
const state = reactive({ count: 0 })
const countRef = toRef(state, 'count') // ? 保持響應(yīng)
2. 類型替換場景
// ref 允許整體替換
const data = ref({ items: [] })
data.value = fetchData() // ? 響應(yīng)式更新
// reactive 需保持引用
const data = reactive({ items: [] })
Object.assign(data, fetchData()) // ? 正確方式
data = fetchData() ? 破壞響應(yīng)性
五、性能對(duì)比分析
| 操作 | ref(基本類型) | reactive(對(duì)象) | 差異原因 |
|---|---|---|---|
| 創(chuàng)建速度 | ★★★★☆ | ★★★☆☆ | Proxy初始化成本較高 |
| 屬性訪問 | ★★★★☆ | ★★★☆☆ | Proxy攔截帶來額外開銷 |
| 深層監(jiān)聽 | ★☆☆☆☆ | ★★★★★ | Proxy自動(dòng)深度代理 |
| 內(nèi)存占用 | 較低 | 較高 | Proxy對(duì)象占用更多內(nèi)存 |
六、最佳實(shí)踐
1. 組合式函數(shù)規(guī)范
// 推薦返回 ref 保持靈活性
function useCounter(initial = 0) {
const count = ref(initial)
return { count }
}
// 復(fù)雜狀態(tài)使用 reactive + toRefs
function useUser() {
const state = reactive({
name: '',
age: ''
})
return { ...toRefs(state) }
}
2. 表單處理策略
// 復(fù)雜表單使用 reactive
const form = reactive({
username: '',
password: '',
preferences: {
theme: 'light',
notifications: true
}
})
// 單個(gè)表單字段使用 ref
const searchQuery = ref('')
七、特殊場景處理
1. DOM 引用
獲取元素對(duì)象或者組件對(duì)象只能使用 ref ,reactive無法處理 DOM 引用
<script setup> const inputRef = ref(null) </script> <templete> <div ref="inputRef"></div> </templete>
2. 第三方庫集成
// 需要保持引用的場景
const chartInstance = ref(null)
// 響應(yīng)式配置對(duì)象
const options = reacitve({
title: { text: '熱銷' },
series: [ ... ]
})
八、總結(jié)選擇策略
1. 優(yōu)先使用 ref 的場景
- 基本類型值(string/number/boolean)
- 需要模板自動(dòng)解包
- 可能被整體替換的對(duì)象
- 需要傳遞給composable函數(shù)的參數(shù)
2. 優(yōu)先使用 reactive 的場景
- 復(fù)雜嵌套對(duì)象
- 需要深度響應(yīng)式監(jiān)聽
- 表單/配置對(duì)象等結(jié)構(gòu)化數(shù)據(jù)
- 需要直接操作集合類型(Map/Set)
3. 混合使用
3.1 reactive 嵌套 ref
const state = reactive({
count: ref(0), // 基礎(chǔ)類型 ref ,自動(dòng)解包
user: ref<User>({ name: '' }) // 對(duì)象類型 ref ,自動(dòng)解包
})
- 行為特點(diǎn): Vue 會(huì)自動(dòng)解包嵌套在 reactive 中的 ref,訪問時(shí)無需 .value
state.count++ // 直接操作數(shù)字(等價(jià)于 state.count.value++) state.user.name = 'john' // 直接操作對(duì)象(等價(jià)于 state.user.value.name)
- 適用場景
- 混合響應(yīng)式類型: 當(dāng) reactive 對(duì)象需要包含基礎(chǔ)類型 + 對(duì)象類型的混合數(shù)據(jù)時(shí),用 ref 統(tǒng)一包裹,享受自動(dòng)解包特性。
- 外部數(shù)據(jù)注入: 當(dāng)某個(gè)屬性需要從外部接收 ref 類型時(shí)(如組合式函數(shù)返回的 ref),直接將其嵌入 reactive 對(duì)象。
3.2 ref 嵌套 reactive
const complexRef = ref({
data: reactive({ /*...*/ }), // 嵌套 reactive 對(duì)象
status: 'loading' // 普通屬性(非響應(yīng)式)
})
- 行為特點(diǎn):
- 層級(jí)訪問: 需要 .value 訪問容器內(nèi)內(nèi)容
- 響應(yīng)式范圍:
- complexRef.value.data 是響應(yīng)式對(duì)象(reactive 創(chuàng)建)
- complexRef.value.status 是普通字符串(非響應(yīng)式)
complexRef.value.data.key = "value"; // 直接修改 reactive 對(duì)象 complexRef.value.status = "success"; // 修改普通屬性(非響應(yīng)式)
- 適用場景:
- 整體替換響應(yīng)式對(duì)象 :如果需要完全替換整個(gè)響應(yīng)式對(duì)象,用 ref 包裹可以保持引用。
- 組合非響應(yīng)式數(shù)據(jù): 當(dāng)需要混合響應(yīng)式和非響應(yīng)式數(shù)據(jù)時(shí),ref 作為容器更靈活。
complexRef.value = { // 替換整個(gè)對(duì)象(觸發(fā)響應(yīng)式更新)
data: reactive({ /*...*/ }),
status: 'success'
};總結(jié)
到此這篇關(guān)于vue3中ref和reactive的區(qū)別詳解的文章就介紹到這了,更多相關(guān)vue3中ref和reactive區(qū)別內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Vue3.0中Ref與Reactive的區(qū)別示例詳析
- vue3?中ref和reactive的區(qū)別講解
- 前端vue3中的ref與reactive用法及區(qū)別總結(jié)
- Vue3 的ref和reactive的用法和區(qū)別示例解析
- Vue3中ref和reactive的基本使用及區(qū)別詳析
- Vue3中ref和reactive的區(qū)別及說明
- vue3.0中ref與reactive的區(qū)別及使用場景分析
- Vue3中關(guān)于ref和reactive的區(qū)別分析
- vue3中reactive和ref的實(shí)現(xiàn)與區(qū)別詳解
- vue3 ref 和reactive的區(qū)別詳解
相關(guān)文章
vue中實(shí)現(xiàn)頁面刷新以及局部刷新的方法
這篇文章主要給大家介紹了關(guān)于vue中實(shí)現(xiàn)頁面刷新以及局部刷新的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2022-01-01
elementui使用el-upload組件如何實(shí)現(xiàn)自定義上傳
這篇文章主要介紹了elementui使用el-upload組件如何實(shí)現(xiàn)自定義上傳,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-08-08
Vue中如何實(shí)現(xiàn)在線預(yù)覽word文件、excel文件
這篇文章主要介紹了Vue中如何實(shí)現(xiàn)在線預(yù)覽word文件、excel文件,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-07-07
詳解關(guān)于Vue2.0路由開啟keep-alive時(shí)需要注意的地方
這篇文章主要介紹了關(guān)于Vue2.0路由開啟keep-alive時(shí)需要注意的地方,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-09-09
elementUI Tree 樹形控件單選實(shí)現(xiàn)示例
在ElementUI中樹形控件本身不支持單選功能,本文就來介紹一下如何實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-06-06
vue插件v-touch的坑及解決(不能上下滑動(dòng))
這篇文章主要介紹了vue插件v-touch的坑及解決(不能上下滑動(dòng)),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03

