$emit 和 $v-on

vue 组件之间数据绑定是基于单项绑定的,想要父子组件之间的数据和行为的交互要使用 $emit$v-on

<!-- child component -->

<template> 
    <dev @click='handleClik'> {{value}} </dev>
</template>
<script>
export default {
    name: 'child',
    props: ['value'],
    methods:{
        handleClik(){
            this.$emit('change', 'value1')
        }
    }
    
}
</script>
<template> 
    <child @change="onChange" :vlaue='val'/>
</template>
<script>
import Child from './child'
export default {
    name: 'child',
    components: {Child},
    methods:{
        onChange(val){
            this.value = val 
        }
    }
    
}
</script>

child 组件内部内部的 value 是根据父级传入的 value 显示的, 如果想要改变 value 的值,在child 组件里面是不行,只能通过 $emit 向父级传入想要改变的值,然后 父级在捕获到 change 时间的时候,拿到 child 组件传来的值, 再次给 value 赋值 this.value = val

这种方式是组件与组件数据和事件交互的惯常用法,最然逻辑是很明了的,但是如果遇到复杂的组件间交互逻辑写起来就会比较繁琐。

v-model

还有一种数据的交互方式是使用 v-model, 这种方法专门是 input 组件, 这只不过是 一种语法糖的形式:
input 组件上面本身有一个 onInput 事件。每当素输入框内容发生发生的时候,就会触发这个事件,然后把 input 的值 通过 $emit 传递出去。

<!-- 下面两个表达 实现同样的效果 -->
<input v-model='val'>
<input :value='val' @input="val = $event.target.value"/>

这里需要注意的是 v-model 默认情况下只会接受 value 属性和响应 input 事件。 因为 v-model 本身是基于 input 框定制的, value 是 input 内部定制的绑定值的属性, input 方法是内部定制的当值改变出发的事件。

了解了 v-model 的语法, 可以根据这种写法自定义 组件的 v-model;
还是使用开头的 child 组件的例子;

<!-- child component -->

<template> 
    <dev @click='handleClik'> {{value}} </dev>
</template>
<script>
export default {
    name: 'child',
    props: ['value'],
    methods:{
        handleClik(){
            this.$emit('input', 'value1')
        }
    }
    
}
</script>
<template> 
    <child  :vlaue='val' @input='val = $event'/>
    <!-- 等同于 -->
     <child v-model='val'/>
</template>
<script>
import Child from './child'
export default {
    name: 'child',
    components: {Child}
}
</script>

定制 v-model

上面说 v-model 只能接受 value 属性和响应 input 事件,其实这种说法并不是绝对的,我们可以通过 model 选项来改变这种情况。

model 接受有两个属性:

  • props 代替原来 value 的值。
  • event 代替原本 input 出发的事件。

子组件

<template>
   <div v-if="show">
      <div>
         <p>定制 v-model</p>
        <button @click='close'>关闭组件</button>
      </div>
   </div>
</template>

<script>
export default {
  model: {
    prop: 'show',
    event: 'close'
  },
  props: ['show'],
  methods: {
    close () {
      this.$emit('close',false)
    }
  }
}
</script>

父组件

<template>
  <div>
    <button @click="show=true">打开model</button>
    <child v-model="show" ></child>
    <!-- 等同于 -->
    <child :show='show' @close="show = $event"></child>
  </div>
</template>

<script>
  import Child from './child'
  export default {
    components: {
      Child
    },
    data () {
      return {
        show: false
      }
    }
  }
</script>

上面的例子通过在子组件内部定义 model 选项来改变 v-model 绑定属性和响应的方法,

  1. 当父组件点击 打开model 按钮的时候,内部的 show 属性变为了 true, 这时因为子组件的显示与隐藏是根据 props 里面的 show 来确定的, 子组件将会显示。
  2. 点击子组件的 关闭组件 按钮, 出发了 close 事件,这个事件中将会像父级暴露一个 close 方法,并传递一个 false 参数。
  3. 父级触发 close 方法,改变 show 的值.

这个过程在使用 $emitv-on 时的逻辑,因为我们自定义了 model 属性, 这时就可以在使用 child 组件的时候就可以使用 v-model 来简写这种写法啦。

Logo

前往低代码交流专区

更多推荐