vue父子组件的双向数据绑定

123 阅读2分钟

vue2实现

在实现父子组件通信的时候我们一般是用emit抛出事件来实现,用vue2举个例子

vue create vue2-app //创建一个简单的vue2项目
cd vue2-app
npm run serve // 启动项目

在项目的components文件夹下新建score.vue

<template>
  <div class="welcome">
    <input v-model="count" />
    <button @click="handleClick">plus</button>
  </div>
</template>
<script>
export default {
  // eslint-disable-next-line vue/multi-word-component-names
  name: 'Score',
  props:{
    start:{
      type: Number,
      default: 0
    }
  },
  data(){
    return{
      // 在 子组件 中不能直接修改 props 里面的内容,所以需要维护一个count,start作为默认值传入
      count: this.$props.start 
    }
  },
  methods:{
    handleClick(){
      this.count++;
      this.$emit('onChange',this.count)
    }
  }
}
</script>

在HelloWorld.vue文件引入子组件count.vue,删除掉多余的内容

<template>
  <div class="hello">
    result: {{ count }} 
    <score @onChange="onChange" :start="count" />
  </div>
</template>

<script>
import Score from './score.vue';
export default {
  name: 'HelloWorld',
  props: {
    msg: String
  },
  components:{
    Score
  },
  data(){
    return{
      count: 10
    }
  },
  methods:{
    onChange(e){
      this.count = e
    }
  }
}
</script>

Screen Shot 2024-07-23 at 17.00.09.png

start是Score组件的一个属性,父组件调用的时候可以通过start给Score设置个起始值,每次点击加1的时候都抛出事件,在父组件里面更改count值。有没有办法让父子组件之间传值像input那么方便呢,答案是有的。
v-model是一个语法糖,v-model 相当于 :value="value" @input="getInput",它做了如下事情

  1. 绑定数据value
  2. 触发输入事件input,然后emit抛出事件
  3. data更新触发重新渲染

可以通过model属性自定义value和getInput

model:{
    prop: 'myValue',// 自定义传入的值
    event: 'myInput'// 自定义emit抛出事件
}

Score.vue修改成

<template>
  <div class="welcome">
    <input v-model="count" />
    <button @click="handleClick">plus</button>
  </div>
</template>
<script>
export default {
  // eslint-disable-next-line vue/multi-word-component-names
  name: 'Score',
  props:{
    myStart: Number
  },
  model:{
    prop: 'myStart',
    event: 'myInput'
  },
  data(){
    return{
      count: this.$props.myStart
    }
  },
  methods:{
    handleClick(){
      this.count++;
      this.$emit('myInput',this.count)
    }
  }
}
</script>

HelloWorld.vue简写为:

<template>
  <div class="hello">
    result: {{ count }} 
     <score v-model="count" />
  </div>
</template>

<script>
import Score from './score.vue';
export default {
  name: 'HelloWorld',
  props: {
    msg: String
  },
  components:{
    Score
  },
  data(){
    return{
      count: 10
    }
  }
}
</script>

父组件在引用子组件的时候使用了v-model指令,并且不需要再写onChange方法维护count值,count会自动加1。子组件的v-model指令极大方便了父组件的调用。

vue3实现

vue3中没有了vue2的model属性,v-model默认的props属性名称是modelValue,绑定的事件是update:modelValue。以下用vue3实现一遍。

npm create vue@latest //创建一个简单的项目
cd vue3-app
npm install
npm run dev // 启动项目

子组件Score.vue

<template>
  <div>
    <input v-model="count" />
    <button @click="handleClick">plus</button>
  </div>
</template>
<script setup>
import { ref } from 'vue';
import { defineProps } from 'vue';
const emits = defineEmits(['update:modelValue'])
const props = defineProps({
  modelValue:{// 默认的名称modelValue
    type: Number,
    default: 0
  }
})
const count = ref(props.modelValue)
const handleClick = () =>{
  count.value++
  emits('update:modelValue',count.value)// 默认的事件名称
}
</script>

父组件App.vue

<script setup>
import { ref } from 'vue';
import Score from './components/Score.vue';
const count = ref(0)
</script>

<template>
  <header>
    <img alt="Vue logo" class="logo" src="./assets/logo.svg" width="125" height="125" />
    <div>result: {{ count }}</div>
     <score v-model="count" />
  </header>
</template>

如果要自定义双向绑定的model名称也可以。例如在Score.vue中双向绑定的model名称改为myValue,对应事件名称也改为update:myValue

<template>
  <div>
    <input v-model="count" />
    <button @click="handleClick">plus</button>
  </div>
</template>
<script setup>
import { ref } from 'vue';
import { defineProps } from 'vue';
const emits = defineEmits(['update:myValue'])
const props = defineProps({
  myValue:{
    type: Number,
    default: 0
  }
})
const count = ref(props.myValue)
const handleClick = () =>{
  count.value++
  emits('update:myValue',count.value)
}
</script>

父组件使用的时候就要指定model名称,这种方式可以实现多个数据的双向绑定,vue2也可以实现。

<score v-model:my-value="count" />