vue3中ref和reactive的區(qū)別舉例詳解
一、核心機(jī)制對比
特性 | ref | reactive |
---|---|---|
包裝對象 | RefImpl類實(shí)現(xiàn) | Proxy代理對象 |
響應(yīng)式原理 | 通過 .value 的 getter/setter 攔截 | 深度代理對象的屬性訪問 |
數(shù)據(jù)類型支持 | 任意類型(推薦基本類型) | 僅對象/數(shù)組/集合類型 |
深層響應(yīng)式 | 自動展開(對象會轉(zhuǎn)為 reactive) | 默認(rèn)深層響應(yīng) |
模板自動解包 | 支持(頂層屬性自動解包) | 不支持(需保持對象引用) |
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)對象的屬性關(guān)聯(lián)。
- 使用 WeakMap 存儲依賴關(guān)系,結(jié)構(gòu)為:
targetMap = WeakMap({ target: Map({ key: Set(effect1, effect2, ...) }) });
- 觸發(fā)更新(Trigger):
- 當(dāng)屬性變化時,通過 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ā)更新:
- 每個 ref 實(shí)例內(nèi)部維護(hù)一個 dep 集合(類似 reactive 的依賴管理)。
- 當(dāng)通過 .value 訪問時,觸發(fā) track;修改時觸發(fā) trigger。
- 關(guān)鍵設(shè)計(jì):
- 值類型包裝: 通過 .value 訪問值,解決基本類型無法被 Proxy 直接代理的問題。
- 自動解包: 在模板中使用 ref 時,Vue 會自動解包(無需寫 .value)。
三、實(shí)戰(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. 響應(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)性
五、性能對比分析
操作 | ref(基本類型) | reactive(對象) | 差異原因 |
---|---|---|---|
創(chuàng)建速度 | ★★★★☆ | ★★★☆☆ | Proxy初始化成本較高 |
屬性訪問 | ★★★★☆ | ★★★☆☆ | Proxy攔截帶來額外開銷 |
深層監(jiān)聽 | ★☆☆☆☆ | ★★★★★ | Proxy自動深度代理 |
內(nèi)存占用 | 較低 | 較高 | Proxy對象占用更多內(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 } }) // 單個表單字段使用 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) // 響應(yīng)式配置對象 const options = reacitve({ title: { text: '熱銷' }, series: [ ... ] })
八、總結(jié)選擇策略
1. 優(yōu)先使用 ref 的場景
- 基本類型值(string/number/boolean)
- 需要模板自動解包
- 可能被整體替換的對象
- 需要傳遞給composable函數(shù)的參數(shù)
2. 優(yōu)先使用 reactive 的場景
- 復(fù)雜嵌套對象
- 需要深度響應(yīng)式監(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 ,自動解包 })
- 行為特點(diǎn): Vue 會自動解包嵌套在 reactive 中的 ref,訪問時無需 .value
state.count++ // 直接操作數(shù)字(等價于 state.count.value++) state.user.name = 'john' // 直接操作對象(等價于 state.user.value.name)
- 適用場景
- 混合響應(yīng)式類型: 當(dāng) reactive 對象需要包含基礎(chǔ)類型 + 對象類型的混合數(shù)據(jù)時,用 ref 統(tǒng)一包裹,享受自動解包特性。
- 外部數(shù)據(jù)注入: 當(dāng)某個屬性需要從外部接收 ref 類型時(如組合式函數(shù)返回的 ref),直接將其嵌入 reactive 對象。
3.2 ref 嵌套 reactive
const complexRef = ref({ data: reactive({ /*...*/ }), // 嵌套 reactive 對象 status: 'loading' // 普通屬性(非響應(yīng)式) })
- 行為特點(diǎn):
- 層級訪問: 需要 .value 訪問容器內(nèi)內(nèi)容
- 響應(yīng)式范圍:
- complexRef.value.data 是響應(yīng)式對象(reactive 創(chuàng)建)
- complexRef.value.status 是普通字符串(非響應(yīng)式)
complexRef.value.data.key = "value"; // 直接修改 reactive 對象 complexRef.value.status = "success"; // 修改普通屬性(非響應(yīng)式)
- 適用場景:
- 整體替換響應(yīng)式對象 :如果需要完全替換整個響應(yīng)式對象,用 ref 包裹可以保持引用。
- 組合非響應(yīng)式數(shù)據(jù): 當(dāng)需要混合響應(yīng)式和非響應(yīng)式數(shù)據(jù)時,ref 作為容器更靈活。
complexRef.value = { // 替換整個對象(觸發(fā)響應(yīng)式更新) 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的實(shí)現(xiàn)與區(qū)別詳解
- vue3 ref 和reactive的區(qū)別詳解
相關(guān)文章
vue中實(shí)現(xiàn)頁面刷新以及局部刷新的方法
這篇文章主要給大家介紹了關(guān)于vue中實(shí)現(xiàn)頁面刷新以及局部刷新的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2022-01-01elementui使用el-upload組件如何實(shí)現(xiàn)自定義上傳
這篇文章主要介紹了elementui使用el-upload組件如何實(shí)現(xiàn)自定義上傳,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-08-08Vue中如何實(shí)現(xiàn)在線預(yù)覽word文件、excel文件
這篇文章主要介紹了Vue中如何實(shí)現(xiàn)在線預(yù)覽word文件、excel文件,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-07-07詳解關(guān)于Vue2.0路由開啟keep-alive時需要注意的地方
這篇文章主要介紹了關(guān)于Vue2.0路由開啟keep-alive時需要注意的地方,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-09-09elementUI Tree 樹形控件單選實(shí)現(xiàn)示例
在ElementUI中樹形控件本身不支持單選功能,本文就來介紹一下如何實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-06-06