Vue3 中再次封装已支持 v-model 的组件的方式

简介

Vue3 部分 API 进行了升级或改变,对于 v-model 的实现原理是相同的,但语法略微不同。主要有两种方式:

  • 通过 prop 和自定义事件
  • 通过一个支持 get/set 的计算属性代替 prop

方式1

与老版相同,都是先绑定一个 prop,并在组件内部通过 vue 的自定义事件返回:

子组件(这里将官方示例的默认 prop 名称 modelValue 改成了自定义名称 msg):

<script setup>
defineProps(['msg'])
defineEmits(['update:msg'])
</script>

<template>
  <input
    :value="msg"
    @input="$emit('update:msg', $event.target.value)"
  />
</template>

方式2

子组件:

<script setup>
import { ref, defineProps, defineEmits, computed, } from 'vue'
const props = defineProps(['msg'])
const emit = defineEmits(['update:msg'])

const value = computed({
    get() {
      return props.msg
    },
    set(value) {
      emit('update:msg', value)
    }
})

const title = ref('Hello World!')
</script>

<template>
<h1>这是子组件标题:{{ title }}</h1>
  <input v-model="value">
</template>

父组件:

<script setup>
  import Comp from './Comp.vue'
  import { ref } from 'vue'

  const title = ref('Hello World!')
</script>

<template>
  <!-- 这里的 :msg 非常重要,如果没有写上,子组件修改了内部值之后,及时成功调用了 emit() 方法,父组件也无法获取到更新值
     因为对于组件, v-model 绑定时,默认的 prop 名称是 modelValue ,而不是此处的变量名 title 
     也就是说,未绑定自定义名称时,子组件内部触发的事件其实是 emit('update:modelValue', newValue) -->
  <Comp v-model:msg=title />

  <h1>这是父组件标题:{{ title }}</h1>
</template>

简单对比

方式1与老版思路基本一致,但新增了默认的 prop 名称是 modelValue,且触发事件必须使用 update:modelValue 这种形式的限制。方式2则是通过一个计算属性,代替 prop 传递初始值并向外触发自定义事件,好处是原先的组件可以继续使用 v-model,坏处是代码量比方式1稍多。

方式1由于使用 prop 作为数据的来源,而 prop 是只能单向传递数据的,不允许修改其值,因此不能在原先的组件上直接使用 v-model 绑定,必须拆分 v-bindemit 这两个过程。

posted @ 2022-08-13 08:33  CJc_3103  阅读(1674)  评论(0)    收藏  举报