前面的话

虽然v-model和.sync之间的区别,是非常明显的,但是通过这个去研究下去,就有点意思了。首先说明v-model是.sync的一种特殊情况,也就是当prop是value,vm.$emit('update:input'),这个时候就是传统意义上的v-model,也就是说v-model是model为prop等于value,event为input的特殊情况。

概念

双向数据传递

为了和绑定区分我把他称为传递,也就是父子组件之间的传递

双向数据绑定

和双向数据绑定不同,双向数据绑定则是数据驱动dom,dom反作用于数据,vue通过Object.defineProperty(),实现了数据驱动dom,而通过dom驱动数据很容易实现了,举个例子当<input>,发生变化的时候把变化后的值赋给一个当前页面的局部变量,周而复始的实现双向的数据绑定

v-modle

引用官网上的一句话:通过使用自定义事件可以在自定义的组件中创建v-modle(注意Input中I大写,是因为这个是自定义的一个包装input标签的组件)

<Input v-model="searchText">

等价于,也就是说当使用v-model的时候默认会调用父级组件中的一个用input触发的事件,这个事件就是为了给当前的prop进行重新赋值。

<Input
  v-bind:value="searchText"
  v-on:input="searchText = $event.target.value"
>

如果想顺利的使用v-model还需要对组件进行以下的处理

Vue.component('Input', {
  props: ['value'],
  template: `
    <input
      v-bind:value="value"
      v-on:input="$emit('input', $event.target.value)"
    >
  `
})

详情

双向数据传递

概念:父子组件之间可以相互修改数据

应用:弹窗、表单

实现:v-model,.sync,model

我们先从概念入手,什么是相互修改,我们都知道的是,父子沟通用的是props,子父沟通用的是$emit,那双向传递就显而易见了,就是在父组件使用props传值给子组件,子组件再通过$emit可以调整父组件。

三种实现的差异

v-modle是最特殊的一个,他其实是model和.sync的特殊情况,有代码为证

model

<style>
  input.i911-model-input {
    color: #f747ff;
    border-radius: 4px;
    border: 3px solid #fa4dde;
  }
</style>

<template>
  <div>
    <input
      class="i911-model-input"
      :value="modelInput"
      @click="handleInput"
    />
  </div>
</template>

<script>
  export default {
    name: "ModelInput",
    model:{
      prop:'modelInput',//为了和v-model默认的value区分,将prop改为自定义的名称
      event:'click'//为了和v-model默认的input区分,将event改为click
    },
    props: {
      modelInput: {
        type: String,
        default: 'modelInput'
      }
    },
    methods:{
      handleInput(event){
        this.$emit('click',event.target.value)//自定义组件中使用v-model都需要通过$emit触发
      }
    }
  }
</script>

sync

<style>
  input.i911-sync{
    color: #801a27;
    border-radius: 4px;
    border: 3px solid #ff375a;
  }
</style>
<template>
  <main>
    <input class="i911-sync"
           :value="msg"
           @input="handleInput"
    >
  </main>
</template>

<script>
  export default {
    name: "SyncInput",
    props: {
      msg: {
        type: String,
        default: ''
      }
    },
    methods:{
      handleInput(event){
        this.$emit('update:msg',event.target.value)
      }
    }
  }
</script>

v-model

<style>
  input.i911-v-model{
    color:green;
    border-radius: 4px;
    border: 3px solid #36ff7a;
  }
</style>
<template>
  <main>
    <input class="i911-v-model"
      :value="value"
      @input="handleInput"
    >
  </main>
</template>

<script>
  export default {
    name: "VModel",
    props: {
      value: {
        type: String,
        default: ''
      }
    },
    methods:{
      handleInput(event){
        this.$emit('input',event.target.value)//这里$emit调用的是默认的input
      }
    }
  }
</script>

子组件贴完了该我们的父组件了

<style>
input.temp-normal-input{
  color: #1ab0ff;
  border: 3px solid #faf847;
}
  input{
    float: left;
  }
</style>

<template>
  <div>
    TEMP
    //直接通过v-model绑定,通过input触发
    V-MODEL-INPUT:<VMolde v-model="value"></VMolde>
    //这里使用了默认input标签的v-model,因此不需要绑定@input='handleInput',便可以对value修改
    V-MODEL-INPUT-PARENT:<input v-model="value">

    //通过:msg.sync绑定,它实质上是@update:msg='val=>valueSync=val',的语法糖,因此可不用绑定
    SYNC-INPUT:<SyncInput :msg.sync="valueSync"></SyncInput>
    SYNC-INPUT-PARENT:<input v-model="valueSync"><br>

    //直接通过v-model绑定,通过input触发
    NORMAL-INPUT-1:<input class="temp-normal-input" v-model="normalValue"/><br>
    NORMAL-INPUT-2:<input v-model="normalValue"/><br>

    MODEL-INPUT:<model-input v-model="modelValue"></model-input>
    MODEL-INPUT-parent:<input v-model="modelValue"/>
  </div>
</template>

<script>
  import VMolde from '@/components/VMolde'
  import SyncInput from '@/components/SyncInput'
  import ModelInput from '@/components/ModelInput'

  export default {
    name: "temp",
    data() {
      return {
        value:'value',
        valueSync:'valueSync',
        normalValue:'normalValue',
        modelValue:'modelValue'
      }
    },
    methods: {
      handleSyncInput(event){
        this.valueSync=event.target.value;
      }
    },
    components: {
      VMolde,
      SyncInput,
      ModelInput
    }
  }
</script>

对比

model&.sync

双向数据绑定

后续总结,可以先看双向数据绑定原理

双向数据传递与双向数据绑定的区别详解

有个问题可能会比较疑惑,v-model不是用来双向数据绑定的吗,为什么也可以用来做双向数据传递,这是什么鬼。但是如果把默认的标签也想成一个组件的话这个问题就迎刃而解了,当使用input标签时,input的内部也有一段类似于上文中自定义Input的代码。

现在我们具体分析,首先我们来分析下在父组件中修改一个被v-model绑定的input标签(也就是子组件)时候发生了什么?

1.双向数据传递(子组件传递给父组件)

核心部分也就是通过$emit调用父组件的input方法去改变输入的value,这一步是干嘛呢?其实就是将子组件的value通过$emit传递给了父级组件,这部分属于数据传递中子组件传递给父组件;

2.双向数据绑定(dom修改value)

而调用的input方法中的对value进行的修改,这个时候就是双向数据绑定中的dom去修改value

反之如果通过父组件直接修改数据使得子组件发生变化这经历了下面的过程

 

3.双向数据传递(父组件传递给子组件)

当父组件的数据发生变化后,父组件通过prop将数据传递给了子组件

4.双向数据绑定(value修改value)

子组件数据变化后要去动态的修改dom,这个时候就是用到了Object.defineProperty()中的set,监听其数据变化,并且修改dom,vue具体dom修改方式还没有看,不过可以理解为通过jquery或者原生js进行修改

 

 

 

 

 

 

 

 

 

 

 

Logo

前往低代码交流专区

更多推荐