双向绑定怎么实现的?
v-model 是语法糖,本质是 props + emit。
父组件 子组件
│ │
│ :modelValue="count" │
│───────────────────────>│ props: ['modelValue']
│ │
│ @update:modelValue │
│<───────────────────────│ emit('update:modelValue', val)
│ │
等价的写法:
<Child v-model="count" />
<!-- 等价于 -->
<Child :modelValue="count" @update:modelValue="count = $event" />默认绑定 value + input:
// Child.vue
props: ['value'],
methods: {
update(val) {
this.$emit('input', val)
}
}Vue 2 用 .sync 实现多值双向绑定:
<!-- Parent.vue -->
<Child :title.sync="title" :name.sync="name" />
<!-- 会被展开为 -->
<Child
:title="title" @update:title="title = $event"
:name="name" @update:name="name = $event"
/>统一了 API,不再需要 .sync。
<!-- Child.vue -->
<script setup>
defineProps(['modelValue'])
defineEmits(['update:modelValue'])
</script>
<template>
<input
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
/>
</template><!-- Child.vue -->
<script setup>
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
const value = computed({
get() {
return props.modelValue
},
set(val) {
emit('update:modelValue', val)
}
})
</script>
<template>
<input v-model="value" />
</template><!-- Parent.vue -->
<template>
<UserName
v-model:first="firstName"
v-model:last="lastName"
/>
</template>
<script setup>
import { ref } from 'vue'
const firstName = ref('Tom')
const lastName = ref('Jerry')
</script><!-- UserName.vue -->
<script setup>
defineProps(['first', 'last'])
defineEmits(['update:first', 'update:last'])
</script>
<template>
<input
:value="first"
@input="$emit('update:first', $event.target.value)"
/>
<input
:value="last"
@input="$emit('update:last', $event.target.value)"
/>
</template><!-- trim: 自动去除首尾空格 -->
<input v-model.trim="text" />
<!-- number: 自动转数字 -->
<input v-model.number="num" />
<!-- lazy: 失焦时触发 -->
<input v-model.lazy="text" /><!-- Parent.vue -->
<template>
<Input v-model.capitalize="text" />
</template>
<script setup>
import { ref } from 'vue'
const text = ref('hello')
</script><!-- Input.vue -->
<script setup>
const props = defineProps({
modelValue: String,
modelModifiers: {
default: () => ({})
}
})
const emit = defineEmits(['update:modelValue'])
function handleInput(e) {
let value = e.target.value
// 应用自定义修饰符
if (props.modelModifiers.capitalize) {
value = value.charAt(0).toUpperCase() + value.slice(1)
}
emit('update:modelValue', value)
}
</script>
<template>
<input :value="modelValue" @input="handleInput" />
</template>| Vue 2 | Vue 3 | |
|---|---|---|
| Prop | value | modelValue |
| Event | input | update:modelValue |
| 多值 | .sync | 多个 v-model |
| 修饰符 | 无内置 | trim/number/lazy + 自定义 |