vue2和vue3组件封装父子组件之间的通信之v-model

635 阅读1分钟

亲自动手实现一个v-model

大家好,我是铁柱,时常分享一些项目的收获。 点赞 + 关注 + 收藏 = 学会了

新增:vue3 v-model已更新(2022-12-30)

背景

在日常封装组件时,会遇到这种情况,给子组件传值,然后经过子组件的内部处理,将处理之后的值再传回父组件,平常会使用父子组件之间的相互传值实现,父组件传入属性,子组件通过 props 接收,子组件通过$emit()向外弹出一个自定义事件, 在父组件中的属性监听事件,同时也能获取子组件传出来的参数,这样写既要在父组件中处理属性还需要处理自定义事件的回调,今天就给大家介绍一下使用 v-model是如何处理这种情况的。

一个好的公共组件,首先是 API 的设计应该让人容易理解,并且使用方便。再就是应该尽量将公共逻辑放在子组件中,这样子才会让组件的封装更有意义。

基础知识-父子组件的通信,父子组件之间怎么传值

子组件:

<template>
  <div>
    父组件传递的数据{{ parentText }}
    <button @click="$emit('childEvent', '子组件的数据')">按钮</button>
  </div>
</template>

<script>
export default {
  name: 'ChildCompontent',
  props: {
    // 接收父组件的数据
    parentText: String,
  },
}
</script>

父组件:

<template>
  子组件传递的数据{{ childText }}
  <child-compontent parentText="父组件的数据" @childEvent="parentFn" />
</template>

<script>
import ChildCompontent from './components/ChildCompontent'
export default {
  components: { ChildCompontent },
  data() {
    return {
      childText: ''
    }
  },
  methods: {
    parentFn(val) {
      // 接收子组件的数据
      this.childText = val
    }
  }
}
</script>

vue2使用v-model改造父子组件的通信

子组件:

<template>
  <div>
    父组件传递的数据{{ this.$attrs.parentText }}
    <button @click="$emit('childEvent', '子组件的数据')">按钮</button>
  </div>
</template>

<script>
export default {
  name: 'ChildCompontent',
  model: {
    event: 'childEvent', // 默认值input
    prop:'parentText' // 默认值value
  },
}
</script>

父组件:

<template>
  text的数据{{ text }}
  <child-compontent v-model="text" />
</template>

<script>
import ChildCompontent from './components/ChildCompontent'
export default {
  components: { ChildCompontent },
  data() {
    return {
      // 接收子组件的数据
      text: '父组件的数据'
    }
  },
}
</script>

(新)vue2和vue3中v-model两者的区别

  • 默认值的改变

    • prop:value -> modelValue
    • 事件:input -> update:modelValue
  • 新增 支持多个v-model

  • 新增 支持自定义 修饰符 Modifiers

  • v-bind 的 .sync 修饰符和组件的 model 选项已移除

(新)vue3使用v-model改造父子组件的通信

子组件:

<template>
  <div>
    父组件传递的数据{{ modelValue }}
    <button @click="$emit('update:modelValue', '子组件的数据')">按钮</button>
    <button @click="$emit('update:text2', '子组件的数据text2')">按钮text2</button>
  </div>
</template>

<script setup> 
    defineProps({ modelValue: String, text2: String, })
    defineEmits(['update:modelValue', 'update:text2'])
</script>

父组件:

<template>
  text的数据{{ text }}
  text2的数据{{ text2 }}
  <child-compontent v-model="text" v-model:text2="text2" />
</template>

<script setup>
import ChildCompontent from './components/ChildCompontent'
let text = ref('父组件的数据')
let text2 = ref('')
</script>

小结

通过比较,这样处理之后在使用组件时,无需记特定的 prop 字段名,即可绑定到组件中的值,也无需处理自定义事件,降低组件的使用成本。

v-model 实际上就是 $emit('input') 以及 props:value 的组合语法糖,只要组件中满足这两个条件,就可以在组件中使用 v-model

凡事都有例外,例如:

checkboxradio 使用 props:checked 属性和 $emit('change') 事件。

select 使用 props:value 属性和 $emit('change') 事件。

但是,除了上面列举的这些,别的都是 $emit('input') 以及 props:value