前言
大家好,我是辉夜真是太可爱啦。
(本文仅适合刚入门v3的小白用户如何自行封装 v-model)
文本主要讲述在
Vue3
中如何一步步封装v-model
,为何要使用currentValue
进行变量中转,以及为何要使用 watch 监听 ,以及和Vue2
中不同的是,如何封装多个v-model
。以及文末提及了最新的defineModel
ok,那我们先从简单的一个 input封装开始。
首先,先了解, v-model 其实就是一个语法糖,其实就是父组件中
<ChildComponent v-model:modelValue="inputValue" />
的语法糖缩写。
我们可以将上面的代码进行进一步的拆分。
<ChildComponent :modelValue="inputValue" @update:modelValue="(val)=>inputValue = val" />
现在大家应该理解了,说白了就是 子组件中 emit('update:modelValue')
以及 props:modelValue
的组合,就是那么简单。(如果对于emit以及props也不太清楚,可以往下看,笔者有着相应的说明)
只是 modelValue
是官方指定的默认值,v-model:modelValue
可以省略为 v-model
。
那么,我们来代码实现一下。
定义 props:modelValue
props在子组件中定义,当父组件使用v-bind绑定子组件中定义的props时,可以从父组件中将变量的值传入子组件中,且实时更新
在子组件中,首先定义一个输入框,以及定义一个 props
。
javascript
<template>
<el-input v-model="modelValue" />
</template>
<script setup>
import { defineProps } from 'vue'
const props = defineProps({
modelValue: {
type: String,
default: '',
},
})
</script>
v-model
无法直接绑定 props
我们发现,程序直接报错了,给了我们提示 Use a v-bind binding combined with a v-on listener that emits update:x event instead.
大意就是 v-model 不能直接绑定 props ,所以我们通过一个变量来中转一下。
我们加一行 let currentValue = ref(props.modelValue)
然后将 el-input
绑定这个 currentValue
。
现在代码如下:
javascript
<template>
<el-input v-model="currentValue" />
</template>
<script setup>
import { defineProps, ref } from 'vue'
const props = defineProps({
modelValue: {
type: String,
default: '',
},
})
let currentValue = ref(props.modelValue)
</script>
实现 emit('update:modelValue')
emit主要是在子组件中定义,可以通过 emit('自定义的emit事件','想要传递的值')来向父组件传值,在父组件中,通过@自定义的emit事件,来接收子组件中传入的值
我们可以通过 @update:modelValue
监听输入框的变化,将它的值传递给父组件。
先定义 emit
事件
const emit = defineEmits(['update:modelValue'])
接下来,我们就可以使用 emit('update:modelValue', currentValue.value)
去更新值了。
javascript
<template>
<el-input v-model="currentValue" @input="updateValue" />
</template>
<script setup>
import { defineProps, ref, defineEmits } from 'vue'
const props = defineProps({
modelValue: {
type: String,
default: '',
},
})
const emit = defineEmits(['update:modelValue'])
let currentValue = ref(props.modelValue)
const updateValue = (value) => {
currentValue.value = value
emit('update:modelValue', currentValue.value)
}
</script>
父组件使用
javascript
<template>
<MyInput v-model="inputValue" />
</template>
<script setup>
import MyInput from '@/components/MyInput.vue'
let inputValue = ref('test')
</script>
为了更加方便效果的展示了,我们可以在 MyInput
后面加入 {{inputValue}}
,可以看到,当子组件中输入框更改时,父组件中的 inputValue
也会实时更新。
关于 watch 监听 modelValue
但是,比方说我们在父组件中加入一个按钮,点击他即可以改变 inputValue
的值
javascript
<MyInput v-model="inputValue" /> {{ inputValue }}
<el-button type="primary" size="default" @click="inputValue = '重置'">重置</el-button>
点击按钮之后,我们会发现,父组件中更新了值,但是子组件中不会触发更新。
还记得我们由于之前弄了个 currentValue
值的中转吗,modelValue
更新了,但是没有触发 currentValue
的更新,所以我们可以用一个 watch
监听 modelValue
的变化。
javascript
watch(
() => props.modelValue,
(newValue) => {
currentValue.value = newValue
}
)
当然,这仅限于如果你需要父组件中的更新,实时响应到子组件中,那你可以添加一个watch监听
如何v-model:show
比方说我现在有一个弹窗,我需要实现 v-model:show
,那怎么办呢,很简单,你只需要将子组件中的 modelValue
全局替换为 show
。
也就是说 定义 props:show
使用 emit('update:show')
, 父组件中使用 v-model:show
即可。
javascript
<template>
<el-dialog v-model="currentShow" title="标题" :before-close="handleClose">
<div>我的弹窗</div>
</el-dialog>
</template>
<script setup>
const props = defineProps({
show: {
type: Boolean,
default: false,
},
})
const emit = defineEmits(['update:show'])
let currentShow = ref(props.show)
watch(
() => props.show,
(newValue) => {
currentShow.value = newValue
}
)
const handleClose = () => {
currentShow.value = false
emit('update:show', currentShow.value)
}
</script>
实现多个 v-model
相比于v2,我们还可以使用多个v-model。
例如我们实现一个弹窗组件 v-model:show 控制弹窗的显示隐藏, v-model控制输入框中的值。
在 Vue3
中,我们可以将相同的功能块进行分块处理。
子组件:
javascript
<template>
<el-dialog v-model="currentShow" title="标题" :before-close="handleClose">
<el-input v-model="currentValue" @input="updateValue" />
</el-dialog>
</template>
<script setup>
const props = defineProps({
show: {
type: Boolean,
default: false,
},
modelValue: {
type: String,
default: '',
},
})
const emit = defineEmits(['update:show', 'update:modelValue'])
let currentValue = ref(props.modelValue)
watch(
() => props.modelValue,
(newValue) => {
currentValue.value = newValue
}
)
const updateValue = (value) => {
currentValue.value = value
emit('update:modelValue', currentValue.value)
}
let currentShow = ref(props.show)
watch(
() => props.show,
(newValue) => {
currentShow.value = newValue
}
)
const handleClose = () => {
currentShow.value = false
emit('update:show', currentShow.value)
}
</script>
父组件:
javascript
<template>
<MyDialog v-model:show="dialogShow" v-model="inputValue" />
</template>
<script setup>
import MyDialog from '@/components/MyDialog.vue'
let dialogShow = ref(true)
let inputValue = ref('test')
</script>
defineModel
在了解完基础使用之后,官方还推出了新的 defineModel()
, 需要注意的是,这是Vue3.4+ 推出的功能,使用前先确认自己的版本是否高于 3.4
感兴趣的可以点击这里查看cn.vuejs.org/api/sfc-scr...