Vue中對(duì)象賦值問(wèn)題:對(duì)象引用被保留僅部分屬性被覆蓋的解決方案
問(wèn)題重現(xiàn)
import { reactive } from 'vue'; const state = reactive({ obj1: { name: "Alice", age: 25 }, }); // 嘗試用新對(duì)象覆蓋 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,可能會(huì)出問(wèn)題: const anotherObj = { name: "Charlie" }; Object.assign(state.obj1, anotherObj); // ? 只修改了 name,age 仍然保留 console.log(state.obj1); // { name: "Charlie", age: 30 }(age 沒(méi)變!)
原因
直接賦值 =
- 完全替換對(duì)象,Vue 能檢測(cè)到變化并觸發(fā)更新。
- 適用于
reactive
或ref
包裝的對(duì)象。
Object.assign
或解構(gòu)賦值 { ... }
- 僅修改現(xiàn)有對(duì)象的屬性,不會(huì)觸發(fā) Vue 的響應(yīng)式更新(如果直接操作深層對(duì)象)。
- 如果目標(biāo)對(duì)象是響應(yīng)式的,修改其屬性仍然會(huì)觸發(fā)更新,但 不會(huì)替換整個(gè)對(duì)象。
解決方案
1. 完全替換對(duì)象(推薦)
直接賦值新對(duì)象,確保 Vue 檢測(cè)到變化:
state.obj1 = { ...newObj }; // 使用新對(duì)象替換 // 或 state.obj1 = Object.assign({}, newObj); // 創(chuàng)建新對(duì)象
2. 使用 ref 代替 reactive
ref
更適合管理對(duì)象替換:
import { ref } from 'vue'; const objRef = ref({ name: "Alice", age: 25 }); // 直接替換整個(gè)對(duì)象 objRef.value = { name: "Bob", age: 30 }; // ? 觸發(fā)響應(yīng)式更新
3. 使用 reactive + 手動(dòng)觸發(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ā)依賴(lài)更新) state.obj1 = null; // 強(qiáng)制清除舊引用 state.obj1 = { name: "Bob", age: 30 }; // ?
4. 使用 Vue.set(Vue 2 兼容方案)
在 Vue 2 中,直接修改對(duì)象可能不會(huì)觸發(fā)更新,需要用 Vue.set
:
// Vue 2 專(zhuān)用 Vue.set(state, 'obj1', { name: "Bob", age: 30 });
但在 Vue 3 中,reactive
或 ref
已經(jīng)解決了這個(gè)問(wèn)題。
總結(jié)
方法 | 適用場(chǎng)景 | 示例 |
---|---|---|
直接賦值 = | Vue 3 reactive/ref | state.obj = newObj ? |
ref + .value | 需要明確替換對(duì)象 | objRef.value = newObj ? |
Object.assign | 僅修改屬性(不替換對(duì)象) | Object.assign(state.obj, { name: "Bob" }) ?(慎用) |
解構(gòu)賦值 {...} | 創(chuàng)建新對(duì)象替換 | state.obj = { ...newObj } ? |
推薦做法:
- 如果希望 完全替換對(duì)象,直接用
=
賦值。 - 如果希望 修改部分屬性,確保目標(biāo)對(duì)象是響應(yīng)式的(如
reactive
或ref
包裝的)。
這樣就能避免“對(duì)象未完全替換,僅部分屬性更新”的問(wèn)題。
結(jié)論:第一種方法最好用,簡(jiǎn)單易懂好操作。
Vue 3 中 reactive 和 ref 的全面解析
在 Vue 3 的 Composition API 中,reactive
和 ref
都是用來(lái)創(chuàng)建 響應(yīng)式數(shù)據(jù) 的核心 API,但它們的使用場(chǎng)景和底層機(jī)制有所不同。下面從 定義、訪問(wèn)方式、適用場(chǎng)景、底層實(shí)現(xiàn)、TS 類(lèi)型支持 等方面進(jìn)行詳細(xì)對(duì)比。
1. 基本定義
reactive | ref | |
---|---|---|
作用 | 使 對(duì)象/數(shù)組 變成響應(yīng)式 | 使 任意值(基本類(lèi)型、對(duì)象、數(shù)組等)變成響應(yīng)式 |
返回值 | 返回一個(gè) Proxy 代理對(duì)象 | 返回一個(gè) RefImpl 對(duì)象(通過(guò) .value 訪問(wèn)) |
適用數(shù)據(jù)類(lèi)型 | 僅適用于 對(duì)象/數(shù)組 | 適用于 所有類(lèi)型(number, string, object, array 等) |
訪問(wèn)方式 | 直接訪問(wèn)屬性(obj.key) | 必須通過(guò) .value 訪問(wèn)(refObj.value) |
模板自動(dòng)解包 | 直接使用,無(wú)需 .value | 在模板中自動(dòng)解包(無(wú)需 .value) |
2. 基本用法對(duì)比
(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):
- 適用于 嵌套對(duì)象,自動(dòng)深度響應(yīng)式。
- 不能直接替換整個(gè)對(duì)象(會(huì)失去響應(yīng)性),必須修改其屬性。
(2)ref 示例
import { ref } from 'vue'; const count = ref(0); // 基本類(lèi)型 const user = ref({ name: "Alice" }); // 對(duì)象 // 修改數(shù)據(jù) count.value++; // 必須用 .value user.value.name = "Bob"; // 深層屬性也是響應(yīng)式的 // 完全替換對(duì)象 user.value = { name: "Charlie" }; // ? 仍然保持響應(yīng)式
特點(diǎn):
- 可以存儲(chǔ) 任意類(lèi)型(基本類(lèi)型、對(duì)象、數(shù)組等)。
- 在 JS 中 必須用
.value
訪問(wèn),但在 模板中 自動(dòng)解包(無(wú)需.value
)。
3. 核心區(qū)別
(1)底層實(shí)現(xiàn)
reactive | ref | |
---|---|---|
實(shí)現(xiàn)方式 | 基于 Proxy 代理整個(gè)對(duì)象 | 基于 RefImpl 類(lèi),用 .value 存儲(chǔ)值 |
響應(yīng)式原理 | 直接監(jiān)聽(tīng)對(duì)象的所有屬性 | 通過(guò) .value 觸發(fā) getter/setter |
適用場(chǎng)景 | 適合 復(fù)雜對(duì)象/數(shù)組 | 適合 基本類(lèi)型 或 需要替換整個(gè)對(duì)象 的情況 |
(2)數(shù)據(jù)替換
reactive
:
const state = reactive({ count: 0 }); state = { count: 1 }; // ? 錯(cuò)誤!不能直接替換整個(gè) reactive 對(duì)象
必須修改屬性:
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> <!-- 自動(dòng)解包,無(wú)需 .value --> </template>
但在 JS 中必須用 .value
:
console.log(countRef.value); // 必須用 .value
4. 如何選擇?
使用場(chǎng)景 | 推薦 API |
---|---|
管理復(fù)雜對(duì)象/表單數(shù)據(jù) | reactive |
基本類(lèi)型(string/number/boolean) | ref |
需要靈活替換整個(gè)對(duì)象 | ref |
組合式函數(shù)(Composable)返回值 | ref(更靈活) |
需要解構(gòu)響應(yīng)式對(duì)象 | 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é)
對(duì)比項(xiàng) | reactive | ref |
---|---|---|
適用數(shù)據(jù)類(lèi)型 | 對(duì)象/數(shù)組 | 任意類(lèi)型 |
訪問(wèn)方式 | 直接 obj.key | .value |
模板自動(dòng)解包 | 直接使用 | 自動(dòng)解包 |
是否支持替換整個(gè)對(duì)象 | 不能直接替換 | 可以替換 |
底層實(shí)現(xiàn) | Proxy | RefImpl + getter/setter |
推薦使用場(chǎng)景 | 復(fù)雜對(duì)象 | 基本類(lèi)型或需要替換的對(duì)象 |
最終建議:
- 如果管理 復(fù)雜對(duì)象/表單數(shù)據(jù),用
reactive
。 - 如果是 基本類(lèi)型 或 需要靈活替換對(duì)象,用
ref
。 - 在組合式函數(shù)(Composable)中返回?cái)?shù)據(jù)時(shí),優(yōu)先用
ref
(更靈活)
以上就是Vue中對(duì)象賦值問(wèn)題:對(duì)象引用被保留僅部分屬性被覆蓋的解決方案的詳細(xì)內(nèi)容,更多關(guān)于Vue中對(duì)象賦值問(wèn)題的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
淺談java中unmodifiableList方法的應(yīng)用場(chǎng)景
下面小編就為大家?guī)?lái)一篇淺談java中unmodifiableList方法的應(yīng)用場(chǎng)景。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-06-06Java中import java.util.Scanner的用處詳解
文章主要介紹Java中的Scanner類(lèi)及其常用方法next()和nextLine()的區(qū)別,next()方法在遇到空格、Tab鍵、回車(chē)鍵等分隔符時(shí)結(jié)束輸入,而nextLine()方法則接收所有輸入,直到遇到回車(chē)鍵2024-11-11SpringBoot?RESTful?應(yīng)用中的異常處理梳理小結(jié)
這篇文章主要介紹了SpringBoot?RESTful?應(yīng)用中的異常處理梳理小結(jié),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-05-05SpringBoot操作Mongodb的實(shí)現(xiàn)示例
本文主要介紹了SpringBoot操作Mongodb的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06詳解Java的MyBatis框架中的緩存與緩存的使用改進(jìn)
很多人在使用MyBatis的緩存后經(jīng)常會(huì)遇到MySQL分頁(yè)查詢(xún)的顯示問(wèn)題,針對(duì)于此,這里我們就來(lái)詳解Java的MyBatis框架中的緩存與緩存的使用改進(jìn),首先來(lái)回顧一下MyBatis的緩存機(jī)制與執(zhí)行:2016-06-06關(guān)于java.math.BigDecimal比較大小問(wèn)題
這篇文章主要介紹了關(guān)于java.math.BigDecimal比較大小問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07Java中的collection集合類(lèi)型總結(jié)
Java的集合類(lèi)型都是對(duì)java.util包中Collection接口的繼承,這里我們主要介紹依賴(lài)于collection的一些主分支,一起來(lái)看一下Java中的collection集合類(lèi)型總結(jié)2016-05-05