vue3的setup語(yǔ)法如何自定義v-model為公用hooks
前言
- 基礎(chǔ)篇:簡(jiǎn)單介紹
vue3
的setup
語(yǔ)法如何自定義v-model
; - 進(jìn)階篇:如何提取
v-model
語(yǔ)法作為一個(gè)公用hooks
;
基礎(chǔ)
基礎(chǔ)篇可繞過(guò),只是對(duì)于官網(wǎng)給出的教程,進(jìn)行了總結(jié)概括并給出demo
基本的v-model
子組件中滿足兩個(gè)點(diǎn),即可完成自定義雙向綁定:
props
中定義一個(gè)值xxx
emit
中定義一個(gè)update:xxx
事件
下面我們來(lái)寫(xiě)一個(gè)最基本的v-model
組件:
props
中定義一個(gè)modelValue
值,并綁定到input
的value
屬性上;emit
中定義一個(gè)update:modelValue
事件
需要注意的是,當(dāng)modelValue
作為props
傳入,update:modelValue
事件將被自動(dòng)注冊(cè)到emit
事件中
<template> ??<input ????type="text" ????@input="emit('update:modelValue',?$event.target.value)" ????:value="props.modelValue" ??/> </template> <script?setup> const?emit?=?defineEmits(); const?props?=?defineProps({ ??modelValue:?String, }); </script>
父組件中,引入modelComp
子組件,并綁定test
值到v-model
上,test便完成了一次雙向綁定。
<template> ??<modelComp?v-model="test"></modelComp> </template> <script?setup> import?{?ref,?watch?}?from?"vue"; import?modelComp?from?"./components/model/modelComp.vue"; const?test?=?ref(""); </script>
這便是一個(gè)最基本的自定義v-model
組件;
多個(gè)v-model綁定
當(dāng)我們需要多個(gè)雙向綁定時(shí),如下:
<modelComp ??v-model="test" ??v-model:test1="test1" ??v-model:test2="test2" ></modelComp> <script?setup> import?{?ref,?watch?}?from?"vue"; import?modelComp?from?"./components/model/modelComp.vue"; const?test?=?ref(""); const?test1?=?ref(""); const?test2?=?ref(""); </script>
子組件中,同樣按著兩個(gè)點(diǎn)來(lái)定義:
props
中定義兩個(gè)值,test1
和test2
emits
中定義兩個(gè)事件,update:test1
和update:test2
<template> ??<input ????type="text" ????@input="emit('update:modelValue',?$event.target.value)" ????:value="props.modelValue" ??/> ??<input ????type="text" ????@input="emit('update:test1',?$event.target.value)" ????:value="props.test1" ??/> ??<input ????type="text" ????@input="emit('update:test2',?$event.target.value)" ????:value="props.test2" ??/> </template> <script?setup> const?emit?=?defineEmits(["update:modelValue","update:test1",?"update:test2"]); const?props?=?defineProps({ ??modelValue:?String, ??test1:?String, ??test2:?String, }); </script>
v-model修飾符
vue
提供了一些v-model
修飾符,我們可以在v-model
中使用他們:
<modelComp ??v-model.trim="test" ??v-model:test1.lazy="test1" ??v-model:test2.trim.lazy="test2" ></modelComp>
在一些場(chǎng)景下,我們需要自己定義修飾符,來(lái)滿足我們的需求,舉個(gè)栗子:
<modelComp ??v-model.a="test" ??v-model:test1.b.c="test1" ></modelComp>
默認(rèn)v-model
中我們綁定了a
修飾符,v-model:test1
中則綁定b
和c
兩個(gè)修飾符;
對(duì)于修飾符,我們需要滿足以下條件:
- 對(duì)于默認(rèn)
v-model
來(lái)說(shuō),需要props
中定義兩個(gè)值modelValue
modelModifiers
,接受修飾符key
值
- 對(duì)于自定義
v-model:xxx
來(lái)說(shuō),props
中:xxx
xxxModeifiers
,接受修飾符key
值
由此,上代碼:
<template> ??<input?type="text"?@input="vModelInput"?:value="props.modelValue"?/> ??<input?type="text"?@input="vModelTest1"?:value="props.test1"?/> </template> <script?setup> const?emit?=?defineEmits(["update:modelValue",?"update:test1"]); const?props?=?defineProps({ ??modelValue:?String, ??//接受v-model的修飾符 ??modelModifiers:?{ ????default:?()?=>?({}), ??}, ??test1:?String, ??//接受v-model:test1的修飾符 ??test1Modifiers:?{ ????default:?()?=>?({}), ??} }); const?vModelInput?=?(e)?=>?{ ??let?value?=?e.target.value ??console.log(props.modelModifiers); ??//{a:true} ??if(props.modelModifiers.a){ ??????//處理value值 ??} ??emit("update:modelValue",?value); }; const?vModelTest1?=?(e)?=>?{ ??let?value?=?e.target.value ??console.log(props.test1Modifiers); ??//{b:true,c:true} ??if(props.modelModifiers.b){ ??????//處理value值 ??} ??if(props.modelModifiers.c){ ??????//處理value值 ??} ??emit("update:test1",?value); }; </script>
進(jìn)階
問(wèn)題背景
基礎(chǔ)篇中已經(jīng)講解了如何封裝一個(gè)自定義v-model
的組件,可是在實(shí)際開(kāi)發(fā)中,子組件中使用@input
和:value
來(lái)綁定我們的值,會(huì)比較麻煩,有沒(méi)有更簡(jiǎn)單的辦法呢?
我們通常想要對(duì)需要雙向綁定的子組件,直接進(jìn)行v-model
綁定:
<!--?子組件?--> <input?type="text"?v-model="xxx"?/>
問(wèn)題來(lái)了,在子組件中接受到父組件的傳值時(shí),xxx
我們應(yīng)該綁定誰(shuí)?直接綁定props.modelValue
么?
<!--?子組件?--> <input?type="text"?v-model="props.modelValue"/>
我們會(huì)得到一個(gè)錯(cuò)誤:
??reactivity.esm-bundler.js:512?Set?operation?on?key?"modelValue"?failed:?target?is?readonly.
因?yàn)?code>props是一個(gè)readonly
的值(isReadonly(props) === true
),所以我們不能直接這么使用
所以,我們是需要一個(gè)中間值來(lái)綁定v-model
方式一:通過(guò)watch中轉(zhuǎn)
借助內(nèi)部變量綁定v-model
,使用watch
監(jiān)聽(tīng)它,并同步數(shù)據(jù)props.xxx
<!--?子組件?--> <template> ??<input?type="text"?v-model="proxy"?/> </template> <script?setup> import?{?ref,?watch?}?from?"vue"; const?emit?=?defineEmits(); const?props?=?defineProps({ ??modelValue:?String, }); const?proxy?=?ref(props.modelValue); watch( ??()?=>?proxy.value, ??(v)?=>?emit("update:modelValue",v) ); </script>
因?yàn)橛袝r(shí)候我們雙向綁定的可能是一個(gè)對(duì)象或者數(shù)組,因此我們可以使用watch
里的deep
選項(xiàng)來(lái)深度監(jiān)聽(tīng)并同步proxy
;
watch( ??()?=>?proxy.value, ??(v)?=>?emit("update:modelValue",v), ??{deep:true} );
當(dāng)然,props.modelValue
可能存在默認(rèn)值傳入,所以我們也可以加上immediate
選項(xiàng),使得組件在創(chuàng)建時(shí),就直接給proxy
賦上默認(rèn)值;
方式二:computed的get和set
我們也可以借助computed
提供的get
和set
來(lái)進(jìn)行數(shù)據(jù)同步
const?proxy?=?computed({ ??get()?{ ????return?props.modelValue; ??}, ??set(v)?{ ????emit("update:modelValue",?v); ??}, });
終極:封裝v-model的hooks
我們先來(lái)提取watch
這種方式,將其封裝為一個(gè)hooks
<!--?子組件?--> <template> ??<input?type="text"?v-model="proxy"?/> </template> <script?setup> import?{?ref,?watch,?computed?}?from?"vue"; const?emit?=?defineEmits(); const?props?=?defineProps({ ??modelValue:?String, }); const?proxy?=?ref(props.modelValue); watch( ??()?=>?proxy.value, ??(v)?=>?emit("update:modelValue",?v) ); </script>
在子組件中,我們用v-model
在input
上綁定了一個(gè)內(nèi)部值proxy
,并以props.modelValue
的值初始化proxy
變量(ref(props.modelValue)
);
在watch
中,我們監(jiān)聽(tīng)input
上的綁定值proxy
,在input
進(jìn)行輸入其值變化時(shí),向外分發(fā)emit('update:modelValue',v)
事件,將改變的值動(dòng)態(tài)傳到外部組件上
提取公用邏輯
//?useVmodel1.js import?{?ref,?watch?}?from?"vue"; export?function?useVmodel(props,?emit)?{ ??const?proxy?=?ref(props.modelValue); ??watch( ????()?=>?proxy.value, ????(v)?=>?emit("update:modelValue",?v) ??); ??return?proxy; }
一個(gè)最簡(jiǎn)單的hooks
便被封裝好了;
<template> ??<input?type="text"?v-model="proxy"?/> </template> <script?setup> import?{?ref,?watch,?computed?}?from?"vue"; import?{?useVmodel?}?from?"./hooks/useVmodel1"; const?emit?=?defineEmits(); const?props?=?defineProps({ ??modelValue:?String, }); const?proxy?=?useVmodel(props,?emit); </script>
繼續(xù)抽離封裝
考慮到以下幾個(gè)點(diǎn),繼續(xù)進(jìn)行抽離封裝:
emit
可以不傳,更簡(jiǎn)潔的調(diào)用方式- 多個(gè)
v-model:test1
這種情況的事件,emit("update:xxxx")
中的xxxx
事件名需要提取
我們可以通過(guò)vue3
提供的getCurrentInstance
方法,獲取當(dāng)前的組件實(shí)例,而modelValue
可覆蓋,則抽取成變量:
//useVmodel2.js import?{?ref,?watch,?getCurrentInstance?}?from?"vue"; export?function?useVmodel(props,?key?=?"modelValue",?emit)?{ ??const?vm?=?getCurrentInstance(); ??const?_emit?=?emit?||?vm?.emit; ??const?event?=?`update:${key}`; ??const?proxy?=?ref(props[key]); ??watch( ????()?=>?proxy.value, ????(v)?=>?_emit(event,?v) ??); ??return?proxy; }
好了,現(xiàn)在我們可以更簡(jiǎn)單的調(diào)用我們的hooks
了:
<!--?子組件?childModel?--> <template> ??<input?type="text"?v-model="modelValue"?/> ??<input?type="text"?v-model="test"?/> </template> <script?setup> import?{?useVmodel?}?from?"./hooks/useVmodel2"; const?emit?=?defineEmits(); const?props?=?defineProps({ ??modelValue:?String, ??test:?String, }); const?modelValue?=?useVmodel(props); const?test?=?useVmodel(props,?"test"); </script> <!--?父組件?--> <template> ??<Model?v-model="modelValue"?v-model:test="test"?/> </template>? <script?setup> import?{?ref,?watch?}?from?"vue"; import?Model?from?"./childModel.vue"; const?modelValue?=?ref(""); const?test?=?ref(""); </script>
到此這篇關(guān)于vue3的setup語(yǔ)法如何自定義v-model為公用hooks的文章就介紹到這了,更多相關(guān)vue自定義v-model內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue+axios 攔截器實(shí)現(xiàn)統(tǒng)一token的案例
這篇文章主要介紹了vue+axios 攔截器實(shí)現(xiàn)統(tǒng)一token的案例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-09-09vue前端HbuliderEslint實(shí)時(shí)校驗(yàn)自動(dòng)修復(fù)設(shè)置
這篇文章主要為大家介紹了vue前端中Hbulider中Eslint實(shí)時(shí)校驗(yàn)自動(dòng)修復(fù)設(shè)置操作過(guò)程,有需要的朋友可以借鑒參考下希望能夠有所幫助2021-10-10Vue3?KeepAlive實(shí)現(xiàn)原理解析
KeepAlive?是一個(gè)內(nèi)置組件,那封裝一個(gè)組件對(duì)于大家來(lái)說(shuō)應(yīng)該不會(huì)有太大的困難,它的核心邏輯在于它的?render?函數(shù),它用?map?去記錄要緩存的組件,就是?[key,vnode]?的形式,這篇文章主要介紹了Vue3?KeepAlive實(shí)現(xiàn)原理,需要的朋友可以參考下2022-09-09基于Vue、Vuex、Vue-router實(shí)現(xiàn)的購(gòu)物商城(原生切換動(dòng)畫(huà))效果
這篇文章主要介紹了基于Vue、Vuex、Vue-router實(shí)現(xiàn)的購(gòu)物商城(原生切換動(dòng)畫(huà))效果,需要的朋友可以參考下2018-01-01vue實(shí)現(xiàn)手機(jī)號(hào)碼的校驗(yàn)實(shí)例代碼(防抖函數(shù)的應(yīng)用場(chǎng)景)
這篇文章主要給大家介紹了關(guān)于vue實(shí)現(xiàn)手機(jī)號(hào)碼的校驗(yàn)的相關(guān)資料,主要是防抖函數(shù)的應(yīng)用場(chǎng)景,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用vue具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09