defineModel

前言

随着 Vue3.4 版本的发布,defineModel 也正式转正了。它可以简化父子组件之间的双向绑定,是目前官方推荐的双向绑定实现方式。

defineModel 使用

在开发的过程中,如果有需要通过子组件进行状态更新的话,v-model是一个绕不开的点。以前的v-model是这样用的 👇

Vue3中得 v-model 默认解析成 :modelValue 与 @update:modelValue

<!-- Father.vue -->
<template>
  <span>num:{{ num }}</span>
  <hr />
  <Child v-model="num" />
</template>

<script lang="ts" setup>
import { ref } from 'vue'
import Child from './Child.vue'
const num = ref<number>(0)
</script>

<!-- Child.vue -->
<template>
  num: {{ modelValue }}
  <button @click="onClick">count</button>
</template>

<script lang="ts" setup>
const $props = defineProps<{ modelValue: number }>()
const $emits = defineEmits<{
  (e: 'update:modelValue', newCount: number): void
  // 注册update:modelValue事件,作为状态更新的回调
}>()
function onClick() {
  $emits('update:modelValue', $props.modelValue + 1)
  // 状态更新时发布事件
}
</script>

在有了defineModel之后,我们就可以在Child.vue中这样实现 👇

<!-- Child.vue -->
<template>
  num: {{ num }}
  <button @click="onClick">count</button>
</template>

<script lang="ts" setup>
// 一步到位,完成事件注册和监听状态变化并发布事件
const num = defineModel({ type: Number, default: 0 })
// 单个v-model绑定等价于如下
// const num = defineModel('modelValue', { type: Number, default: 0 })
function onClick() {
  num.value += 1
}
</script>

defineModel如何实现多个 v-model 绑定 👇

<!-- Father.vue -->
<template>
  <span>num1: {{ num1 }}</span>
  <span>num2: {{ num2 }}</span>
  <hr />
  <Child v-model:num1="num1" v-model:num2="num2" />
</template>

<script lang="ts" setup>
import { ref } from 'vue'
import Child from './Child.vue'
const num1 = ref<number>(0)
const num2 = ref<number>(0)
</script>

<!-- Child.vue -->
<template>
  num1: {{ num1 }} num2: {{ num2 }}
  <button @click="onClick">+1</button>
</template>

<script lang="ts" setup>
const num1 = defineModel('num1', { type: Number, default: 20 })
const num2 = defineModel('num2', { type: Number, default: 20 })
// 一步到位,完成事件注册和监听状态变化并发布事件
function onClick() {
  num1.value += 1
  num2.value += 1
}
</script>

实现原理

<!-- Father.vue -->
<template>
  <span>num:{{ num }}</span>
  <hr />
  <Child v-model="num" />
</template>

<script lang="ts" setup>
import { ref } from 'vue'
import Child from './Child.vue'
const num = ref<number>(0)
</script>

<!-- Child.vue -->
<template>
  num: {{ num }}
  <button @click="onClick">count</button>
</template>

<script lang="ts" setup>
import { ref, watch } from 'vue'
const $props = defineProps<{ modelValue: number }>()
const $emits = defineEmits<{
  (e: 'update:modelValue', newCount: number): void
}>()

const num = ref()

watch(
  () => $props.modelValue,
  () => {
    num.value = $props.modelValue
  },
  {
    // 回调函数会在watch创建时立即执行一次
    immediate: true
  }
)
watch(num, () => {
  $emits('update:modelValue', num.value)
})

function onClick() {
  num.value += 1
}
</script>

等同于如下

<!-- Child.vue -->
<template>
  num: {{ num }}
  <button @click="onClick">count</button>
</template>

<script lang="ts" setup>
const num = defineModel({ type: Number, default: 0 })

function onClick() {
  num.value += 1
}
</script>

  • defineModel其实就是在子组件内定义了一个ref变量nummodelValue的props。

  • watch了props中的modelValue,watch创建时立即执行一次将props中的modelValue赋值给num。当props中的modelValue的值改变后会同步更新num变量的值。

  • watch了ref变量num,当在子组件内改变num变量的值后会抛出update:modelValue事件

  • 父组件收到这个事件后就会更新父组件中对应的变量值。

其实defineModel的源码中是使用 customRef 和 watchSyncEffect 去实现的,我这里是为了让大家能够更容易的明白defineModel的实现原理才举的refwatch的例子。

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值