如何在 Vue 中使用帶有 v-model 的對象
Vue.js中的v-model
指令大家都非常熟悉,它實作了元件間的雙向資料綁定。但當為自訂元件手動實作v-model
時,通常會遇到一些問題。
通常的做法如下:
const props = defineProps(['modelValue']); const emit = defineEmits(['update:modelValue']); <template></template>
請注意,我們不會在元件內部修改modelValue
prop的值。相反,我們將更新後的值透過emit
方法傳遞回父元件,由父元件進行實際的修改。這是因為:子元件不應該影響父元件的狀態,這會使資料流複雜化,並增加除錯難度。
如Vue文件所述,不應在子元件內部修改prop。如果這樣做,Vue會在控制台中發出警告。
物件的情況如何?
JavaScript中的物件和陣列是一種特殊情況,因為它們是按引用傳遞的。這意味著元件可以直接修改物件prop的嵌套屬性。但是,Vue不會對嵌套物件屬性中的修改發出警告(追蹤這些修改會帶來效能損失)。因此,這種意外的更改可能會導致應用程式中出現難以察覺和難以調試的問題。
大多數情況下,我們使用基本值作為v-model
。但是,在某些情況下,例如在建立表單元件時,我們可能需要一個可以處理物件的自訂v-model
。這就引出一個重要的問題:
如何實作一個自訂的
v-model
來處理對象,同時避免上述陷阱?
探討問題
一種方法是使用可寫入的計算屬性或defineModel
輔助函數。但是,這兩種解決方案都有一個顯著的缺點:它們直接修改了原始對象,這違背了保持清晰資料流的目的。
為了說明這個問題,讓我們來看一個「表單」元件的範例。這個元件旨在在表單中的值發生變更時,將物件的更新副本發射回父元件。我們將嘗試使用可寫的計算屬性來實現這一點。
在這個例子中,可寫的計算屬性仍然會修改原始物件。
import { computed } from 'vue'; import { cloneDeep } from 'lodash-es'; type Props = { modelValue: { name: string; email: string; }; }; const props = withDefaults(defineProps<Props>(), { modelValue: () => ({ name: '', email: '' }), }); const emit = defineEmits<{ 'update:modelValue': [value: Props['modelValue']]; }>(); const formData = computed({ // 返回的getter对象仍然是可变的 get() { return props.modelValue; }, // 注释掉setter仍然会修改prop set(newValue) { emit('update:modelValue', cloneDeep(newValue)); }, });
這不起作用,因為從getter回傳的物件仍然是可變的,導致原始物件被意外修改。
defineModel
也是一樣。由於update:modelValue
沒有從組件中發出,並且物件屬性在沒有任何警告的情況下被修改。
解決方案
處理這種情況的「Vue方式」是使用內部響應式值來表示對象,並實現兩個觀察者:
- 一個觀察者監控
modelValue
prop的更改,並更新內部值。這確保了內部狀態反映了父元件傳遞的最新prop值。 - 一個觀察者觀察內部值的改變。當內部值更新時,它將向父元件發出物件的全新克隆版本,以避免直接修改原始物件。
為了防止這兩個觀察者之間發生無休止的回饋循環,我們需要確保對modelValue
prop的更新不會意外地重新觸發內部值的觀察者。
const props = defineProps(['modelValue']); const emit = defineEmits(['update:modelValue']); <template></template>
我知道你在想什麼:「這太多了!」讓我們看看如何進一步簡化它。
使用VueUse簡化解
將此邏輯提取到可重複使用的組合式函數中是簡化流程的好方法。但好消息是:我們甚至不需要這樣做! VueUse中的useVModel
組合式函數可以幫我們處理這個問題!
VueUse是一個功能強大的Vue實用程式庫,通常被稱為組合式實用程式的「瑞士軍刀」。它是完全可樹狀搖動的,因此我們可以只使用所需的部分,而無需擔心會增加套件的大小。
以下是使用useVModel
重構之前的範例:
import { computed } from 'vue'; import { cloneDeep } from 'lodash-es'; type Props = { modelValue: { name: string; email: string; }; }; const props = withDefaults(defineProps<Props>(), { modelValue: () => ({ name: '', email: '' }), }); const emit = defineEmits<{ 'update:modelValue': [value: Props['modelValue']]; }>(); const formData = computed({ // 返回的getter对象仍然是可变的 get() { return props.modelValue; }, // 注释掉setter仍然会修改prop set(newValue) { emit('update:modelValue', cloneDeep(newValue)); }, });
簡潔許多!
就是這樣!我們已經探討瞭如何在Vue中正確使用帶有v-model
的對象,而不會直接從子組件中修改它。透過使用觀察者或利用VueUse的useVModel
等組合式函數,我們可以在應用程式中保持清晰且可預測的狀態管理。
這裡有一個包含本文所有範例的Stackblitz連結。隨意探索和實驗。
感謝您的閱讀,祝您編碼愉快!
以上是如何在 Vue 中使用帶有 v-model 的對象的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

JavaScript是現代Web開發的基石,它的主要功能包括事件驅動編程、動態內容生成和異步編程。 1)事件驅動編程允許網頁根據用戶操作動態變化。 2)動態內容生成使得頁面內容可以根據條件調整。 3)異步編程確保用戶界面不被阻塞。 JavaScript廣泛應用於網頁交互、單頁面應用和服務器端開發,極大地提升了用戶體驗和跨平台開發的靈活性。

Python和JavaScript開發者的薪資沒有絕對的高低,具體取決於技能和行業需求。 1.Python在數據科學和機器學習領域可能薪資更高。 2.JavaScript在前端和全棧開發中需求大,薪資也可觀。 3.影響因素包括經驗、地理位置、公司規模和特定技能。

實現視差滾動和元素動畫效果的探討本文將探討如何實現類似資生堂官網(https://www.shiseido.co.jp/sb/wonderland/)中�...

學習JavaScript不難,但有挑戰。 1)理解基礎概念如變量、數據類型、函數等。 2)掌握異步編程,通過事件循環實現。 3)使用DOM操作和Promise處理異步請求。 4)避免常見錯誤,使用調試技巧。 5)優化性能,遵循最佳實踐。

JavaScript的最新趨勢包括TypeScript的崛起、現代框架和庫的流行以及WebAssembly的應用。未來前景涵蓋更強大的類型系統、服務器端JavaScript的發展、人工智能和機器學習的擴展以及物聯網和邊緣計算的潛力。

如何在JavaScript中將具有相同ID的數組元素合併到一個對像中?在處理數據時,我們常常會遇到需要將具有相同ID�...

zustand異步操作中的數據更新問題在使用zustand狀態管理庫時,經常會遇到異步操作導致數據更新不及時的問題。 �...
