自学内容网 自学内容网

【Vue】Vue3.0(二十二) v-model 在原始Dom元素、自定义输入组件中双向绑定的底层实现原理详解

上篇文章 【Vue】Vue3.0(二十一)Vue 3.0中 的$event使用示例

🏡作者主页:点击!

🤖Vue专栏:点击!

⏰️创作时间:2024年11月11日17点30分

v-model 底层实现的详细解释:

1. v-model 用于 HTML 标签时的底层原理

v-model 用于普通的 HTML 输入标签(如 <input type="text">)时,它实际上是一种语法糖,背后做了以下几件事:

  • 数据绑定
    • v-model 会将输入框的 value 属性与 Vue 实例中的数据属性进行绑定。在上述 Father.vue 中,v-model="username" 会将输入框的 valueusername 建立双向数据绑定关系。这意味着当 username 的值发生变化时,输入框的 value 会自动更新,反之亦然。
  • 事件监听
    • 同时,v-model 会自动为输入框添加 input 事件监听器。当用户在输入框中输入内容时,input 事件会被触发,然后事件处理函数会获取输入框的最新值,并将其更新到对应的 Vue 实例数据属性中。在底层,Vue 会自动处理这个更新过程,确保数据的一致性。

2. v-model 用于自定义组件时的底层原理

v-model 用于自定义组件(如 <AtguiguInput>)时,它的实现方式略有不同,但遵循一定的约定:

  • modelValue prop 和 update:modelValue 事件
    • 自定义组件需要通过名为 modelValueprop 来接收父组件传递的值,这类似于普通 v-model 中的数据绑定部分。在 AtguiguInput.vue 中,通过 defineProps(['ming', 'mima']) 接收了父组件传递的 mingmima,这里的 mingmima 就类似于 modelValue 的作用,用于接收父组件的数据。
    • 当子组件内部的数据发生变化时,需要通过触发名为 update:modelValue 的自定义事件来通知父组件更新数据。在 AtguiguInput.vue 中,通过 emit('update:ming', (<HTMLInputElement>$event.target).value)emit('update:mima', (<HTMLInputElement>$event.target).value) 实现了这一点,当输入框的 input 事件触发时,会将最新的值通过相应的 update 事件传递给父组件。

3. 自定义 v-model 修饰符的底层实现

Father.vue 中,使用了自定义的 v-model 修饰符 mingmima,这是对 v-model 功能的一种扩展:

  • 自定义绑定和事件
    • 通过 v-model:ming="username"v-model:mima="password",Vue 会将 usernamepassword 分别与子组件中的 mingmima 进行绑定,并监听对应的 update:mingupdate:mima 事件来更新数据。这种自定义修饰符的方式允许开发者根据具体需求灵活地定义数据绑定和事件更新的逻辑,使得 v-model 在不同的场景下能够更精准地满足业务需求。

4. 与响应式系统的结合

无论是用于 HTML 标签还是自定义组件,v-model 的底层实现都与 Vue 的响应式系统紧密结合:

  • 数据更新的响应式传播
    • 当输入框的值发生变化并更新了 Vue 实例中的数据属性时,Vue 的响应式系统会检测到这个变化,并自动触发相关的 DOM 更新操作,确保所有依赖于该数据属性的视图部分都能得到及时更新。同样,当 Vue 实例中的数据属性在其他地方被修改时,绑定的输入框也会相应地更新显示,从而实现了数据和视图的双向同步更新,保持了整个应用的状态一致性。

综上所述,v-model 的底层实现通过数据绑定、事件监听、自定义事件触发以及与响应式系统的协作,为开发者提供了一种简洁而强大的双向数据绑定机制,无论是在普通的 HTML 表单元素还是自定义组件中,都能方便地实现数据与视图之间的交互和同步。

代码示例:
AtguiguInput.vue

<template>
  <input
    type="text"
    :value="ming"
    @input="emit('update:ming', (<HTMLInputElement>$event.target).value)"
  >
  <br>
  <input
    type="text"
    :value="mima"
    @input="emit('update:mima', (<HTMLInputElement>$event.target).value)"
  >
</template>

<script setup lang="ts" name="AtguiguInput">
defineProps(['ming', 'mima'])
const emit = defineEmits(['update:ming', 'update:mima'])
</script>

<style scoped>
input {
  border: 2px solid black;
  background-image: linear-gradient(45deg, red, yellow, green);
  height: 30px;
  font-size: 20px;
  color: white;
}
</style>

Father.vue

<template>
  <div class="father">
    <h3>父组件</h3>
    <h4>{{ username }}</h4>
    <h4>{{ password }}</h4>
    <!-- v-model用在html标签上 -->
    <!-- <input type="text" v-model="username"> -->
    <!-- <input type="text" :value="username" @input="username = (<HTMLInputElement>$event.target).value"> -->

    <!-- v-model用在组件标签上 -->
    <!-- <AtguiguInput v-model="username"/> -->
    <!-- <AtguiguInput 
      :modelValue="username" 
      @update:modelValue="username = $event"
    /> -->

    <!-- 修改modelValue -->
    <AtguiguInput
      v-model:ming="username"
      v-model:mima="password"
    />
  </div>
</template>

<script setup lang="ts" name="Father">
import { ref } from "vue";
import AtguiguInput from './AtguiguInput.vue'
// 数据
let username = ref('zhansgan')
let password = ref('123456')
</script>

<style scoped>
.father {
  padding: 20px;
  background-color: rgb(165, 164, 164);
  border-radius: 10px;
}
</style>

详解:
1.

<!-- 使用v-model指令 -->
<input type="text" v-model="userName">

<!-- v-model的本质是下面这行代码 -->
<input 
  type="text" 
  :value="userName" 
  @input="userName =(<HTMLInputElement>$event.target).value"
>
  1. 组件标签上的v-model的本质::moldeValueupdate:modelValue事件。

    <!-- 组件标签上使用v-model指令 -->
    <AtguiguInput v-model="userName"/>
    
    <!-- 组件标签上v-model的本质 -->
    <AtguiguInput :modelValue="userName" @update:model-value="userName = $event"/>
    

    AtguiguInput组件中:

    <template>
      <div class="box">
        <!--将接收的value值赋给input元素的value属性,目的是:为了呈现数据 -->
    <!--给input元素绑定原生input事件,触发input事件时,进而触发update:model-value事件-->
        <input 
           type="text" 
           :value="modelValue" 
           @input="emit('update:model-value',$event.target.value)"
        >
      </div>
    </template>
    
    <script setup lang="ts" name="AtguiguInput">
      // 接收props
      defineProps(['modelValue'])
      // 声明事件
      const emit = defineEmits(['update:model-value'])
    </script>
    
  2. 也可以更换value,例如改成abc

    <!-- 也可以更换value,例如改成abc-->
    <AtguiguInput v-model:abc="userName"/>
    
    <!-- 上面代码的本质如下 -->
    <AtguiguInput :abc="userName" @update:abc="userName = $event"/>
    

    AtguiguInput组件中:

    <template>
      <div class="box">
        <input 
           type="text" 
           :value="abc" 
           @input="emit('update:abc',$event.target.value)"
        >
      </div>
    </template>
    
    <script setup lang="ts" name="AtguiguInput">
      // 接收props
      defineProps(['abc'])
      // 声明事件
      const emit = defineEmits(['update:abc'])
    </script>
    
  3. 如果value可以更换,那么就可以在组件标签上多次使用v-model

    <AtguiguInput v-model:abc="userName" v-model:xyz="password"/>
    

原文地址:https://blog.csdn.net/qq_39666711/article/details/143691211

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!