一文帶你搞懂Vue3?defineModel中的雙向綁定
前言
隨著vue3.4版本的發(fā)布,defineModel也正式轉(zhuǎn)正了。它可以簡(jiǎn)化父子組件之間的雙向綁定,是目前官方推薦的雙向綁定實(shí)現(xiàn)方式。
vue3.4以前如何實(shí)現(xiàn)雙向綁定
大家應(yīng)該都知道v-model只是一個(gè)語(yǔ)法糖,實(shí)際就是給組件定義了modelValue屬性和監(jiān)聽(tīng)update:modelValue事件,所以我們以前要實(shí)現(xiàn)數(shù)據(jù)雙向綁定需要給子組件定義一個(gè)modelValue屬性,并且在子組件內(nèi)要更新modelValue值時(shí)需要emit出去一個(gè)update:modelValue事件,將新的值作為第二個(gè)字段傳出去。
我們來(lái)看一個(gè)簡(jiǎn)單的例子,父組件的代碼如下:
<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)該很熟悉,以前都是這樣去實(shí)現(xiàn)v-model雙向綁定的。但是存在一個(gè)問(wèn)題就是input輸入框其實(shí)支持直接使用v-model的,我們這里卻沒(méi)有使用v-model而是在input輸入框上面添加value屬性和input事件。
原因是因?yàn)閺?code>vue2開(kāi)始就已經(jīng)是單向數(shù)據(jù)流,在子組件中是不能直接修改props中的值。而是應(yīng)該由子組件中拋出一個(gè)事件,由父組件去監(jiān)聽(tīng)這個(gè)事件,然后去修改父組件中傳遞給props的變量。如果這里我們給input輸入框直接加一個(gè)v-model="props.modelValue",那么其實(shí)是在子組件內(nèi)直接修改props中的modelValue。由于單向數(shù)據(jù)流的原因,vue是不支持直接修改props的,所以我們才需要將代碼寫(xiě)成上面的樣子。
使用defineModel實(shí)現(xiàn)數(shù)據(jù)雙向綁定
defineModel是一個(gè)宏,所以不需要從vue中import導(dǎo)入,直接使用就可以了。這個(gè)宏可以用來(lái)聲明一個(gè)雙向綁定 prop,通過(guò)父組件的 v-model 來(lái)使用。
基礎(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輸入框上面,無(wú)需定義 modelValue 屬性和監(jiān)聽(tīng) update:modelValue 事件,代碼更加簡(jiǎn)潔。defineModel的返回值是一個(gè)ref,我們可以在子組件中修改model變量的值,并且父組件中的inputValue變量的值也會(huì)同步更新,這樣就可以實(shí)現(xiàn)雙向綁定。
那么問(wèn)題來(lái)了,從vue2開(kāi)始就變成了單向數(shù)據(jù)流。這里修改子組件的值后,父組件的變量值也被修改了,那這不就變回了vue1的雙向數(shù)據(jù)流了嗎?其實(shí)并不是這樣的,這里還是單向數(shù)據(jù)流,我們接下來(lái)會(huì)簡(jiǎn)單講一下defineModel的實(shí)現(xiàn)原理。
實(shí)現(xiàn)原理
defineModel其實(shí)就是在子組件內(nèi)定義了一個(gè)叫model的ref變量和modelValue的props,并且watch了props中的modelValue。當(dāng)props中的modelValue的值改變后會(huì)同步更新model變量的值。并且當(dāng)在子組件內(nèi)改變model變量的值后會(huì)拋出update:modelValue事件,父組件收到這個(gè)事件后就會(huì)更新父組件中對(duì)應(yīng)的變量值。
實(shí)現(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的返回值后父組件對(duì)應(yīng)的變量也會(huì)同步更新了吧。我們修改的其實(shí)是defineModel返回的ref變量,而不是直接修改props中的modelValue。實(shí)現(xiàn)方式還是和vue3.4以前實(shí)現(xiàn)雙向綁定一樣的,只是defineModel這個(gè)宏幫我們將以前的那些繁瑣的代碼給封裝到內(nèi)部實(shí)現(xiàn)了。
其實(shí)defineModel的源碼中是使用 customRef 和 watchSyncEffect 去實(shí)現(xiàn)的,我這里是為了讓大家能夠更容易的明白defineModel的實(shí)現(xiàn)原理才舉的ref和watch的例子。如果大家對(duì)defineModel的源碼感興趣,請(qǐng)?jiān)谠u(píng)論區(qū)留言,如果感興趣的小伙伴比較多,我會(huì)在下一期出一篇defineModel源碼的文章。
defineModel如何定義type、default等
既然defineModel是聲明了一個(gè)prop,那同樣也可以定義prop的type、default。具體代碼如下:
const model = defineModel({ type: String, default: "20" });
除了支持type和default,也支持required和validator,用法和定義prop時(shí)一樣。
defineModel如何實(shí)現(xiàn)多個(gè)v-model綁定
同樣也支持在父組件上面實(shí)現(xiàn)多個(gè) v-model 綁定,這時(shí)我們給defineModel傳的第一個(gè)參數(shù)就不是對(duì)象了,而是一個(gè)字符串。
const model1 = defineModel("count1");
const model2 = defineModel("count2");
在父組件中使用v-model時(shí)代碼如下:
<CommonInput v-model:count1="inputValue1" /> <CommonInput v-model:count2="inputValue2" />
我們也可以在多個(gè)v-model中定義type、default等
const model1 = defineModel("count1", {
type: String,
default: "aaa",
});
defineModel如何使用內(nèi)置修飾符和自定義修飾符
如果要使用系統(tǒng)內(nèi)置的修飾符比如trim,父組件的寫(xiě)法還是和之前是一樣的:
<CommonInput v-model.trim="inputValue" />
子組件也無(wú)需做任何修改,和上面其他的defineModel例子是一樣的:
const model = defineModel();
defineModel也支持自定義修飾符,比如我們要實(shí)現(xiàn)一個(gè)將輸入框的字母全部變成大寫(xiě)的uppercase自定義修飾符,同時(shí)也需要使用內(nèi)置的trim修飾符。
我們的父組件代碼如下:
<CommonInput v-model.trim.uppercase="inputValue" />
我們的子組件需要寫(xiě)成下面這樣的:
<template>
<input v-model="modelValue" />
</template>
<script setup lang="ts">
const [modelValue, modelModifiers] = defineModel({
// get我們這里不需要
set(value) {
if (modelModifiers.uppercase) {
return value?.toUpperCase();
}
},
});
</script>
這時(shí)我們給defineModel傳進(jìn)去的第一個(gè)參數(shù)就是包含get 和 set 方法的對(duì)象,當(dāng)對(duì)modelValue變量進(jìn)行讀操作時(shí)會(huì)走到get方法里面去,當(dāng)對(duì)modelValue變量進(jìn)行寫(xiě)操作時(shí)會(huì)走到set方法里面去。如果只需要對(duì)寫(xiě)操作進(jìn)行攔截,那么可以不用寫(xiě)get。
defineModel的返回值也可以解構(gòu)成兩個(gè)變量,第一個(gè)變量就是我們前面幾個(gè)例子的ref對(duì)象,用于給v-model綁定。第二個(gè)變量是一個(gè)對(duì)象,里面包含了有哪些修飾符,在這里我們有trim和uppercase兩個(gè)修飾符,所以modelModifiers的值為:
{
trim: true,
uppercase: true
}
在輸入框進(jìn)行輸入時(shí),就會(huì)走到set方法里面,然后調(diào)用value?.toUpperCase()就可以實(shí)現(xiàn)將輸入的字母變成大寫(xiě)字母。
總結(jié)
這篇文章介紹了如何使用defineModel宏實(shí)現(xiàn)雙向綁定以及defineModel的實(shí)現(xiàn)原理。
- 在子組件內(nèi)調(diào)用
defineModel宏會(huì)返回一個(gè)ref對(duì)象,在子組件內(nèi)可以直接對(duì)這個(gè)ref對(duì)象進(jìn)行賦值,父組件內(nèi)的相應(yīng)變量也會(huì)同步修改。 defineModel其實(shí)就是在子組件內(nèi)定義了一個(gè)ref變量和對(duì)應(yīng)的prop,然后監(jiān)聽(tīng)了對(duì)應(yīng)的prop保持ref變量的值始終和對(duì)應(yīng)的prop是一樣的。在子組件內(nèi)當(dāng)修改ref變量值時(shí)會(huì)拋出一個(gè)事件給父組件,讓父組件更新對(duì)應(yīng)的變量值,從而實(shí)現(xiàn)雙向綁定。- 使用
defineModel({ type: String, default: "20" })就可以定義prop的type和default等選項(xiàng)。 - 使用
defineModel("count")就可以實(shí)現(xiàn)多個(gè)v-model綁定。 - 通過(guò)解構(gòu)
defineModel()的返回值拿到modelModifiers修飾符對(duì)象,配合get和set轉(zhuǎn)換器選項(xiàng)實(shí)現(xiàn)自定義修飾符。
到此這篇關(guān)于一文帶你搞懂Vue3 defineModel中的雙向綁定的文章就介紹到這了,更多相關(guān)Vue3 defineModel雙向綁定內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue3如何利用自定義指令實(shí)現(xiàn)下拉框分頁(yè)懶加載
下拉框一開(kāi)始請(qǐng)求第一頁(yè)的內(nèi)容,滾動(dòng)到最后的時(shí)候,請(qǐng)求第二頁(yè)的內(nèi)容,如此反復(fù),直到所有數(shù)據(jù)加載完成,這篇文章主要介紹了vue3如何利用自定義指令實(shí)現(xiàn)下拉框分頁(yè)懶加載,需要的朋友可以參考下2024-07-07
Vue微信項(xiàng)目按需授權(quán)登錄策略實(shí)踐思路詳解
這篇文章主要介紹了Vue微信項(xiàng)目按需授權(quán)登錄策略實(shí)踐思路詳解,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-05-05
vue3.0 CLI - 2.4 - 新組件 Forms.vue 中學(xué)習(xí)表單
這篇文章主要介紹了vue3.0 CLI - 2.4 - 新組件 Forms.vue 中學(xué)習(xí)表單的相關(guān)知識(shí),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值 ,需要的朋友可以參考下2018-09-09
在vue.js渲染完界面之后如何再調(diào)用函數(shù)
這篇文章主要介紹了在vue.js渲染完界面之后如何再調(diào)用函數(shù)的實(shí)現(xiàn)方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-07-07
在Vue3中實(shí)現(xiàn)四種全局狀態(tài)數(shù)據(jù)的統(tǒng)一管理的方法
在開(kāi)發(fā)中,通常遇到四種全局狀態(tài)數(shù)據(jù):異步數(shù)據(jù)、同步數(shù)據(jù),傳統(tǒng)的Vue3使用不同機(jī)制處理這些數(shù)據(jù),而Zova框架通過(guò)Model機(jī)制來(lái)統(tǒng)一管理,簡(jiǎn)化了數(shù)據(jù)處理流程,提高了代碼的可維護(hù)性,本文介紹在Vue3中實(shí)現(xiàn)四種全局狀態(tài)數(shù)據(jù)的統(tǒng)一管理的方法,感興趣的朋友一起看看吧2024-10-10
Vue render函數(shù)實(shí)戰(zhàn)之實(shí)現(xiàn)tabs選項(xiàng)卡組件
這篇文章主要介紹了Vue render函數(shù)實(shí)戰(zhàn)之實(shí)現(xiàn)tabs選項(xiàng)卡組件的相關(guān)知識(shí),非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-04-04
Vue-Cli如何在index.html中進(jìn)行環(huán)境判斷
這篇文章主要介紹了Vue-Cli如何在index.html中進(jìn)行環(huán)境判斷問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03
腳手架(vue-cli)創(chuàng)建Vue項(xiàng)目的超詳細(xì)過(guò)程記錄
用vue-cli腳手架可以快速的構(gòu)建出一個(gè)前端vue框架的項(xiàng)目結(jié)構(gòu),下面這篇文章主要給大家介紹了關(guān)于腳手架(vue-cli)創(chuàng)建Vue項(xiàng)目的超詳細(xì)過(guò)程,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-05-05

