一文帶你搞懂Vue3?defineModel中的雙向綁定
前言
隨著vue3.4
版本的發(fā)布,defineModel
也正式轉(zhuǎn)正了。它可以簡化父子組件之間的雙向綁定,是目前官方推薦的雙向綁定實現(xiàn)方式。
vue3.4以前如何實現(xiàn)雙向綁定
大家應(yīng)該都知道v-model
只是一個語法糖,實際就是給組件定義了modelValue
屬性和監(jiān)聽update:modelValue
事件,所以我們以前要實現(xiàn)數(shù)據(jù)雙向綁定需要給子組件定義一個modelValue
屬性,并且在子組件內(nèi)要更新modelValue
值時需要emit
出去一個update:modelValue
事件,將新的值作為第二個字段傳出去。
我們來看一個簡單的例子,父組件的代碼如下:
<template> <CommonInput v-model="inputValue" /> </template> <script setup lang="ts"> import { ref } from "vue"; const inputValue = ref(); </script>
子組件的代碼如下:
<template> <input :value="props.modelValue" @input="emit('update:modelValue', $event.target.value)" /> </template> <script setup lang="ts"> const props = defineProps(["modelValue"]); const emit = defineEmits(["update:modelValue"]); </script>
上面的例子大家應(yīng)該很熟悉,以前都是這樣去實現(xiàn)v-model
雙向綁定的。但是存在一個問題就是input
輸入框其實支持直接使用v-model
的,我們這里卻沒有使用v-model
而是在input
輸入框上面添加value
屬性和input
事件。
原因是因為從vue2
開始就已經(jīng)是單向數(shù)據(jù)流,在子組件中是不能直接修改props
中的值。而是應(yīng)該由子組件中拋出一個事件,由父組件去監(jiān)聽這個事件,然后去修改父組件中傳遞給props
的變量。如果這里我們給input
輸入框直接加一個v-model="props.modelValue"
,那么其實是在子組件內(nèi)直接修改props
中的modelValue
。由于單向數(shù)據(jù)流的原因,vue
是不支持直接修改props
的,所以我們才需要將代碼寫成上面的樣子。
使用defineModel實現(xiàn)數(shù)據(jù)雙向綁定
defineModel
是一個宏,所以不需要從vue中import
導入,直接使用就可以了。這個宏可以用來聲明一個雙向綁定 prop,通過父組件的 v-model
來使用。
基礎(chǔ)demo
父組件的代碼和前面是一樣的,如下:
<template> <CommonInput v-model="inputValue" /> </template> <script setup lang="ts"> import { ref } from "vue"; const inputValue = ref(); </script>
子組件的代碼如下:
<template> <input v-model="model" /> </template> <script setup lang="ts"> const model = defineModel(); model.value = "xxx"; </script>
在上面的例子中我們直接將defineModel
的返回值使用v-model
綁定到input輸入框上面,無需定義 modelValue
屬性和監(jiān)聽 update:modelValue
事件,代碼更加簡潔。defineModel
的返回值是一個ref
,我們可以在子組件中修改model
變量的值,并且父組件中的inputValue
變量的值也會同步更新,這樣就可以實現(xiàn)雙向綁定。
那么問題來了,從vue2
開始就變成了單向數(shù)據(jù)流。這里修改子組件的值后,父組件的變量值也被修改了,那這不就變回了vue1
的雙向數(shù)據(jù)流了嗎?其實并不是這樣的,這里還是單向數(shù)據(jù)流,我們接下來會簡單講一下defineModel
的實現(xiàn)原理。
實現(xiàn)原理
defineModel
其實就是在子組件內(nèi)定義了一個叫model
的ref變量和modelValue
的props,并且watch
了props中的modelValue
。當props
中的modelValue
的值改變后會同步更新model
變量的值。并且當在子組件內(nèi)改變model
變量的值后會拋出update:modelValue
事件,父組件收到這個事件后就會更新父組件中對應(yīng)的變量值。
實現(xiàn)原理代碼如下:
<template> <input v-model="model" /> </template> <script setup lang="ts"> import { ref, watch } from "vue"; const props = defineProps(["modelValue"]); const emit = defineEmits(["update:modelValue"]); const model = ref(); watch( () => props.modelValue, () => { model.value = props.modelValue; } ); watch(model, () => { emit("update:modelValue", model.value); }); </script>
看了上面的代碼后你應(yīng)該了解到了為什么可以在子組件內(nèi)直接修改defineModel
的返回值后父組件對應(yīng)的變量也會同步更新了吧。我們修改的其實是defineModel
返回的ref
變量,而不是直接修改props中的modelValue
。實現(xiàn)方式還是和vue3.4
以前實現(xiàn)雙向綁定一樣的,只是defineModel
這個宏幫我們將以前的那些繁瑣的代碼給封裝到內(nèi)部實現(xiàn)了。
其實defineModel
的源碼中是使用 customRef 和 watchSyncEffect 去實現(xiàn)的,我這里是為了讓大家能夠更容易的明白defineModel
的實現(xiàn)原理才舉的ref
和watch
的例子。如果大家對defineModel
的源碼感興趣,請在評論區(qū)留言,如果感興趣的小伙伴比較多,我會在下一期出一篇defineModel
源碼的文章。
defineModel如何定義type、default等
既然defineModel
是聲明了一個prop,那同樣也可以定義prop的type
、default
。具體代碼如下:
const model = defineModel({ type: String, default: "20" });
除了支持type
和default
,也支持required
和validator
,用法和定義prop
時一樣。
defineModel如何實現(xiàn)多個v-model綁定
同樣也支持在父組件上面實現(xiàn)多個 v-model
綁定,這時我們給defineModel
傳的第一個參數(shù)就不是對象了,而是一個字符串。
const model1 = defineModel("count1"); const model2 = defineModel("count2");
在父組件中使用v-model
時代碼如下:
<CommonInput v-model:count1="inputValue1" /> <CommonInput v-model:count2="inputValue2" />
我們也可以在多個v-model
中定義type
、default
等
const model1 = defineModel("count1", { type: String, default: "aaa", });
defineModel如何使用內(nèi)置修飾符和自定義修飾符
如果要使用系統(tǒng)內(nèi)置的修飾符比如trim
,父組件的寫法還是和之前是一樣的:
<CommonInput v-model.trim="inputValue" />
子組件也無需做任何修改,和上面其他的defineModel
例子是一樣的:
const model = defineModel();
defineModel
也支持自定義修飾符,比如我們要實現(xiàn)一個將輸入框的字母全部變成大寫的uppercase
自定義修飾符,同時也需要使用內(nèi)置的trim
修飾符。
我們的父組件代碼如下:
<CommonInput v-model.trim.uppercase="inputValue" />
我們的子組件需要寫成下面這樣的:
<template> <input v-model="modelValue" /> </template> <script setup lang="ts"> const [modelValue, modelModifiers] = defineModel({ // get我們這里不需要 set(value) { if (modelModifiers.uppercase) { return value?.toUpperCase(); } }, }); </script>
這時我們給defineModel
傳進去的第一個參數(shù)就是包含get
和 set
方法的對象,當對modelValue
變量進行讀操作時會走到get
方法里面去,當對modelValue
變量進行寫操作時會走到set
方法里面去。如果只需要對寫操作進行攔截,那么可以不用寫get
。
defineModel
的返回值也可以解構(gòu)成兩個變量,第一個變量就是我們前面幾個例子的ref
對象,用于給v-model
綁定。第二個變量是一個對象,里面包含了有哪些修飾符,在這里我們有trim
和uppercase
兩個修飾符,所以modelModifiers
的值為:
{ trim: true, uppercase: true }
在輸入框進行輸入時,就會走到set
方法里面,然后調(diào)用value?.toUpperCase()
就可以實現(xiàn)將輸入的字母變成大寫字母。
總結(jié)
這篇文章介紹了如何使用defineModel
宏實現(xiàn)雙向綁定以及defineModel
的實現(xiàn)原理。
- 在子組件內(nèi)調(diào)用
defineModel
宏會返回一個ref
對象,在子組件內(nèi)可以直接對這個ref
對象進行賦值,父組件內(nèi)的相應(yīng)變量也會同步修改。 defineModel
其實就是在子組件內(nèi)定義了一個ref變量和對應(yīng)的prop,然后監(jiān)聽了對應(yīng)的prop保持ref變量的值始終和對應(yīng)的prop是一樣的。在子組件內(nèi)當修改ref變量值時會拋出一個事件給父組件,讓父組件更新對應(yīng)的變量值,從而實現(xiàn)雙向綁定。- 使用
defineModel({ type: String, default: "20" })
就可以定義prop的type
和default
等選項。 - 使用
defineModel("count")
就可以實現(xiàn)多個v-model
綁定。 - 通過解構(gòu)
defineModel()
的返回值拿到modelModifiers
修飾符對象,配合get
和set
轉(zhuǎn)換器選項實現(xiàn)自定義修飾符。
到此這篇關(guān)于一文帶你搞懂Vue3 defineModel中的雙向綁定的文章就介紹到這了,更多相關(guān)Vue3 defineModel雙向綁定內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解HTTP4種方法(GET、POST、 PUT和DELETE)
本文介紹了HTTP協(xié)議中的四種方法:GET、POST、PUT和DELETE,分別用于不同的操作,GET用于獲取數(shù)據(jù),POST用于提交數(shù)據(jù),PUT用于創(chuàng)建或更新資源,DELETE用于刪除資源,每種方法都有其特點和適用場景,了解這些方法有助于更好地進行數(shù)據(jù)交互和開發(fā),感興趣的朋友一起看看吧2025-02-02vue-create創(chuàng)建VUE3項目詳細圖文教程
create-vue是Vue官方新的腳手架工具,底層切換到了vite(下一代前端工具鏈),為開發(fā)提供極速響應(yīng),下面這篇文章主要給大家介紹了關(guān)于vue-create創(chuàng)建VUE3項目的相關(guān)資料,需要的朋友可以參考下2024-03-03vue對storejs獲取的數(shù)據(jù)進行處理時遇到的幾種問題小結(jié)
這篇文章主要介紹了vue對storejs獲取的數(shù)據(jù)進行處理時遇到的幾種問題小結(jié),需要的朋友可以參考下2018-03-03