Vue中對象賦值問題:對象引用被保留僅部分屬性被覆蓋的解決方案
問題重現(xiàn)
import { reactive } from 'vue';
const state = reactive({
obj1: { name: "Alice", age: 25 },
});
// 嘗試用新對象覆蓋 obj1
const newObj = { name: "Bob", age: 30 };
state.obj1 = newObj; // 預(yù)期:obj1 完全變成 newObj
console.log(state.obj1); // { name: "Bob", age: 30 } ?
// 但如果用解構(gòu)賦值或 Object.assign,可能會出問題:
const anotherObj = { name: "Charlie" };
Object.assign(state.obj1, anotherObj); // ? 只修改了 name,age 仍然保留
console.log(state.obj1); // { name: "Charlie", age: 30 }(age 沒變!)
原因
直接賦值 =
- 完全替換對象,Vue 能檢測到變化并觸發(fā)更新。
- 適用于
reactive或ref包裝的對象。
Object.assign 或解構(gòu)賦值 { ... }
- 僅修改現(xiàn)有對象的屬性,不會觸發(fā) Vue 的響應(yīng)式更新(如果直接操作深層對象)。
- 如果目標(biāo)對象是響應(yīng)式的,修改其屬性仍然會觸發(fā)更新,但 不會替換整個對象。
解決方案
1. 完全替換對象(推薦)
直接賦值新對象,確保 Vue 檢測到變化:
state.obj1 = { ...newObj }; // 使用新對象替換
// 或
state.obj1 = Object.assign({}, newObj); // 創(chuàng)建新對象
2. 使用 ref 代替 reactive
ref 更適合管理對象替換:
import { ref } from 'vue';
const objRef = ref({ name: "Alice", age: 25 });
// 直接替換整個對象
objRef.value = { name: "Bob", age: 30 }; // ? 觸發(fā)響應(yīng)式更新
3. 使用 reactive + 手動觸發(fā)更新
如果必須用 reactive,可以強(qiáng)制替換:
import { reactive } from 'vue';
const state = reactive({ obj1: { name: "Alice", age: 25 } });
// 方法1:直接賦值
state.obj1 = { name: "Bob", age: 30 }; // ?
// 方法2:先置空再賦值(確保觸發(fā)依賴更新)
state.obj1 = null; // 強(qiáng)制清除舊引用
state.obj1 = { name: "Bob", age: 30 }; // ?
4. 使用 Vue.set(Vue 2 兼容方案)
在 Vue 2 中,直接修改對象可能不會觸發(fā)更新,需要用 Vue.set:
// Vue 2 專用
Vue.set(state, 'obj1', { name: "Bob", age: 30 });
但在 Vue 3 中,reactive 或 ref 已經(jīng)解決了這個問題。
總結(jié)
| 方法 | 適用場景 | 示例 |
|---|---|---|
| 直接賦值 = | Vue 3 reactive/ref | state.obj = newObj ? |
| ref + .value | 需要明確替換對象 | objRef.value = newObj ? |
| Object.assign | 僅修改屬性(不替換對象) | Object.assign(state.obj, { name: "Bob" }) ?(慎用) |
| 解構(gòu)賦值 {...} | 創(chuàng)建新對象替換 | state.obj = { ...newObj } ? |
推薦做法:
- 如果希望 完全替換對象,直接用
=賦值。 - 如果希望 修改部分屬性,確保目標(biāo)對象是響應(yīng)式的(如
reactive或ref包裝的)。
這樣就能避免“對象未完全替換,僅部分屬性更新”的問題。
結(jié)論:第一種方法最好用,簡單易懂好操作。
Vue 3 中 reactive 和 ref 的全面解析
在 Vue 3 的 Composition API 中,reactive 和 ref 都是用來創(chuàng)建 響應(yīng)式數(shù)據(jù) 的核心 API,但它們的使用場景和底層機(jī)制有所不同。下面從 定義、訪問方式、適用場景、底層實(shí)現(xiàn)、TS 類型支持 等方面進(jìn)行詳細(xì)對比。
1. 基本定義
| reactive | ref | |
|---|---|---|
| 作用 | 使 對象/數(shù)組 變成響應(yīng)式 | 使 任意值(基本類型、對象、數(shù)組等)變成響應(yīng)式 |
| 返回值 | 返回一個 Proxy 代理對象 | 返回一個 RefImpl 對象(通過 .value 訪問) |
| 適用數(shù)據(jù)類型 | 僅適用于 對象/數(shù)組 | 適用于 所有類型(number, string, object, array 等) |
| 訪問方式 | 直接訪問屬性(obj.key) | 必須通過 .value 訪問(refObj.value) |
| 模板自動解包 | 直接使用,無需 .value | 在模板中自動解包(無需 .value) |
2. 基本用法對比
(1)reactive 示例
import { reactive } from 'vue';
const state = reactive({
count: 0,
user: { name: "Alice" }
});
// 修改數(shù)據(jù)
state.count++; // 直接修改
state.user.name = "Bob"; // 深層屬性也是響應(yīng)式的
特點(diǎn):
- 適用于 嵌套對象,自動深度響應(yīng)式。
- 不能直接替換整個對象(會失去響應(yīng)性),必須修改其屬性。
(2)ref 示例
import { ref } from 'vue';
const count = ref(0); // 基本類型
const user = ref({ name: "Alice" }); // 對象
// 修改數(shù)據(jù)
count.value++; // 必須用 .value
user.value.name = "Bob"; // 深層屬性也是響應(yīng)式的
// 完全替換對象
user.value = { name: "Charlie" }; // ? 仍然保持響應(yīng)式
特點(diǎn):
- 可以存儲 任意類型(基本類型、對象、數(shù)組等)。
- 在 JS 中 必須用
.value訪問,但在 模板中 自動解包(無需.value)。
3. 核心區(qū)別
(1)底層實(shí)現(xiàn)
| reactive | ref | |
|---|---|---|
| 實(shí)現(xiàn)方式 | 基于 Proxy 代理整個對象 | 基于 RefImpl 類,用 .value 存儲值 |
| 響應(yīng)式原理 | 直接監(jiān)聽對象的所有屬性 | 通過 .value 觸發(fā) getter/setter |
| 適用場景 | 適合 復(fù)雜對象/數(shù)組 | 適合 基本類型 或 需要替換整個對象 的情況 |
(2)數(shù)據(jù)替換
reactive:
const state = reactive({ count: 0 });
state = { count: 1 }; // ? 錯誤!不能直接替換整個 reactive 對象
必須修改屬性:
Object.assign(state, { count: 1 }); // ? 修改屬性(響應(yīng)式)
ref:
const countRef = ref(0); countRef.value = 1; // ? 可以直接替換
(3)模板中的使用
reactive:
<template>
<div>{{ state.count }}</div> <!-- 直接使用 -->
</template>
ref:
<template>
<div>{{ countRef }}</div> <!-- 自動解包,無需 .value -->
</template>
但在 JS 中必須用 .value:
console.log(countRef.value); // 必須用 .value
4. 如何選擇?
| 使用場景 | 推薦 API |
|---|---|
| 管理復(fù)雜對象/表單數(shù)據(jù) | reactive |
| 基本類型(string/number/boolean) | ref |
| 需要靈活替換整個對象 | ref |
| 組合式函數(shù)(Composable)返回值 | ref(更靈活) |
| 需要解構(gòu)響應(yīng)式對象 | toRefs(reactiveObj) |
5. 進(jìn)階技巧
(1)ref 可以包裹 reactive
const user = ref({
name: "Alice",
age: 25
});
// 修改方式
user.value.name = "Bob"; // ? 響應(yīng)式
user.value = { name: "Charlie" }; // ? 仍然響應(yīng)式
(2)toRefs 解構(gòu) reactive
const state = reactive({ count: 0, name: "Alice" });
const { count, name } = toRefs(state); // 解構(gòu)后仍然是響應(yīng)式
// 使用方式
count.value++; // 必須用 .value
(3)isRef 和 isReactive
import { isRef, isReactive } from 'vue';
console.log(isRef(countRef)); // true
console.log(isReactive(state)); // true
6. 總結(jié)
| 對比項(xiàng) | reactive | ref |
|---|---|---|
| 適用數(shù)據(jù)類型 | 對象/數(shù)組 | 任意類型 |
| 訪問方式 | 直接 obj.key | .value |
| 模板自動解包 | 直接使用 | 自動解包 |
| 是否支持替換整個對象 | 不能直接替換 | 可以替換 |
| 底層實(shí)現(xiàn) | Proxy | RefImpl + getter/setter |
| 推薦使用場景 | 復(fù)雜對象 | 基本類型或需要替換的對象 |
最終建議:
- 如果管理 復(fù)雜對象/表單數(shù)據(jù),用
reactive。 - 如果是 基本類型 或 需要靈活替換對象,用
ref。 - 在組合式函數(shù)(Composable)中返回?cái)?shù)據(jù)時(shí),優(yōu)先用
ref(更靈活)
以上就是Vue中對象賦值問題:對象引用被保留僅部分屬性被覆蓋的解決方案的詳細(xì)內(nèi)容,更多關(guān)于Vue中對象賦值問題的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
springcloud gateway聚合swagger2的方法示例
這篇文章主要介紹了springcloud gateway聚合swagger2的方法示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
eclipse實(shí)現(xiàn)ECDSA數(shù)字簽名
這篇文章主要為大家詳細(xì)介紹了eclipse實(shí)現(xiàn)ECDSA數(shù)字簽名,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-06-06
詳解SpringBoot?Start組件開發(fā)之記錄接口日志信息
這篇文章主要為大家介紹了SpringBoot-Start組件開發(fā)之記錄接口日志信息詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04
圖解Java經(jīng)典算法希爾排序的原理與實(shí)現(xiàn)
希爾排序是希爾(Donald Shell)于1959年提出的一種排序算法。希爾排序也是一種插入排序,它是簡單插入排序經(jīng)過改進(jìn)之后的一個更高效的版本,也稱為縮小增量排序,同時(shí)該算法是沖破O(n2)的第一批算法之一。本文會以圖解的方式詳細(xì)介紹希爾排序的基本思想及其代碼實(shí)現(xiàn)2022-09-09
Java中如何給List進(jìn)行排序(這7種方法輕松實(shí)現(xiàn))
在Java項(xiàng)目中可能會遇到給出一些條件,將List元素按照給定條件進(jìn)行排序的情況,這篇文章主要給大家介紹了關(guān)于Java中如何給List進(jìn)行排序的相關(guān)資料,通過文中介紹的這7種方法可以輕松實(shí)現(xiàn),需要的朋友可以參考下2023-10-10
java通過方法來交換實(shí)參的值實(shí)現(xiàn)方式
文章介紹了Java方法參數(shù)傳遞方式以及如何通過數(shù)組、自定義包裝類或返回結(jié)果來實(shí)現(xiàn)變量交換,同時(shí),討論了內(nèi)部類中static關(guān)鍵字的作用和使用場景2025-12-12
SpringBoot+docker環(huán)境變量配置詳解
這篇文章主要介紹了SpringBoot+docker環(huán)境變量配置詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10

