将原生事件绑定到组件的阅读笔记

1,740 阅读2分钟

👌父组件想要在自定义组件的根元素上直接监听一个原生事件。这时,在 v-on 原生事件后使用 .native 修饰符是有用的

  • 父组件:
<template>
  <div id="app">
    <BaseInput1 @focus.native="onFocus" v-model="value"/>
  </div>
</template>
<script>
import BaseInput1 from './components/BaseInput1.vue'
export default {
  name: 'App',
  data(){
    return {
      value: ''
    },
  components: {
    BaseInput1
  },
  methods:{
    onFocus(){
      alert('onFocus')
    }
  }
}
</script>
  • 子组件
<template>
    <input
      v-bind="$attrs"
      v-bind:value="value"
      v-on:input="$emit('input', $event.target.value)"
    >
</template>
<script>
export default {
  name:"",
  inheritAttrs: false,
  props: {
    value: String,
  }
}
</script>
<style lang="less">
</style>

😢但父组件要想在自定义组件的非根元素上监听一个原生事件。这时,父级的 .native 监听器将静默失败。并且不会产生任何报错。例如:在input外包裹label,即根元素实际上是一个 <label> 元素

<label>
    {{label}}
    <input
      v-bind="$attrs"
      v-bind:value="value"
      v-on="inputListeners"
    >
  </label>
前父子组件关系 后父子组件关系
app >> BaseInput1 (input为根元素) app >> BaseInput1 (label为根元素) >> input

🤔解决方案:让 BaseInput1 组件变成一个完全透明的包裹器,也就是让它可以完全像一个普通的 <input> 元素一样使用:让所有跟它相同的 attribute 和监听器都可以工作。 如果你对inheritAttrs&&$attrs&&$listeners不熟悉,推荐你看下面这篇推文🙌: inheritAttrs&&$attrs&&$listeners 【Vue 2.4.0新增inheritAttrs,attrs详解】

  • 父组件:
<template>
  <div id="app">
    <!-- 它可以完全像一个普通的 <input> 元素一样使用,无需再使用.native修饰符了 -->
    <BaseInput1 label="将原生事件绑定到组件" @focus="onFocus" v-model="value"/>
    <h2>{{value}}</h2>
  </div>
</template>
<script>
import BaseInput1 from './components/BaseInput1.vue'
export default {
  name: 'App',
  data(){
    return {
      value: ''
    }
  },
  components: {
    BaseInput1
  },
  methods:{
    onFocus(){
      alert('onFocus')
    }
  }
}
</script>
  • 子组件:
<template>
  <label>
    {{label}}
    <input
      v-bind="$attrs"
      v-bind:value="value"
      v-on="inputListeners"
    >
    <!--使用 v-on="$listeners" 将所有的事件监听器指向这个组件的某个特定的子元素。
    对于类似 <input> 的你希望它也可以配合 v-model 工作的组件来说
    可以为这些监听器创建一个类似inputListeners 的计算属性-->
  </label>
</template>
<script>
export default {
  name:"",
  inheritAttrs: false,
  props: {
    value: String,
    label: String,
  },
  computed: {
    inputListeners: function(){
      var vm = this
      // `Object.assign` 将所有的对象合并为一个新对象
      return Object.assign({},
      // 我们从父级添加所有的监听器
        this.$listeners,
        // 然后我们添加自定义监听器,
        // 或覆写一些监听器的行为
        {
          // 这里确保组件配合 `v-model` 的工作
          input: function(event){
            vm.$emit('input',event.target.value)
          }
        }
      )
    }
  }
}
</script>
<style lang="less">
</style>