一文带你搞懂Vue3 defineModel中的双向绑定

 更新时间:2024年02月04日 15:38:18   作者:欧阳码农  
随着vue3.4版本的发布,defineModel也正式转正了,它可以简化父子组件之间的双向绑定,是目前官方推荐的双向绑定实现方式,下面就跟随小编一起深入了解一下defineModel的使用吧

前言

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

vue3.4以前如何实现双向绑定

大家应该都知道v-model只是一个语法糖,实际就是给组件定义了modelValue属性和监听update:modelValue事件,所以我们以前要实现数据双向绑定需要给子组件定义一个modelValue属性,并且在子组件内要更新modelValue值时需要emit出去一个update:modelValue事件,将新的值作为第二个字段传出去。

我们来看一个简单的例子,父组件的代码如下:

<template>
  <CommonInput v-model="inputValue" />
</template>

<script setup lang="ts">
import { ref } from "vue";

const inputValue = ref();
</script>

子组件的代码如下:

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

<script setup lang="ts">
const props = defineProps(["modelValue"]);
const emit = defineEmits(["update:modelValue"]);
</script>

上面的例子大家应该很熟悉,以前都是这样去实现v-model双向绑定的。但是存在一个问题就是input输入框其实支持直接使用v-model的,我们这里却没有使用v-model而是在input输入框上面添加value属性和input事件。

原因是因为从vue2开始就已经是单向数据流,在子组件中是不能直接修改props中的值。而是应该由子组件中抛出一个事件,由父组件去监听这个事件,然后去修改父组件中传递给props的变量。如果这里我们给input输入框直接加一个v-model="props.modelValue",那么其实是在子组件内直接修改props中的modelValue。由于单向数据流的原因,vue是不支持直接修改props的,所以我们才需要将代码写成上面的样子。

使用defineModel实现数据双向绑定

defineModel是一个宏,所以不需要从vue中import导入,直接使用就可以了。这个宏可以用来声明一个双向绑定 prop,通过父组件的 v-model 来使用。

基础demo

父组件的代码和前面是一样的,如下:

<template>
  <CommonInput v-model="inputValue" />
</template>

<script setup lang="ts">
import { ref } from "vue";

const inputValue = ref();
</script>

子组件的代码如下:

<template>
  <input v-model="model" />
</template>

<script setup lang="ts">
const model = defineModel();
model.value = "xxx";
</script>

在上面的例子中我们直接将defineModel的返回值使用v-model绑定到input输入框上面,无需定义 modelValue 属性和监听 update:modelValue 事件,代码更加简洁。defineModel的返回值是一个ref,我们可以在子组件中修改model变量的值,并且父组件中的inputValue变量的值也会同步更新,这样就可以实现双向绑定。

那么问题来了,从vue2开始就变成了单向数据流。这里修改子组件的值后,父组件的变量值也被修改了,那这不就变回了vue1的双向数据流了吗?其实并不是这样的,这里还是单向数据流,我们接下来会简单讲一下defineModel的实现原理。

实现原理

defineModel其实就是在子组件内定义了一个叫model的ref变量和modelValue的props,并且watch了props中的modelValue。当props中的modelValue的值改变后会同步更新model变量的值。并且当在子组件内改变model变量的值后会抛出update:modelValue事件,父组件收到这个事件后就会更新父组件中对应的变量值。

实现原理代码如下:

<template>
  <input v-model="model" />
</template>

<script setup lang="ts">
import { ref, watch } from "vue";

const props = defineProps(["modelValue"]);
const emit = defineEmits(["update:modelValue"]);
const model = ref();

watch(
  () => props.modelValue,
  () => {
    model.value = props.modelValue;
  }
);
watch(model, () => {
  emit("update:modelValue", model.value);
});
</script>

看了上面的代码后你应该了解到了为什么可以在子组件内直接修改defineModel的返回值后父组件对应的变量也会同步更新了吧。我们修改的其实是defineModel返回的ref变量,而不是直接修改props中的modelValue。实现方式还是和vue3.4以前实现双向绑定一样的,只是defineModel这个宏帮我们将以前的那些繁琐的代码给封装到内部实现了。

其实defineModel的源码中是使用 customRef 和 watchSyncEffect 去实现的,我这里是为了让大家能够更容易的明白defineModel的实现原理才举的refwatch的例子。如果大家对defineModel的源码感兴趣,请在评论区留言,如果感兴趣的小伙伴比较多,我会在下一期出一篇defineModel源码的文章。

defineModel如何定义type、default等

既然defineModel是声明了一个prop,那同样也可以定义prop的typedefault。具体代码如下:

const model = defineModel({ type: String, default: "20" });

除了支持typedefault,也支持requiredvalidator,用法和定义prop时一样。

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

同样也支持在父组件上面实现多个   v-model   绑定,这时我们给defineModel传的第一个参数就不是对象了,而是一个字符串。

const model1 = defineModel("count1");
const model2 = defineModel("count2");

在父组件中使用v-model时代码如下:

<CommonInput v-model:count1="inputValue1" />
<CommonInput v-model:count2="inputValue2" />

我们也可以在多个v-model中定义typedefault

const model1 = defineModel("count1", {
  type: String,
  default: "aaa",
});

defineModel如何使用内置修饰符和自定义修饰符

如果要使用系统内置的修饰符比如trim,父组件的写法还是和之前是一样的:

<CommonInput v-model.trim="inputValue" />

子组件也无需做任何修改,和上面其他的defineModel例子是一样的:

const model = defineModel();

defineModel也支持自定义修饰符,比如我们要实现一个将输入框的字母全部变成大写的uppercase自定义修饰符,同时也需要使用内置的trim修饰符。

我们的父组件代码如下:

<CommonInput v-model.trim.uppercase="inputValue" />

我们的子组件需要写成下面这样的:

<template>
  <input v-model="modelValue" />
</template>

<script setup lang="ts">
const [modelValue, modelModifiers] = defineModel({
  // get我们这里不需要
  set(value) {
    if (modelModifiers.uppercase) {
      return value?.toUpperCase();
    }
  },
});
</script>

这时我们给defineModel传进去的第一个参数就是包含get 和 set 方法的对象,当对modelValue变量进行读操作时会走到get方法里面去,当对modelValue变量进行写操作时会走到set方法里面去。如果只需要对写操作进行拦截,那么可以不用写get

defineModel的返回值也可以解构成两个变量,第一个变量就是我们前面几个例子的ref对象,用于给v-model绑定。第二个变量是一个对象,里面包含了有哪些修饰符,在这里我们有trimuppercase两个修饰符,所以modelModifiers的值为:

{
  trim: true,
  uppercase: true
}

在输入框进行输入时,就会走到set方法里面,然后调用value?.toUpperCase()就可以实现将输入的字母变成大写字母。

总结

这篇文章介绍了如何使用defineModel宏实现双向绑定以及defineModel的实现原理。

  • 在子组件内调用defineModel宏会返回一个ref对象,在子组件内可以直接对这个ref对象进行赋值,父组件内的相应变量也会同步修改。
  • defineModel其实就是在子组件内定义了一个ref变量和对应的prop,然后监听了对应的prop保持ref变量的值始终和对应的prop是一样的。在子组件内当修改ref变量值时会抛出一个事件给父组件,让父组件更新对应的变量值,从而实现双向绑定。
  • 使用defineModel({ type: String, default: "20" })就可以定义prop的typedefault等选项。
  • 使用defineModel("count")就可以实现多个v-model绑定。
  • 通过解构 defineModel() 的返回值拿到modelModifiers修饰符对象,配合 get 和 set 转换器选项实现自定义修饰符。

到此这篇关于一文带你搞懂Vue3 defineModel中的双向绑定的文章就介绍到这了,更多相关Vue3 defineModel双向绑定内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 结合康熙选秀讲解vue虚拟列表实现

    结合康熙选秀讲解vue虚拟列表实现

    这篇文章主要为大家介绍了结合康熙选秀讲解vue虚拟列表的原理使用,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-07-07
  • 详解HTTP4种方法(GET、POST、 PUT和DELETE)

    详解HTTP4种方法(GET、POST、 PUT和DELETE)

    本文介绍了HTTP协议中的四种方法:GET、POST、PUT和DELETE,分别用于不同的操作,GET用于获取数据,POST用于提交数据,PUT用于创建或更新资源,DELETE用于删除资源,每种方法都有其特点和适用场景,了解这些方法有助于更好地进行数据交互和开发,感兴趣的朋友一起看看吧
    2025-02-02
  • vue中非父子组件的通信你了解吗

    vue中非父子组件的通信你了解吗

    这篇文章主要为大家详细介绍了vue中非父子组件通信,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-02-02
  • vue中插值表达式使用的示例详解

    vue中插值表达式使用的示例详解

    Vue的插值表达式是一种特殊的语法,用于在模板中动态地将数据绑定到视图中,一般使用双大括号 ("{{ }}")将表达式包裹起来,下面我们就来根据三个案例来深入了解下插值表达式的使用吧
    2023-11-11
  • vue-create创建VUE3项目详细图文教程

    vue-create创建VUE3项目详细图文教程

    create-vue是Vue官方新的脚手架工具,底层切换到了vite(下一代前端工具链),为开发提供极速响应,下面这篇文章主要给大家介绍了关于vue-create创建VUE3项目的相关资料,需要的朋友可以参考下
    2024-03-03
  • vue对storejs获取的数据进行处理时遇到的几种问题小结

    vue对storejs获取的数据进行处理时遇到的几种问题小结

    这篇文章主要介绍了vue对storejs获取的数据进行处理时遇到的几种问题小结,需要的朋友可以参考下
    2018-03-03
  • Vue + Element 自定义上传封面组件功能

    Vue + Element 自定义上传封面组件功能

    这篇文章主要介绍了Vue + Element 自定义上传封面组件,本文通过示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-01-01
  • vue中checkbox如何修改为圆形样式

    vue中checkbox如何修改为圆形样式

    这篇文章主要介绍了vue中checkbox如何修改为圆形样式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-12-12
  • Vue项目报错:parseComponent问题及解决

    Vue项目报错:parseComponent问题及解决

    这篇文章主要介绍了Vue项目报错:parseComponent问题及解决,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-05-05
  • vsCode中vue文件无法提示html标签的操作方法

    vsCode中vue文件无法提示html标签的操作方法

    在vsCode中书写Vue页面时无法提示,那真是很郁闷的事情,下面这篇文章主要给大家介绍了关于vsCode中vue文件无法提示html标签的操作方法,需要的朋友可以参考下
    2023-03-03

最新评论