Vue.js 教程

Vue.js 组件绑定原生事件

你可能有很多次想要在一个组件的根元素上直接监听一个原生事件。这时,你可以使用 v-on 的 .native 修饰符:

<base-input v-on:focus.native="onFocus"></base-input>

在有的时候这是很有用的,不过在你尝试监听一个类似 <input> 的非常特定的元素时,这并不是个好主意。比如上述 <base-input> 组件可能做了如下重构,所以根元素实际上是一个 <label> 元素:

<label>
   {{ label }}
   <input v-bind="$attrs"
       v-bind:value="value"
       v-on:input="$emit('input', $event.target.value)" />
</label>

这时,父级的 .native 监听器将静默失败。它不会产生任何报错,但是 onFocus 处理函数不会如你预期地被调用。

为了解决这个问题,Vue 提供了一个 $listeners 属性,它是一个对象,里面包含了作用在这个组件上的所有监听器。例如:

{
   focus: function (event) { /* ... */ }
   input: function (value) { /* ... */ },
}

有了这个 $listeners 属性,你就可以配合 v-on="$listeners" 将所有的事件监听器指向这个组件的某个特定的子元素。对于类似 <input> 的你希望它也可以配合 v-model 工作的组件来说,为这些监听器创建一个类似下述 inputListeners 的计算属性通常是非常有用的:

Vue.component('base-input', {
   inheritAttrs: false,
   props: ['label', 'value'],
   computed: {
       inputListeners: function () {
           var vm = this
           // `Object.assign` 将所有的对象合并为一个新对象
           return Object.assign({},
               // 我们从父级添加所有的监听器
               this.$listeners,
               // 然后我们添加自定义监听器,
               // 或覆写一些监听器的行为
               {
                   // 这里确保组件配合 `v-model` 的工作
                   input: function (event) {
                       vm.$emit('input', event.target.value)
                   }
               }
           )
       }
   },
   template: `
       <label>
         {{ label }}
         <input
           v-bind="$attrs"
           v-bind:value="value"
           v-on="inputListeners"
         >
       </label>
   `
})

现在 <base-input> 组件是一个完全透明的包裹器了,也就是说它可以完全像一个普通的 <input> 元素一样使用了:所有跟它相同的 attribute 和监听器都可以工作,不必再使用 .native 监听器。

完整示例

<html>
<head>
   <meta charset="UTF-8">
   <meta http-equiv="X-UA-Compatible" content="IE=edge">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <title>Vue</title>
   <!-- 使用 CDN 引入 Vue 库 -->
   <!-- <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> -->
   <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.7.9/vue.js"></script>
</head>
<body>

   <div id="app">
       <h1>lovingVue = {{ lovingVue }}</h1>
       <base-input v-model="lovingVue"></base-input>
   </div>

   <script type="text/javascript">
       Vue.component('base-input', {
           inheritAttrs: false,
           props: ['label', 'value'],
           computed: {
               inputListeners: function () {
                   var vm = this
                   // `Object.assign` 将所有的对象合并为一个新对象
                   return Object.assign({},
                       // 我们从父级添加所有的监听器
                       this.$listeners,
                       // 然后我们添加自定义监听器,
                       // 或覆写一些监听器的行为
                       {
                           // 这里确保组件配合 `v-model` 的工作
                           input: function (event) {
                               vm.$emit('input', event.target.value)
                           }
                       }
                   )
               }
           },
           template: `
               <label>
                   {{ label }}
                   <input v-bind="$attrs"
                       v-bind:value="value"
                       v-on="inputListeners" >
               </label>
           `
       });

       var app = new Vue({
           el: "#app",
           data: {
               lovingVue: null
           }
       });
   </script>

</body>
</html>

运行效果图:

说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
公众号