跳到主要内容

defineModel

2024年03月13日
柏拉文
越努力,越幸运

一、认识


defineModel 可以用来声明一个双向绑定 prop,通过父组件的 v-model 来使用。defineModel() 返回的值是一个 ref。它可以像其他 ref 一样被访问以及修改, 能起到在父组件和当前变量之间的双向绑定的作用。

  1. 它的 .value 和父组件的 v-model 的值同步

  2. 当它被子组件变更了,会触发父组件绑定的值一起更新

defineModel 是一个便利宏。编译器将其展开为以下内容:

  1. 一个名为 modelValueprop,本地 ref 的值与其同步

  2. 一个名为 update:modelValue 的事件,当本地 ref 的值发生变更时触发

defineModel 底层机制如下:

<script setup>
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
</script>

<template>
<input
:value="props.modelValue"
@input="emit('update:modelValue', $event.target.value)"
/>
</template>

二、语法


2.1 modelValue

// 第一个参数不传, 默认 prop 名称为 modelValue
const model = defineModel()

// 第一个参数为对象, 默认 prop 名称为 modelValue, 且传入配置
const model = defineModel({ type: String, default: "xx", require: true })

// 第一个参数如果为字符串变量,用作 prop 名称为 xx
const model = defineModel("xx")

// 第一个参数如果为字符串变量, 用作 prop 名称为 xx, 且传入配置
const model = defineModel("xx",{ type: String, default: "xx", require: true })

2.2 modelModifiers

为了获取 v-model 指令使用的修饰符,我们可以像这样解构 defineModel() 的返回值

const [modelValue, modelModifiers] = defineModel()

// 对应 v-model.trim
if (modelModifiers.trim) {
// ...
}

三、用法


3.1 使用 modelValue

子组件展示、修改

// 父组件 
<Child v-model="count"></Child>

// 子组件
<script setup>
const model = defineModel({ default: 0, require: true})

function update() {
model.value++
}
</script>

<template>
<div>parent bound v-model is: {{ model }}</div>
</template>

子组件双向绑定

// 父组件 
<Child v-model="count"></Child>


// 子组件
<script setup>
const model = defineModel({ default: 0, require: true})
</script>

<template>
<span>My input</span> <input v-model="model">
</template>

父组件使用多个 v-model

// 父组件
<script setup>
import { ref } from 'vue'
import UserName from './UserName.vue'

const first = ref('John')
const last = ref('Doe')
</script>

<template>
<h1>{{ first }} {{ last }}</h1>
<UserName
v-model:first-name="first"
v-model:last-name="last"
/>
</template>

// 子组件
<script setup>
const firstName = defineModel('firstName')
const lastName = defineModel('lastName')
</script>

<template>
<input type="text" v-model="firstName" />
<input type="text" v-model="lastName" />
</template>

3.2 使用 modelModifiers

v-model.trim

// 父组件 
<script setup>
import { ref } from 'vue'
import MyComponent from './MyComponent.vue'

const myText = ref('')
</script>

<template>
This input capitalizes everything you enter:
<MyComponent v-model.trim="myText" />
</template>

// 子组件
<script setup>
const [modelValue, modelModifiers] = defineModel({
set(value) {
if (modelModifiers.trim) {
return value.trim()
}
return value
}
})
</script>

<template>
<input type="text" v-model="modelValue" />
</template>

v-model.capitalize

// 父组件
<script setup>
import { ref } from 'vue'
import MyComponent from './MyComponent.vue'

const myText = ref('')
</script>

<template>
This input capitalizes everything you enter:
<MyComponent v-model.capitalize="myText" />
</template>

// 子组件
<script setup>
const [model, modifiers] = defineModel({
set(value) {
if (modifiers.capitalize) {
return value.charAt(0).toUpperCase() + value.slice(1)
}
return value
}
})
</script>

<template>
<input type="text" v-model="model" />
</template>