👀 defineModel
是vue3.4中正式加入的API的,可以简化组件之间双向绑定的流程。
1.首先回顾一下基于v-modal实现的双向绑定
v-model是vue.js提供的一个语法糖,用于在表单元素和组件之间创建双向数据绑定,即当数据变属性化时,相应的表单元素也会自动更新。
1.1 v-model的写法
v-model的原理:v-bind绑定一个value属性,v-on指令给当前元素绑定事件
父组件:
js
<template>
<Children v-model:count="countNumber"></Children>
</template>
<script setup lang="ts">
import Children from './children.vue'
import {ref} from 'vue'
const countNumber = ref(1)
</script>
子组件:children
js
<template>
<div>
{{ props.count }}
<button @click="updateCount">child</button>
</div>
</template>
<script setup lang="ts">
const props = defineProps(["count"]);
const emits = defineEmits(['update:count'])
const updateCount = () =>{
emits('update:count', props.count+1)
}
</script>
1.2 自定义事件写法
父组件:
js
<template>
<Children :count="countNumber" @change="updateVal"></Children>
</template>
<script setup lang="ts">
import Children from './components/HelloWorld.vue'
import {ref} from 'vue'
const countNumber = ref(1)
const updateVal = (item:number) => {
countNumber.value = item
}
</script>
子组件:children
js
<template>
<div>
{{ count }}
<button @click="updateCount">child</button>
</div>
</template>
<script setup lang="ts">
const props = defineProps<{
count: number
}>()
const emits = defineEmits(['change'])
const updateCount = () =>{
emits('change', props.count+1)
}
</script>
从vue2开始,vue就是单向数据流,在子组件中是不能直接修改props中的值,由子组件抛出一个事件,由父组件去监听这个事件,再去修改父组件中传递给props的变量。自定义事件的写法更显式,适合复杂逻辑的场景。- v-model写法更简洁,减少了样板代码。
2.defineModel实现数据双向绑定
这个宏可以用来声明一个双向绑定 prop,通过父组件的 v-model
来使用。在底层,这个宏声明了一个 model prop 和一个相应的值更新事件。如果第一个参数是一个字符串字面量,它将被用作 prop 名称;否则,prop 名称将默认为 "modelValue"
。在这两种情况下,你都可以再传递一个额外的对象,它可以包含 prop 的选项和 model ref 的值转换选项。
父组件:
js
<template>
<Children v-model:count="countNumber"></Children>
</template>
<script setup lang="ts">
import Children from './children.vue'
import {ref} from 'vue'
const countNumber = ref(1)
</script>
子组件:
js
<template>
<div>
{{ count }}
<button @click="updateCount">child</button>
</div>
</template>
<script setup lang="ts">
const count = defineModel("count", {
type: Number,
default: 0
})
const updateCount = () =>{
count.value++
}
</script>
2.1修饰符和转换器
为了获取 v-model
指令使用的修饰符,我们可以像这样解构 defineModel()
的返回值:
父组件:
js
<Children v-model:message.capitalize="word"/>
<script setup lang="ts">
import Children from './children.vue'
import {ref} from 'vue'
const word = ref('test')
</script>
子组件:
js
<div>Message: {{ message }}</div>
<input :value="message" @input="handleInput">
<script setup lang="ts">
const [message, modifiers] = defineModel('message', {
type: String,
default: 'test',
set(value){
if(modifiers.capitalize){
return value.charAt(0).toUpperCase() + value.slice(1)
}
return value
}
})
const handleInput = (e) => {
message.value = e.target.value
}