vue3中ref和reactive的區(qū)別舉例詳解
一、核心機制對比
| 特性 | ref | reactive |
|---|---|---|
| 包裝對象 | RefImpl類實現(xiàn) | Proxy代理對象 |
| 響應式原理 | 通過 .value 的 getter/setter 攔截 | 深度代理對象的屬性訪問 |
| 數(shù)據(jù)類型支持 | 任意類型(推薦基本類型) | 僅對象/數(shù)組/集合類型 |
| 深層響應式 | 自動展開(對象會轉(zhuǎn)為 reactive) | 默認深層響應 |
| 模板自動解包 | 支持(頂層屬性自動解包) | 不支持(需保持對象引用) |
| TS類型推斷 | Ref 類型包裹 | 原始類型推斷 |
二、底層實現(xiàn)剖析
1. reactive簡單實現(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ù)將當前正在執(zhí)行的副作用(effect)與目標對象的屬性關(guān)聯(lián)。
- 使用 WeakMap 存儲依賴關(guān)系,結(jié)構(gòu)為:
targetMap = WeakMap({
target: Map({
key: Set(effect1, effect2, ...)
})
});
- 觸發(fā)更新(Trigger):
- 當屬性變化時,通過 trigger 函數(shù)找到所有關(guān)聯(lián)的副作用并執(zhí)行。
2. ref 實現(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ā)更新:
- 每個 ref 實例內(nèi)部維護一個 dep 集合(類似 reactive 的依賴管理)。
- 當通過 .value 訪問時,觸發(fā) track;修改時觸發(fā) trigger。
- 關(guān)鍵設(shè)計:
- 值類型包裝: 通過 .value 訪問值,解決基本類型無法被 Proxy 直接代理的問題。
- 自動解包: 在模板中使用 ref 時,Vue 會自動解包(無需寫 .value)。
三、實戰(zhàn)場景對比
1. 基本類型處理
// 正確用法 const count = ref(0) // ? 自動類型推斷為 Ref<number> // 錯誤嘗試 const count = reactive(0) // ? 參數(shù)必須是對象類型
2. 對象類型處理
// ref 處理對象(自動解包)
const user = ref({
name: 'John',
address: {
city: 'New York'
}
})
// 等效于 reactive 寫法
const user = reactive({
name: 'John',
address: reactive({
city: 'New York'
})
})
// 訪問方式對比
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) // ? 直接操作
四、高級特性差異
1. 響應式丟失問題
// reactive 的解構(gòu)問題
const state = reactive({ count: 0 })
const { count } = state // ? 失去響應性
// ref 的 解構(gòu)
const countRef = ref(0)
const count = countRef // ? 仍需通過 .value 訪問
// 正確解構(gòu)方式(reactive)
const state = reactive({ count: 0 })
const countRef = toRef(state, 'count') // ? 保持響應
2. 類型替換場景
// ref 允許整體替換
const data = ref({ items: [] })
data.value = fetchData() // ? 響應式更新
// reactive 需保持引用
const data = reactive({ items: [] })
Object.assign(data, fetchData()) // ? 正確方式
data = fetchData() ? 破壞響應性
五、性能對比分析
| 操作 | ref(基本類型) | reactive(對象) | 差異原因 |
|---|---|---|---|
| 創(chuàng)建速度 | ★★★★☆ | ★★★☆☆ | Proxy初始化成本較高 |
| 屬性訪問 | ★★★★☆ | ★★★☆☆ | Proxy攔截帶來額外開銷 |
| 深層監(jiān)聽 | ★☆☆☆☆ | ★★★★★ | Proxy自動深度代理 |
| 內(nèi)存占用 | 較低 | 較高 | Proxy對象占用更多內(nèi)存 |
六、最佳實踐
1. 組合式函數(shù)規(guī)范
// 推薦返回 ref 保持靈活性
function useCounter(initial = 0) {
const count = ref(initial)
return { count }
}
// 復雜狀態(tài)使用 reactive + toRefs
function useUser() {
const state = reactive({
name: '',
age: ''
})
return { ...toRefs(state) }
}
2. 表單處理策略
// 復雜表單使用 reactive
const form = reactive({
username: '',
password: '',
preferences: {
theme: 'light',
notifications: true
}
})
// 單個表單字段使用 ref
const searchQuery = ref('')
七、特殊場景處理
1. DOM 引用
獲取元素對象或者組件對象只能使用 ref ,reactive無法處理 DOM 引用
<script setup> const inputRef = ref(null) </script> <templete> <div ref="inputRef"></div> </templete>
2. 第三方庫集成
// 需要保持引用的場景
const chartInstance = ref(null)
// 響應式配置對象
const options = reacitve({
title: { text: '熱銷' },
series: [ ... ]
})
八、總結(jié)選擇策略
1. 優(yōu)先使用 ref 的場景
- 基本類型值(string/number/boolean)
- 需要模板自動解包
- 可能被整體替換的對象
- 需要傳遞給composable函數(shù)的參數(shù)
2. 優(yōu)先使用 reactive 的場景
- 復雜嵌套對象
- 需要深度響應式監(jiān)聽
- 表單/配置對象等結(jié)構(gòu)化數(shù)據(jù)
- 需要直接操作集合類型(Map/Set)
3. 混合使用
3.1 reactive 嵌套 ref
const state = reactive({
count: ref(0), // 基礎(chǔ)類型 ref ,自動解包
user: ref<User>({ name: '' }) // 對象類型 ref ,自動解包
})
- 行為特點: Vue 會自動解包嵌套在 reactive 中的 ref,訪問時無需 .value
state.count++ // 直接操作數(shù)字(等價于 state.count.value++) state.user.name = 'john' // 直接操作對象(等價于 state.user.value.name)
- 適用場景
- 混合響應式類型: 當 reactive 對象需要包含基礎(chǔ)類型 + 對象類型的混合數(shù)據(jù)時,用 ref 統(tǒng)一包裹,享受自動解包特性。
- 外部數(shù)據(jù)注入: 當某個屬性需要從外部接收 ref 類型時(如組合式函數(shù)返回的 ref),直接將其嵌入 reactive 對象。
3.2 ref 嵌套 reactive
const complexRef = ref({
data: reactive({ /*...*/ }), // 嵌套 reactive 對象
status: 'loading' // 普通屬性(非響應式)
})
- 行為特點:
- 層級訪問: 需要 .value 訪問容器內(nèi)內(nèi)容
- 響應式范圍:
- complexRef.value.data 是響應式對象(reactive 創(chuàng)建)
- complexRef.value.status 是普通字符串(非響應式)
complexRef.value.data.key = "value"; // 直接修改 reactive 對象 complexRef.value.status = "success"; // 修改普通屬性(非響應式)
- 適用場景:
- 整體替換響應式對象 :如果需要完全替換整個響應式對象,用 ref 包裹可以保持引用。
- 組合非響應式數(shù)據(jù): 當需要混合響應式和非響應式數(shù)據(jù)時,ref 作為容器更靈活。
complexRef.value = { // 替換整個對象(觸發(fā)響應式更新)
data: reactive({ /*...*/ }),
status: 'success'
};總結(jié)
到此這篇關(guān)于vue3中ref和reactive的區(qū)別詳解的文章就介紹到這了,更多相關(guān)vue3中ref和reactive區(qū)別內(nèi)容請搜索腳本之家以前的文章或繼續(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的實現(xiàn)與區(qū)別詳解
- vue3 ref 和reactive的區(qū)別詳解
相關(guān)文章
詳解從vue-loader源碼分析CSS Scoped的實現(xiàn)
這篇文章主要介紹了詳解從vue-loader源碼分析CSS Scoped的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-09-09
前端項目vue3/React如何使用pako庫解壓縮后端返回gzip數(shù)據(jù)
pako是一個流行的JS庫,用于在瀏覽器中進行數(shù)據(jù)壓縮和解壓縮操作,它提供了對常見的壓縮算法的實現(xiàn),使開發(fā)者能夠在客戶端上輕松進行數(shù)據(jù)壓縮和解壓縮,這篇文章主要介紹了前端項目vue3/React使用pako庫解壓縮后端返回gzip數(shù)據(jù),需要的朋友可以參考下2024-07-07
vue3+ts+axios+pinia實現(xiàn)無感刷新方式
這篇文章主要介紹了vue3+ts+axios+pinia實現(xiàn)無感刷新方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-04-04
vue2.0和mintui-infiniteScroll結(jié)合如何實現(xiàn)無線滾動加載
這篇文章主要介紹了vue2.0和mintui-infiniteScroll結(jié)合如何實現(xiàn)無線滾動加載,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-10-10
vue如何解決sass-loader的版本過高導致的編譯錯誤
這篇文章主要介紹了vue如何解決sass-loader的版本過高導致的編譯錯誤問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-06-06
vue如何導出json數(shù)據(jù)為excel表格并保存到本地
這篇文章主要介紹了vue如何導出json數(shù)據(jù)為excel表格并保存到本地問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-07-07
vue3 ts打包失敗ts類型校驗未通過導致打包失敗的解決方案
這篇文章主要介紹了vue3 ts打包失敗ts類型校驗未通過導致打包失敗的解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2025-06-06

