Vue 3 reactive 和 ref 區別及 失去響應性問題

在 Vue 3 中,reactive?和?ref?是實現響應式數據的兩個核心 API,它們的設計目標和使用場景有所不同。以下是兩者的詳細對比:

1. 基本定義與核心功能

特性reactiveref
作用創建對象類型的響應式代理(對象、數組、Map 等)創建一個響應式的引用,可用于基本類型或對象
返回值原始對象的代理(Proxy包含?value?屬性的響應式對象(如?{ value: xxx }
響應式原理基于對象的屬性劫持(Proxy?+?Reflect基于?getter/setter?劫持,基本類型和對象統一處理
解包機制無(直接訪問屬性)對象會自動解包為?reactive,基本類型需通過?.value?訪問

2. 詳細區別

(1) 適用數據類型
  • reactive
    僅用于對象類型(包括普通對象、數組、MapSet?等),無法直接處理基本類型(如?stringnumberboolean)。

    javascript

    // 正確:對象類型
    const state = reactive({ count: 0 });
    // 錯誤:不能直接處理基本類型(會被視為對象屬性名)
    const num = reactive(1); // 報錯:reactive() expects an object
    
  • ref
    可處理所有類型(基本類型或對象)。對于對象,ref?內部會自動通過?reactive?轉為響應式代理(即 “自動解包”)。

    javascript

    // 基本類型
    const count = ref(0);
    // 對象類型(內部自動轉為 reactive 代理)
    const obj = ref({ name: 'Vue' });
    
(2) 訪問方式
  • reactive
    直接通過屬性訪問,無需額外語法:

    javascript

    const state = reactive({ count: 0 });
    state.count = 1; // 直接修改屬性
    
  • ref

    • 基本類型:需通過?.value?訪問和修改:

      javascript

      const count = ref(0);
      count.value = 1; // 修改需通過 .value
      
    • 對象類型:由于自動解包,可直接訪問屬性(內部已轉為?reactive?代理):

      javascript

      const obj = ref({ name: 'Vue' });
      obj.value.name = 'Vue 3'; // 直接修改屬性(等價于 reactive 的操作)
      // 或通過解包后訪問(模板中無需 .value)
      
(3) 響應式解包規則
  • 在模板中

    • reactive?創建的對象:直接通過屬性名訪問,無需特殊處理。
    • ref?創建的值:
      • 基本類型:模板中會自動解包,直接使用?{{ count }}?即可(無需?{{ count.value }})。
      • 對象類型:同樣自動解包,等價于?reactive,直接訪問屬性(如?{{ obj.name }})。
  • 在 JavaScript 中

    • 若從?ref?中解構屬性,需使用?toRefs?或手動保留?ref?引用,否則會失去響應式:

      javascript

      const obj = ref({ name: 'Vue', age: 3 });
      const { name } = obj.value; // 錯誤:name 是普通值,失去響應式
      const { name } = toRefs(obj.value); // 正確:通過 toRefs 保持響應式
      
(4) 重新賦值與響應式
  • reactive
    代理對象指向固定,不能直接重新賦值為新對象(否則會失去響應式),需通過修改屬性實現更新:

    javascript

    const state = reactive({ count: 0 });
    state = { count: 1 }; // 錯誤:直接賦值會導致響應式丟失
    state.count = 1; // 正確:修改屬性
    
  • ref
    可以重新賦值為新值(基本類型或對象),響應式會自動更新:

    javascript

    const count = ref(0);
    count.value = 1; // 基本類型重新賦值(正確)const obj = ref({ name: 'Vue' });
    obj.value = { name: 'React' }; // 對象重新賦值(正確,內部會重新創建 reactive 代理)
    
(5) 組合式 API 中的使用
  • reactive
    適合定義包含多個屬性的對象狀態,常用于復雜狀態管理:

    javascript

    setup() {const state = reactive({count: 0,user: { name: 'Alice' }});return { state }; // 模板中通過 state.count 訪問
    }
    
  • ref
    適合定義單個值(基本類型或對象),或需要在函數間傳遞的獨立狀態,且返回時無需嵌套對象:

    javascript

    setup() {const count = ref(0);const user = ref({ name: 'Alice' });return { count, user }; // 模板中直接使用 count 和 user.name
    }
    

3. 典型使用場景

場景reactive?更合適ref?更合適
定義對象 / 數組狀態?(reactive({ a: 1, b: 2 })?(ref({ a: 1, b: 2 }),自動解包)
定義基本類型狀態(如計數)?(不支持)?(ref(0)
函數返回單個響應式值?(需返回對象)?(直接返回?ref(0),模板自動解包)
解構響應式對象并保持響應式需要配合?toRefsconst { a, b } = toRefs(state)直接解構?ref(如?const { value } = count,但通常無需解構)
動態創建響應式變量需手動構建對象直接使用?ref?包裹任意值

4. 最佳實踐

  1. 基本類型用?ref
    無論何時需要響應式的基本類型(如?countisLoading),直接使用?ref

    javascript

    const count = ref(0);
    const isLoading = ref(false);
    
  2. 對象 / 數組用?ref?或?reactive

    • 若狀態是單個對象 / 數組,推薦用?ref(統一接口,自動解包):

      javascript

      const user = ref({ name: 'Vue', age: 3 });
      
    • 若需要在一個對象中整合多個狀態(如表單數據),可用?reactive

      javascript

      const form = reactive({name: 'Alice',email: 'alice@example.com'
      });
      
  3. 解構時保持響應式
    當從?reactive?對象中解構屬性時,必須使用?toRefs?避免失去響應式:

    javascript

    const state = reactive({ a: 0, b: 0 });
    const { a, b } = toRefs(state); // a 和 b 是 ref,保持響應式
    
  4. 避免混合使用導致混亂

    • 盡量統一風格:若狀態是對象,要么全用?reactive,要么全用?ref?包裹對象。
    • 優先使用?ref?包裹對象:因為?ref?可以無縫處理基本類型和對象,且返回值更簡潔(無需嵌套對象)。

總結

  • ref?是更通用的選擇:可以處理所有數據類型,對象會自動解包為?reactive,且在組合式 API 中返回更方便(直接返回獨立的?ref?變量)。
  • reactive?適合復雜對象:當需要定義包含多個屬性的對象狀態,且不想通過?.value?訪問時(雖然?ref?包裹對象后也無需?.value,但?reactive?更直接)。

理解兩者的核心區別后,可根據具體場景選擇最合適的 API,避免響應式失效問題(如之前代碼中?reactive?對象直接賦值導致的更新失敗,改用?ref?或正確更新屬性即可解決)。

====================================================================


當需要整體更新?reactive?對象中的某個嵌套對象(例如從接口獲取全新的用戶數據并覆蓋原有對象),同時又要保持響應式時,關鍵是不替換整個?reactive?對象的引用,而是通過以下兩種方式更新其屬性:

方法一:使用?Object.assign?合并新對象(推薦)

適用場景:
  • 已有?reactive?對象(如?state.user?是?reactive?創建的代理對象)。
  • 需要用新對象的屬性覆蓋或補充原有對象的屬性,而非完全替換整個對象的引用。
示例:

javascript

import { reactive } from 'vue';// 初始化:state 是 reactive 對象,user 是嵌套對象
const state = reactive({user: { name: 'Alice', age: 30 }
});// 假設從接口獲取新用戶數據(普通對象)
const newUser = { name: 'Bob', age: 35, city: 'Beijing' };// 正確寫法:將新屬性合并到現有 reactive 對象中(保持代理引用)
Object.assign(state.user, newUser);
// 等價于:state.user = { ...state.user, ...newUser }; (但此寫法錯誤,見下方說明)
關鍵原理:
  • Object.assign(target, source)?會將?source?的屬性直接復制到?target(即?state.user?代理對象),不改變?state.user?的引用,因此響應式得以保留。
  • 錯誤寫法state.user = { ...state.user, ...newUser }?會創建一個新的普通對象并賦值給?state.user,導致其失去?reactive?代理,響應式失效。

方法二:重新賦值時保持?reactive?代理(適用于全新對象)

適用場景:
  • 需要完全替換?reactive?對象中的某個嵌套對象(例如?state.user?原本不存在,或需要用全新的對象結構覆蓋)。
  • 確保新對象被重新包裹為?reactive?代理,或通過?ref?間接處理(更推薦)。
方式 1:對新對象重新應用?reactive(不推薦,可能導致性能問題)

javascript

// 錯誤:直接賦值新普通對象(失去響應式)
state.user = newUser; // 正確:對新對象創建 reactive 代理后再賦值(保持響應式)
state.user = reactive(newUser); 
// 注意:此時 state.user 是新的代理對象,原有代理會被丟棄,可能導致不必要的依賴追蹤
方式 2:用?ref?包裹對象(更優雅的解決方案)

如果嵌套對象需要頻繁整體更新,建議將其定義為?ref,而非直接作為?reactive?的屬性:

javascript

import { reactive, ref } from 'vue';// 初始化:用 ref 包裹 user 對象(可整體更新)
const state = reactive({user: ref({ name: 'Alice', age: 30 }) // user 是 ref 對象
});// 從接口獲取新用戶數據
const newUser = { name: 'Bob', age: 35, city: 'Beijing' };// 正確寫法:直接賦值給 ref 的 value(內部自動處理響應式)
state.user.value = newUser; 
// 或先解包再賦值(模板中無需 .value,自動解包)
原理:
  • ref?允許重新賦值?value(包括對象),且會自動對新對象創建?reactive?代理(即 “自動解包”),因此響應式得以保留。
  • 相比直接使用?reactive?嵌套對象,ref?更靈活,適合需要整體替換的場景。

方法三:解構新對象并逐個更新屬性(適用于少量屬性)

適用場景:
  • 新對象與原對象結構差異較小,只需更新部分屬性。
  • 希望保持代碼可讀性,避免使用?Object.assign
示例:

javascript

// 假設 newUser 包含需要更新的屬性
state.user.name = newUser.name;
state.user.age = newUser.age;
state.user.city = newUser.city; // 直接更新每個屬性,保持代理引用

核心原則:避免丟失代理引用

無論采用哪種方法,核心是確保?reactive?對象的引用不被替換

  1. 對?reactive?嵌套對象:通過修改其屬性或合并新屬性(Object.assign),而非重新賦值整個對象。
  2. 對頻繁整體更新的對象:改用?ref?包裹(const obj = ref(initialValue)),允許直接賦值新對象到?obj.value,利用?ref?的自動響應式處理。

完整示例對比(錯誤 vs 正確)

錯誤寫法(失去響應式):

javascript

const state = reactive({ user: { name: 'Alice' } });
// 錯誤:直接賦值新對象,user 失去 reactive 代理
state.user = { name: 'Bob' }; 
// 視圖不會更新,因為 state.user 現在是普通對象
正確寫法 1(合并屬性):

javascript

const state = reactive({ user: { name: 'Alice' } });
const newUser = { name: 'Bob', age: 30 };
// 正確:合并新屬性到現有代理對象
Object.assign(state.user, newUser); 
// 或逐個賦值:state.user.name = 'Bob'; state.user.age = 30;
// 視圖會正確更新
正確寫法 2(用 ref 包裹對象):

javascript

const state = reactive({ user: ref({ name: 'Alice' }) });
const newUser = { name: 'Bob', age: 30 };
// 正確:賦值給 ref 的 value,內部自動處理響應式
state.user.value = newUser; 
// 視圖會正確更新

總結:推薦方案

  1. 優先使用?ref?包裹需要整體更新的對象
    定義時用?ref,更新時直接賦值?obj.value = newObj,簡潔且避免響應式丟失問題。

    javascript

    const user = ref({}); // 初始化為 ref 對象
    user.value = await fetchUser(); // 直接賦值新對象,保持響應式
    

  2. 若必須使用?reactive?嵌套對象
    使用?Object.assign?合并新屬性,或逐個更新屬性,確保不替換代理對象的引用。

    javascript

    Object.assign(state.user, newUser); // 合并屬性,保持代理
    

通過以上方法,既能實現整體對象的更新,又能保持 Vue 3 的響應式特性,避免因引用丟失導致的視圖不同步問題。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/901828.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/901828.shtml
英文地址,請注明出處:http://en.pswp.cn/news/901828.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

第一節:Vben Admin 最新 v5.0初體驗

系列文章目錄 基礎篇 第一節:Vben Admin介紹和初次運行 第二節:Vben Admin 登錄邏輯梳理和對接后端準備 第三節:Vben Admin登錄對接后端login接口 第四節:Vben Admin登錄對接后端getUserInfo接口 第五節:Vben Admin權…

Nginx部署spa單頁面的小bug

沒部署過,都是給后端干的,自己嘗試部署了一個下午終于成功了 我遇到的最大的bug是進入后只有首頁正常顯示 其他頁面全是404,于是問問問才知道,需要這個 location / { try_files $uri $uri/ /index.html; } 讓…

面試算法高頻08-動態規劃-01

動態規劃 遞歸知識要點 遞歸代碼模板:提供遞歸代碼的標準形式public void recur(int level, int param) ,包含終止條件(if (level> MAX_LEVEL))、當前層邏輯處理(process(level, param))、向下一層遞歸…

若依框架前后端分離版部署全流程詳解(本地+服務器+高級配置)

若依框架前后端分離版部署全流程詳解(本地服務器高級配置) 若依(RuoYi)作為一款基于SpringBoot和Vue的權限管理系統,憑借其模塊化設計和開箱即用的特性廣受開發者歡迎。本文將從本地部署、服務器部署、高級配置三個維…

醫療設備預測性維護合規架構:從法規遵循到技術實現的深度解析

在醫療行業數字化轉型加速推進的當下,醫療設備預測性維護已成為提升設備可用性、保障醫療安全的核心技術。然而,該技術的有效落地必須建立在嚴格的合規框架之上。醫療設備直接關乎患者生命健康,其維護過程涉及醫療法規、數據安全、質量管控等…

LLMs基礎學習(七)DeepSeek專題(4)

LLMs基礎學習(七)DeepSeek專題(4) 文章目錄 LLMs基礎學習(七)DeepSeek專題(4)DeepSeek-R1 訓練過程的四個階段具體流程小結 “規則化獎勵”具體原因小結 “自我認知”(se…

SQL 速查手冊

前言:SQL(Structured Query Language)是用于管理關系型數據庫的標準語言,廣泛應用于數據查詢、更新、定義和管理等操作。本文將為你提供一份詳細的 SQL 速查手冊,涵蓋從基礎到高級的各種 SQL 操作,幫助你快…

IDEA 中 Scala 項目遠程連接虛擬機 Spark 環境

IDEA 中 Scala 項目遠程連接虛擬機 Spark 環境 1. 環境準備 確保虛擬機 Spark 環境正常運行 虛擬機中已安裝并啟動 Spark記錄虛擬機的 IP 地址和 Spark 端口(默認 7077)確保虛擬機防火墻允許相關端口訪問 本地 IDEA 環境配置 安裝 Scala 插件安裝 Spar…

.net core 項目快速接入Coze智能體-開箱即用-全局說明

目錄 一、Coze智能體的核心價值 二、開箱即用-效果如下 三 流程與交互設計 為什么要分析意圖,而不是全部交由AI處理。 四 接入前的準備工作 五:代碼實現----字節Coze 簽署 JWT和獲取Token .net core 項目快速接入Coze智能體-開箱即用 .net core快…

網店運營精細化突破新路徑

內容概要 電商戰場越來越卷,單純靠低價和流量轟炸已經玩不轉了。今天想要站穩腳跟,精細化運營才是破局密碼——從商品怎么選、用戶怎么留,到供應鏈怎么跑得更快,每個環節都得摳細節。比如用數據給選品“開天眼”,把用…

數據結構學習筆記 :線性表的鏈式存儲詳解

目錄 單鏈表 1.1 無頭單鏈表 1.2 有頭單鏈表單向循環鏈表雙鏈表 3.1 雙鏈表 3.2 雙向循環鏈表總結與對比 一、單鏈表 1. 無頭單鏈表(Headless Singly Linked List) 定義:鏈表無頭結點,直接由頭指針指向第一個數據節點。 特點&…

數據庫10(代碼相關語句)

while循環 declare avgprice numeric(10,2) set avgprice(select avg(price)from titles) //自定義參數 while avgprice<10 //循環條件 begin update titles set priceprice*1.1 end //循環語句操作&#xff0c;當avgprice<10,所有price都加0.1 case語句 查詢authors表…

Redis 下載與安裝(Windows版)

一、下載 1、redis官網&#xff1a; https://redis.io/downloads/ 2、Github下載地址&#xff1a; https://github.com/MicrosoftArchive/redis/releases 二、安裝 1、打開一個命令窗口&#xff0c;通過 cd 命令進入到你解壓的目錄 2、輸入命令 &#xff0c;啟動 Redis&…

在高數據速度下確保信號完整性的 10 個關鍵策略

隨著越來越多的傳感器連接到系統&#xff0c;需要快速、可靠和安全地傳輸更多數據&#xff0c;對帶寬和設計復雜性的需求也在增加。優先考慮的是確保從 A 發送到 B 的信號不會失真。 確保信號完整性 對于設計依賴于持續準確數據流的數據密集型應用程序的工程師來說&#xff0c…

NAT、代理服務、內網穿透

NAT、代理服務、內網穿透 1、NAT1.1、NAT過程1.2、NAPT2、內網穿透3、內網打洞3、代理服務器3.1、正向代理3.2、反向代理1、NAT 1.1、NAT過程 之前我們討論了IPv4協議中IP地址數量不充足的問題。NAT技術是當前解決IP地址不夠用的主要手段,是路由器的一個重要功能。 NAT能夠將…

利用互斥鎖或者利用邏輯過期解決緩存擊穿問題

緩存擊穿問題概述 緩存擊穿是指某個 熱點數據緩存過期 時&#xff0c;大量并發請求直接穿透緩存&#xff0c;同時訪問數據庫&#xff0c;導致數據庫壓力驟增甚至崩潰。以下是基于 互斥鎖 和 邏輯過期 的解決方案&#xff1a; 一、緩存擊穿的核心原因 熱點數據失效&#xff1a…

Vue3組合式API內核解析:從原子狀態到企業級架構

一、組合邏輯原子化設計 1.1 狀態管理層級拓撲 1.2 組合單元類型對照表 類型典型實現適用場景復用維度UI邏輯單元useForm/useTable表單/列表交互100%跨項目復用業務邏輯單元useOrderFlow訂單流程控制同項目跨模塊設備能力單元useGeolocation地理位置獲取跨技術棧復用狀態管理…

新生宿舍管理系統

收藏關注不迷路&#xff01;&#xff01; &#x1f31f;文末獲取源碼數據庫&#x1f31f; 感興趣的可以先收藏起來&#xff0c;還有大家在畢設選題&#xff08;免費咨詢指導選題&#xff09;&#xff0c;項目以及論文編寫等相關問題都可以給我留言咨詢&#xff0c;希望幫助更多…

從零上手GUI Guider學習LVGL——Button

視頻教程請關注我b站&#xff1a;同學_好好學習&#xff0c;這里只是做相應的筆記文稿 從零上手GUI Guider學習LVGL——Buttton 前言&#xff1a; 首先我們為什么要學習LVGL設計工具呢&#xff1f; 1 降低開發難度 2 提高開發效率 所以我們需要學習一款合適的設計工具 在b站很少…

【AAOS】【源碼分析】Car UX Restrictions

AAOS UX的核心理念:安全駕駛是駕駛員的首要責任。汽車制造商和應用程序開發人員的所有設計都必須反映這一優先事項。 AAOS平臺允許設備制造商(OEM)對不同駕駛狀態下的限制進行定制。 駕駛員分心指南 只有符合Driver Distraction Guidelines的應用才可以在駕駛過程中運行。…