前言:最近在做毕设的时候,需要二次封装Naive UI的Input组件,达到多次复用的效果,然后遇到了需要v-model来二次传值的需求,实现方法本身不难,这里把Vue3的做法叙述一下,Vue2应该也差不多。
操作方法
- 所谓的
v-model
其实就是v-bind
搭配v-on
和emit
的组合语法糖,可以通过这三个东西来定义需要双向绑定的变量名
- 直接上代码
父组件
1
| <Input v-model:value="val" />
|
子组件
- 子组件需要的做的事情其实不多,通过
props
定义一个叫value
的变量,然后绑定到input
属性的:value
上面
- props中的value你可以给他定义类型,也可以不定义,看你喜欢
- 然后需要定义一个
update:value
的emit事件,一定要叫这个名字,一定要叫这个名字——update:value
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <template> <input :value="props.value" @input="Inputing" /> </template>
<script lang="ts" setup> import { ref, toRefs, computed } from 'vue'; const props = defineProps({ value: { type: [String, Number, Boolean, Array, Object], default: '' } }) const emit = defineEmits(["update:value"])
function Inputing(el: any) { const value = el.target ? el.target.value : el emit('update:value', value) }
</script>
|
- 如果你的props中定义的不是value,比如是title,那么你的父组件应该是这样的👇
1
| <Input v-model:title="val" />
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <template> <input :value="props.title" @input="Inputing" /> </template>
<script lang="ts" setup> import { ref, toRefs, computed } from 'vue'; const props = defineProps({ title: { type: [String, Number, Boolean, Array, Object], default: '' } }) const emit = defineEmits(["update:title"])
function Inputing(el: any) { const value = el.target ? el.target.value : el emit('update:value', value) }
</script>
|
emit的事件名一定是update:
+ 你要用v-model双向绑定传值的变量名
,也就是上面props中的value
或者是title
核心方法
1 2 3 4
| function Inputing(el: any) { const value = el.target ? el.target.value : el emit('update:value', value) }
|
- v-on:input绑定的方法,我不知道是Naive UI中的
@input
方法是不是和原生的不太一样
- 原生的似乎在输入的时候返回了$event事件,但是Naive UI中的input组件第一次是返回$event事件,第二次后面开始返回输入的值
- 所以这里我做了一个判断,如果el是$event事件,那么它会有个target,然后取
$event.target.value
即可
- 如果没有则直接取它的值,不然会有**[object InputEvent]**的BUG
解决方案就是这个
1
| const value = el.target ? el.target.value : el
|
如果是和我一样用了Navie UI中的Input进行一个v-model的二次封装的话,可以用 :on-update:value
来进行emit方法的绑定
1
| <n-input :value="props.value" :on-update:value="Inputing"/>
|
其他方法
除了上面说的这种通过原生的input事件搭配v-on的方法实现父子组件的v-model传值的方法外,还有其他可以用的方法
比如可以通过watch
方法进行监听props.value
,当value发生变化的时候,通过emit给父组件传改变后的值
也可以通过计算属性computed
来拦截父组件传过来的value,进行修改。
另外,这个v-model的父子传值还能绑定多个,也就是说可以搞多个v-model的绑定,进行父子传值间的双向绑定。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
| <template> <div class="flex items-center inputString" v-if="!selfSelect && (typeof (props.value) !== 'number')"> <span>{{ title }}</span> <n-input :placeholder="placeholder" :value="props.value || ''" class="w-50 m-2" :on-update:value="Inputing" :disabled="props.disabled" :maxlength="selfMaxLength" :allow-input="selfAllow" ref="inputInstRef"> <template #prefix v-if="props.Icon"> <n-icon :component="Icon" /> </template> </n-input> </div> <div class="flex items-center inputNumber" v-else-if="!selfSelect && typeof (props.value) === 'number'"> <span>{{ title }}</span> <n-input-number :placeholder="placeholder" :value="props.value" class="flex items-center popInput w-full" :on-update:value="Inputing" :disabled="props.disabled" :show-button="false" :min="1" :validator="validator" ref="inputInstRef"> <template #prefix v-if="props.Icon"> <n-icon :component="Icon" /> </template> </n-input-number> </div> <n-space vertical v-else-if="selfSelect"> <div class="flex items-center"> <span>{{ title }}</span> <n-select :value="props.value || ''" :options="Options" @update:value="Inputing" /> </div> </n-space> </template>
<script lang="ts" setup> import vail from '@/utils/validator'; import { ref, toRefs, computed } from 'vue'; import { useState } from '@/stores'; const props = defineProps({ Icon: { default: '' }, value: { type: [String, Number, Boolean, Array, Object], default: '' }, title: { type: String, default: '' }, placeholder: { type: String, default: '' }, disabled: { type: Boolean, default: false } }) const emit = defineEmits(["update:value"])
function Inputing(el: any) { emit('update:value', el) }
const vailRule = vail();
const index = Object.keys(vailRule).indexOf(props.title) const selfRequired = ref(false) const selfMaxLength = ref() const selfMessage = ref(false) const selfAllow = ref() const selfPattern = ref() const selfSelect = ref(false) if (index !== -1) { const { required, maxlength, message, allowInput, pattern, select } = vailRule[props.title] selfRequired.value = required selfMaxLength.value = maxlength selfMessage.value = message selfAllow.value = allowInput selfPattern.value = pattern if (select) { selfSelect.value = select } }
const validator = (x: number) => x >= 1
const { Options } = toRefs(useState())
</script>
<style lang="scss" scoped> span { text-align: center; margin: 0 10px; width: fit-content; display: -webkit-box; -webkit-line-clamp: 1; -webkit-box-orient: vertical; text-overflow: ellipsis; white-space: nowrap; } </style>
|