前言

vue中,v-model指令用于实现表单输入元素和应用程序状态之间的双向数据绑定,比如像<input><textarea><select>,使用v-model可以很方便地将表单的值绑定到vue实例的数据上,实现双向数据绑定,本文主要是通过对比在v3.4版本前后的使用对比,进而在后续的项目中大力采用这个v-model来简化纯父子组件之间的通讯机制!

vue3.4之前关于在组件中v-model的使用

自定义组件需要手动接收value属性,并通过$emit触发input事件,来更新父组件的值

1
2
3
4
5
6
7
8
<template>
<input :value="value" @input="$emit('input', $event.target.value)">
</template>
<script>
export default {
props: ['value']
}
</script>

vue3.4之后关于在组件中 v-model的使用

vue3.4开始,推荐使用的实现方式是使用defineModel()宏:

1
2
3
4
5
6
7
8
9
10
<!-- Child.vue -->
<template>
<div>父节点捆绑的数据是{{ model }}</div>
</template>
<script setup>
const model = defineModel()
function update(){
module.value ++
}
</script>

然后,在父组件中可以用v-model绑定一个值:

1
<Child v-model="count"/>

😕 这个过程发生了什么了呢?为什么直接使用v-model + defineModel()就可以替换掉之前那么复杂的实现呢?

defineModel()的过程

defineModel()的返回值应该是一个ref,它可以像其他的ref一样被访问以及修改,不过它能够起到在父组件和当前变量之间的双向绑定的作用, 😖 但是,又为什么使用defineModel()所创建出来的ref就可以实现双向的数据绑定了呢?
按照官方文档的解释:defineModel是一个便利宏,它主要做了两件事:

  1. 创建一个名为modelValue的属性,然后本地的ref(即在data中创建另外一个ref,我这里称之为dataModelRef)与这个modelValue同步,这里的同步采用watch的方式来实现;
  2. 声明一个名为update:modelValue的回调方法,当上述的dataModelRef的值发生变化的时候,将自动触发这个方法,这里的自动触发,也是通过采用watch的方式来实现的自动触发!

这里关于数据的流动方向依然还是保持一条路径,也就是父组件定义变量,然后传递给子组件,子组件做展示,当子组件需要更新时,父组件接收事件更新调用,更新父组件变量,然后再传递给子组件继续做展示!!

🤩 可以看一下关于其中生成的父子组件的成员变量
v-model父子组件的成员

v-model例子

v-model自定义modal

通过上面的例子我们可以看出,在后续的自定义组件中,可直接使用v-model + defineModel() + v-model的方式,来创建双向绑定的自定义组件

父组件定义子组件的属性,子组件仅做可视化展示(vue2+)

vuecoding过程中,经常会使用到父子组件的通信,一般是父组件通过传递属性给到子组件,然后子组件接收到属性,并另外定义一data属性,作为子组件中的绑定,并在data属性更新的时候,通过$emit()的方式触发父组件去更新父组件中所定义的属性,完成父子组件间的通讯闭环

如果将这个属性直接传递给子组件,然后子组件直接通过v-model的方式来实现双向绑定的话,则会提示重复渲染的问题,如下图所示:
子组件直接v-model引用父组件的属性
:-1: 因此,不能直接使用子组件来做双向绑定更新父组件的属性, 👉 这里是由于父组件传递的是一基本数据类型的属性,基本数据类型的传递一般是值传递,当子组件通过v-model绑定到传递过来的基本数据类型属性时,所触发的操作将会导致子组件不断的重复渲染,因此才会有上述 的问题!

😕 但是,如果子组件所接收参数过多的话,通过来回这样子通讯的话,估计维护惨了,这个时候,可以选择采用将相关的属性合并到一个对象中,将对象作为一属性进行传递,然后子组件通过v-model的方式直接引用,这样子当子组件通过v-model对传递过来的属性进行更新时,将会通过对象引用的方式来进行的更新操作,因此能够正常的进行双向绑定更新操作! 👉 也就是说,我们可以将所有的响应式变量定义到父组件中,然后子组件所需要的变量由父组件通过对象的方式来传递,子组件通过v-model的方式来对属性进行双向绑定,这可以大大减少父子组件通讯的代码量,而且交由子组件的双向绑定,可减少重复的props/emit操作!!!
父组件双向绑定的运用

总结

其实vue3.4+的版本,提供的defineModel宏,与自己单独一个个去编写对应的代码来控制无异,两者在使用过程上是没什么区别的,但是使用了defineModel可以大大减少这个重复的代码量,再也不用去定义props,然后接收props,同时再定义emit,然后触发对应的emit动作,并传递参数, 👉 而是通过”类model”的props的定义(通过defineModel来创建),然后直接更新创建出来的model属性即可完成双向绑定的目的!! :+1: 后续在这种父子组件之间的通讯的时候,完完全全可以使用v-model:属性的方式来实现父子组件之间的通讯机制!!