单向数据流
Vue遵循单向数据流原则,父组件向子组件传递props,子组件不能更改props,否则会报错,也即数据只能由父组件流向子组件,子组件不能修改父组件数据。
双向数据绑定
那么在一些场景下,如弹窗场景,子组件的确需要更新父组件数据,要怎么处理呢?Vue推荐是父组件使用props传递数据给子组件,子组件再通过自定义事件通知父组件修改数据,这样一来数据就始终是由父组件控制的了。
xml
<!-- 父组件 -->
<template>
<button @click="show = true">打开</button>
<Dialog
:show="show"
@update:show="e => show = e"
>
</Dialog>
</template>
<script>
export default{
data(){
return {
show: false
}
}
}
</script>
xml
<!-- 子组件 -->
<template>
<div v-if="show">
<button @click="$emit('update:show', false)">关闭</button>
</div>
</template>
<script>
export default {
props: {
show: {
type: Boolean,
default: false
}
}
}
</script>
Vue2使用.sync语法糖
在Vue2中提供了修饰符.sync作为双向数据绑定的语法糖,使用.sync修饰符,父组件可以写为
ini
<Dialog :show.sync="show"></Dialog>
子组件不变,同样通过this.$emit更新数据。
Vue3使用v-model
Vue3中则是移除了.sync修饰符,选择用v-model实现双向数据绑定,在Vue3中可以写成
ini
<Dialog v-model:show="show"></Dialog>
子组件中同样通过emit自定义事件更新数据,注意Vue3中不能用this.$emit,而是要defineEmits(["update:show"])定义事件。
xml
<!-- 子组件 -->
<template>
<div v-if="show">
<button @click="$emit('update:show', false)">关闭</button>
</div>
</template>
<script setup>
import { defineEmits } from "vue"
const $emit=defineEmits(['update:show'])
</script>
defineModel
在Vue3.4+中还提供了defineModel宏,使得双向绑定的实现更加简洁,在使用子组件时直接defineModel声明一个model prop,这样,在子组件内部直接修改这个声明的值就会自动触发update事件。
如果第一个参数是一个字符串字面量,它将被用作 prop 名称;否则,prop 名称将默认为 "modelValue"。
xml
<!-- 子组件 -->
<template>
<div v-if="show">
<button @click="show=false">关闭</button>
</div>
</template>
<script setup>
const show=defineModel('show',{type: Boolean,default: false})
</script>