vue3中當用reactive()中定義的物件再次賦值,頁面不會自動更新解決方法
在vue3裡,ref和reacitve都可以定義響應式資料,但是兩者有所不同。在使用reactive定義複雜結構的響應式資料時,如果你要對其賦值,會丟失其響應性。然後賦值是我們經常進行的操作,那麼該怎麼解決呢?
問題:reactive定義的資料不能直接賦值
下述程式碼會報錯: Cannot assign to "form" because it is a constant
//申明表單物件
const form = reactive({
id: undefined,
name: undefined,
...
})
//通過axios讀取資料
getData(id).then((res) => { const { code, data } = res if (code == 200) {form = { ...data, }
}
})
}
解決方法
1. 改為ref定義
const form= ref({})
form.value = {...data}
2. 如果是陣列的話,可以使用push新增資料
const arr = reactive([])
arr.push(...[1, 2, 3])
3.再封裝一層資料(推薦!)即定義屬性名,在後期賦值的時候,對屬性名進行賦值
const state = reactive({ arr: [],
form:{} }); state.arr= [1, 2, 3]
state.form = {...data}
但是這樣的話在html模板裡,使用資料就得state.arr
所以我們可以用解構將它return出來(script setup 裡面 不需要return 結構出來即可)
但是reactive解構出來的資料會丟失響應性
所以再用 toRefs()方法為它們新增響應性
最終為:
const state = reactive({
arr: [],
form:{}
});
state.arr = [1, 2, 3]
state.form ={...data}
return{ ...toRefs(state), }
//如果是在 script setup 裡面 不需要return 結構出來即可
const { form,arr} = toRefs(state)
4.其他,參考下述轉載的方式
優雅的解決reactive()響應式物件初始化重新賦值問題
程式碼裡寫了注意點,此處也先宣告注意事項:
template 裡必須繫結的是 ref() 資料來源 !!
重新初始化整個響應式物件時,用來資料操作的實際變數(例子裡的info)也需要重新賦值!
這是vue3沒正式釋出解決 ref() `.value` 的語法糖的相對方案。正式敲定且釋出後應該就可廢棄我這方案了。
為什麼不用 Object.assign() 處理reactive()? 什麼情況下用 Object.assign() 處理reactive()?
Object.assign() 方法用於將所有可列舉屬性的值從一個或多個源物件分配(賦值)到目標物件。所以無法對例子裡新增加的資料newKey清除掉!(這種問題常見於從後端介面獲得的資料,有時候有{}值,有時候null)。此時不可用 Object.assign()
嚴格定義物件的interface型別,確保資料完全在掌握範圍內,可用 Object.assign()。
此方案解決的痛點:
const info = reactive() ,當info需要重新初始化時,需要用Object.assign(),但是Object.assign() 本質是合併物件並返回結果的新物件。用作初始化資料會導致潛在的問題,尤其是資料非前端可控的情況下(例如介面獲得的資料)
所以用ref宣告,.value 賦值重新覆蓋,才是最穩妥的初始化!(而且這種方法不用擔心各種額外因素,降低心智負擔~~)
如果用ref.value儲存響應式物件,操作起來每次要.value也會很麻煩。 解決這個問題的程式碼就是對應的 let info = xx 和 初始化的 info = xx 這兩行程式碼!
陣列型別的情況下:reactive([1,2]),重新賦值導致丟失響應,ref.value操作也不太方便,因此也可採用這方法。(或者說引用型別目前都可採用這方法)
————————————————
<template lang="">
<div>
{{infoRef.newKey}}
{{infoRef.bar}}
</div>
</template>
<script lang='ts' setup>
import { ref,reactive } from 'vue';
const infoRef = ref<any>(source()) // template 裡必須繫結的是 infoRef 不能是 info !!
let info = infoRef.value // Js裡操作只操作 info 就可以不用 infoRef.value 了
function reset(){
// 這樣需要重置整個響應式物件就不需要 Object.assign和考慮深拷貝問題了
infoRef.value = source()
// 重新初始化整個響應式物件時,用來資料操作的實際變數也需要重新賦值!
info = infoRef.value
}
function source(){
return {
foo:1,
bar:2,
obj:{a:1}
}
}
// 測試
setTimeout(() => {
info.bar *= 110
info.newKey = 666
}, 3000);
setTimeout(() => {
reset()
}, 6000);
setTimeout(() => {
info.bar = 987654321
}, 8000);
</script>
參考連結:https://blog.csdn.net/qq_38974163/article/details/122426426