Preface
- Basics: A brief introduction
vue3
ofsetup
How to customize syntaxv-model
; - Advanced: How to extract
v-model
Syntax as a publichooks
;
Base
The basics can be bypassed, but the tutorials given by the official website are summarized and givendemo
Basic v-model
Custom bidirectional binding can be completed if two points are satisfied in the child component:
-
props
Define a value inxxx
-
emit
Define one inupdate:xxx
event
Let's write a basic onev-model
Components:
-
props
Define one inmodelValue
value and bind toinput
ofvalue
Attributes; -
emit
Define one inupdate:modelValue
event
It should be noted that whenmodelValue
Asprops
Incoming,update:modelValue
Events will be automatically registered toemit
In the event
<template> <input type="text" @input="emit('update:modelValue', $)" :value="" /> </template> <script setup> const emit = defineEmits(); const props = defineProps({ modelValue: String, }); </script>
In parent component, importmodelComp
Subcomponents and bindingtest
Value tov-model
On, test completes a two-way binding.
<template> <modelComp v-model="test"></modelComp> </template> <script setup> import { ref, watch } from "vue"; import modelComp from "./components/model/"; const test = ref(""); </script>
This is the most basic customizationv-model
components;
Multiple v-model bindings
When we need multiple two-way bindings, as follows:
<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/"; const test = ref(""); const test1 = ref(""); const test2 = ref(""); </script>
In the subcomponent, it is also defined by two points:
-
props
define two values,test1
andtest2
-
emits
defines two events,update:test1
andupdate:test2
<template> <input type="text" @input="emit('update:modelValue', $)" :value="" /> <input type="text" @input="emit('update:test1', $)" :value="props.test1" /> <input type="text" @input="emit('update:test2', $)" :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 modifier
vue
Provide somev-model
Modifiers, we canv-model
Use them in:
<modelComp ="test" v-model:="test1" v-model:="test2" ></modelComp>
In some scenarios, we need to define modifiers ourselves to meet our needs.Take a chestnut:
<modelComp ="test" v-model:="test1" ></modelComp>
defaultv-model
We're bounda
Modifier,v-model:test1
In the middle, bindb
andc
Two modifiers;
For modifiers, we need to meet the following conditions:
- For the default
v-model
In other words, it is necessaryprops
Define two values inmodelValue
-
modelModifiers
, accept modifierskey
value
- For customization
v-model:xxx
To be honest,props
middle:xxx
-
xxxModeifiers
, accept modifierskey
value
From this, the above code:
<template> <input type="text" @input="vModelInput" :value="" /> <input type="text" @input="vModelTest1" :value="props.test1" /> </template> <script setup> const emit = defineEmits(["update:modelValue", "update:test1"]); const props = defineProps({ modelValue: String, //Accept the modifier of v-model modelModifiers: { default: () => ({}), }, test1: String, //Accept the modifier of v-model:test1 test1Modifiers: { default: () => ({}), } }); const vModelInput = (e) => { let value = (); //{a:true} if(){ //Processing value } emit("update:modelValue", value); }; const vModelTest1 = (e) => { let value = (props.test1Modifiers); //{b:true,c:true} if(){ //Processing value } if(){ //Processing value } emit("update:test1", value); }; </script>
Advanced
Problem background
The basics have already explained how to encapsulate a customv-model
components, but in actual development, are used in subcomponents@input
and:value
It will be more troublesome to bind our values. Is there an easier way?
We usually want to directly perform child components that require two-way bindingv-model
Bind:
<!-- Subcomponents --> <input type="text" v-model="xxx" />
The problem is that when the child component receives the value passed from the parent component,xxx
Who should we bind? Direct bindingIs it?
<!-- Subcomponents --> <input type="text" v-model=""/>
We will get an error:
⚠️:512 Set operation on key "modelValue" failed: target is readonly.
becauseprops
It's onereadonly
The value ofisReadonly(props) === true
), so we cannot use it directly
So, we need an intermediate value to bindv-model
Method 1: Transfer through watch
Binding with internal variablesv-model
,usewatch
Listen to it and synchronize the data
<!-- Subcomponents --> <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(); watch( () => , (v) => emit("update:modelValue",v) ); </script>
Because sometimes we can bind two-way to an object or an array, so we can usewatch
Insidedeep
Options for deep monitoring and synchronizationproxy
;
watch( () => , (v) => emit("update:modelValue",v), {deep:true} );
certainly,There may be default values passed in, so we can also add them
immediate
Options so that the component is directly given toproxy
Assign default values;
Method 2: computed get and set
We can also use thecomputed
Providedget
andset
To synchronize data
const proxy = computed({ get() { return ; }, set(v) { emit("update:modelValue", v); }, });
Ultimate: hooks that encapsulate v-model
Let's extract it firstwatch
This way, encapsulate it into onehooks
<!-- Subcomponents --> <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(); watch( () => , (v) => emit("update:modelValue", v) ); </script>
In the subcomponent, we usev-model
existinput
An internal value is bound toproxy
, andInitialization of the value of
proxy
variable(ref()
);
existwatch
In, we monitorinput
The binding value onproxy
,existinput
When the input value changes, distribute it to the outsideemit('update:modelValue',v)
Event, dynamically pass the changed value to external components
Extract public logic
// import { ref, watch } from "vue"; export function useVmodel(props, emit) { const proxy = ref(); watch( () => , (v) => emit("update:modelValue", v) ); return proxy; }
The easiesthooks
Then it was encapsulated;
<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>
Continue to remove the package
Taking into account the following points, continue to extract the package:
-
emit
Can not pass, a more concise call method - Multiple
v-model:test1
Events of this situation,emit("update:xxxx")
In-housexxxx
Event name needs to be extracted
We can passvue3
ProvidedgetCurrentInstance
method, get the current component instance, andmodelValue
Can be overridden, then extracted into variables:
// 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( () => , (v) => _emit(event, v) ); return proxy; }
OK, now we can call ours more easilyhooks
Come on:
<!-- Subcomponents 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> <!-- Parent component --> <template> <Model v-model="modelValue" v-model:test="test" /> </template> <script setup> import { ref, watch } from "vue"; import Model from "./"; const modelValue = ref(""); const test = ref(""); </script>
This is the article about how to customize v-model as public hooks in vue3. For more related vue custom v-model content, please search for my previous articles or continue browsing the following related articles. I hope everyone will support me in the future!