Vue 组件上使用 v-model
2024年9月25日...大约 3 分钟
基本用法
在子组件中使用 defineModel()
,在父组件中的子组件上使用 v-model
绑定,此时 defineModel()
返回的值将和 v-model
绑定的值进行双向绑定
单文件组件
<!-- App.vue -->
<template>
{{ studentName }}
<Student v-model="studentName"></Student>
</template>
<script setup>
import Student from "./components/Student.vue";
import { ref } from "vue";
const studentName = ref("John");
</script>
<!-- Student.vue -->
<template>
<input type="text" v-model="name" />
</template>
<script setup>
const name = defineModel();
</script>
HTML
defineModel()
只能用在 <script setup>
上,如想实现相同的效果,可以看下面底层原理部分
底层原理
defineModel()
实际上是使用了 props
和自定义事件实现的
单文件组件
<!-- App.vue -->
<template>
{{ studentName }}
<Student
:name="studentName"
@update-name="($event) => (studentName = $event)"
></Student>
</template>
<script setup>
import Student from "./components/Student.vue";
import { ref } from "vue";
const studentName = ref("John");
</script>
<!-- Student.vue -->
<template>
<input
type="text"
:value="name"
@input="$emit('updateName', $event.target.value)"
/>
</template>
<script setup>
const props = defineProps(["name"]);
const emit = defineEmits(["updateName"]);
</script>
HTML
defineModel()
只能用在 <script setup>
上,如想实现相同的效果,请参考以下代码
<body>
<div id="app">
{{ studentName }}
<Student
:name="studentName"
@update-name="$event => studentName = $event"
></Student>
</div>
<script type="module">
import {
createApp,
ref,
} from "https://unpkg.com/vue@3/dist/vue.esm-browser.js";
import Student from "./Student.js";
createApp({
components: {
Student,
},
setup() {
const studentName = ref("John");
return {
studentName,
};
},
}).mount("#app");
</script>
</body>
// Student.js
export default {
props: ["name"],
emits: ["updateName"],
setup() {
return {};
},
template: `
<input type="text" :value="name" @input = "$emit('updateName', $event.target.value)">`,
};
因为 defineModel()
声明了一个 prop
,我们可以给它传递选项,来声明底层 prop
的选项
多个 v-model 绑定
通过给 v-model
添加参数,可以绑定多个 v-model
在子组件中使用 defineModel(参数名, 选项)
,在父组件中的子组件上使用 v-model:参数名
绑定
单文件组件
<!-- App.vue -->
<template>
{{ studentName }}
{{ studentAge }}
<Student v-model:name="studentName" v-model:age="studentAge"></Student>
</template>
<script setup>
import Student from "./components/Student.vue";
import { ref } from "vue";
const studentName = ref("John");
const studentAge = ref(20);
</script>
<!-- Student.vue -->
<template>
<input type="text" v-model="name" />
<input type="number" v-model="age" />
</template>
<script setup>
const name = defineModel("name");
const age = defineModel("age");
</script>
HTML
defineModel()
只能用在 <script setup>
上,如想实现相同的效果,请参考以下代码
<body>
<div id="app">
{{ studentName }} {{ studentAge}}
<Student
:name="studentName"
@update-name="$event => studentName = $event"
:age="studentAge"
@update-age="$event => studentAge = $event"
></Student>
</div>
<script type="module">
import {
createApp,
ref,
} from "https://unpkg.com/vue@3/dist/vue.esm-browser.js";
import Student from "./Student.js";
createApp({
components: {
Student,
},
setup() {
const studentName = ref("John");
const studentAge = ref(20);
return {
studentName,
studentAge,
};
},
}).mount("#app");
</script>
</body>
// Student.js
export default {
props: ["name", "age"],
emits: ["updateName", "updateAge"],
setup() {
return {};
},
template: `
<input type="text" :value="name" @input = "$emit('updateName', $event.target.value)">
<input type="number" :value="age" @input = "$emit('updateAge', $event.target.value)">`,
};
处理 v-model 修饰符
通过解构 defineModel()
,可以处理 v-model
的修饰符
为了处理修饰符,我们需要在 defineModel()
选项中传入一个 get
或 set
函数,它将接收 v-model
绑定的值,并返回处理后的值
单文件组件
<!-- App.vue -->
<template>
{{ studentName }}
<!-- 一个自定义修饰符 -->
<Student v-model.capitalize="studentName"></Student>
</template>
<script setup>
import Student from "./components/Student.vue";
import { ref } from "vue";
const studentName = ref("John");
</script>
<!-- Student.vue -->
<template>
<input type="text" v-model="name" />
</template>
<script setup>
// 通过解构赋值获取 v-model 的值和修饰符
const [name, modifiers] = defineModel({
set(value) {
if (modifiers.capitalize) {
// 如果有 capitalize 修饰符,则将首字母大写
return value.charAt(0).toUpperCase() + value.slice(1);
}
return value;
},
});
console.log(modifiers); // { capitalize: true }
</script>
HTML
defineModel()
只能用在 <script setup>
上,如想实现相同的效果,请参考以下代码
<body>
<div id="app">
{{ studentName }}
<Student
:name="studentName"
:modifiers="{ capitalize:true }"
@update-name="$event => studentName = $event"
>
</Student>
</div>
<script type="module">
import {
createApp,
ref,
} from "https://unpkg.com/vue@3/dist/vue.esm-browser.js";
import Student from "./Student.js";
createApp({
components: {
Student,
},
setup() {
const studentName = ref("John");
return {
studentName,
};
},
}).mount("#app");
</script>
</body>
// Student.js
export default {
props: {
name: String,
modifiers: {
default: () => ({}),
},
},
emits: ["updateName"],
setup(props, ctx) {
const emitValue = (e) => {
let value = e.target.value;
if (props.modifiers.capitalize) {
value = value.charAt(0).toUpperCase() + value.slice(1);
}
ctx.emit("updateName", value);
};
return {
emitValue,
};
},
template: `
<input type="text" :value="name" @input = "emitValue">`,
};