本文同步发布于:勇者斗 Vue 龙
本文为 2019 铁人赛 续篇
目前为止介绍了下面几种事件监听的时机:
元素上使用v-on
监听原生事件父组件设定 v-on
设定所需要监听的事件,子组件用 $emit
触发事件在 Vue 实体上设定生命週期钩子监听各个钩子事件上面的方式都是在组件被定义时就要做的设定,也就是静态的定义方式,但如果要在执行时去动态增减事件的监听,这时就需要用到 $on
, $once
及 $off
这些 JS 函式来做设定。
静态事件监听
开始说明动态方式之前,先来複习一下静态的方式。
元素上使用 v-on
监听原生事件
<div id="app"> <button @click="alert('native')">native</button></div>
var vm = new Vue({ el: '#app'});
简单使用 v-on
注册原生事件,就可以设定事件的监听。
父组件设定 v-on
设定所需要监听的事件,子组件用 $emit
触发事件
如果要使用自定义事件,在子组件中设定 $emit
发送信号表示欲叫用何种事件,这时父组件如果有使用 v-on
注册该事件,就会触发事件。
<div id="app"> <my-button @component-click="alert('component')">component</my-button></div>
Vue.component('my-button', { template: `<button @click="$emit('component-click')"><slot></slot></button>`});var vm = new Vue({ el: '#app'});
在 <my-button>
按下按钮后,送出的 component-click
信号会被 v-on
所注册的 component-click
接收并触发事件。
在 Vue 实体上设定生命週期钩子监听各个钩子事件
钩子事件的触发可以在 Vue 实体上设定各个 hook
的函式。
var vm = new Vue({ el: '#app', mounted: function() { alert('mounted'); }});
设定 mounted
函式,就可以监听 mounted
钩子。
动态注册事件监听器
在整个专案都是使用 Vue.js 构筑时,使用静态注册事件监听器是足够的,但如果专案中 Vue.js 只佔了一部分,要配合其他框架或工具设定监听事件的话,这时就要靠动态注册事件。
下面的例子模拟在其他非 Vue 元素建立后,要去注册对应的事件使 Vue 可以触发。
<div id="app"> <div id="vue-app"> <button @click="$emit('on')">Emit</button> </div> <div id="other-app"> <button onclick="otherAppClick()">Add Event</button> <button onclick="otherAppClick('once')">Add Event ( once )</button> <button onclick="otherAppClick('off')">Off All Event</button> </div></div>
let echoFunc = (echoStr) => () => {alert(echoStr)};var vm = new Vue({ el: '#vue-app'});function otherAppClick(type) { if (type === 'once') { vm.$once('on', echoFunc('once')); } else if (type === 'off') { vm.$off('on'); } else { vm.$on('on', echoFunc('on')); }}
在 Vue 实体中的按钮点击事件触发 on
事件在 Vue 实体外按钮点击中模拟 $on
, $once
及 $off
方法如果没有使用函式注册事件的话,在 Vue 实体以外的事件是不能被 $emit
所触发的。
动态注册钩子方法
在使用第三方库(ex: Pikaday)时,最需要注意的就是在 Vue 实体销毁时也要一併把库所建立的实体销毁,以避免 memory leak 的问题。
一般我们会像下面这样写:
new Vue({ el: '#app', data: { date: null, picker: null }, mounted: function () { this.picker = new Pikaday({ field: this.$refs.dateInput, format: 'YYYY-MM-DD' }); }, beforeDestroy: function() { this.picker.destroy(); }});
使用 picker 当作接收 Pikaday 实体的物件于 mounted
钩子中建立 Pikaday 实体在 beforeDestroy
时叫用 Pikaday 的 destroy
方法这样做有两个坏处:
需要管理 picker 物件,但这物件只是为了销毁 Pickaday 实体使用,跟此 Vue 实体没有关係建立及销毁 Pickaday 实体的代码分离并散落在实体中,在开发时实体上充斥着这些与此实体无关的代码,造成开发上的麻烦为了避免这个问题,我们可以使用 $once
在建立 Pickaday 实体的同时去注册销毁的事件:
new Vue({ el: '#app', data: { date: null, // picker: null }, mounted: function () { // this.picker = const picker = new Pikaday({ field: this.$refs.dateInput, format: 'YYYY-MM-DD' }); this.$once('hook:beforeDestroy', function() { picker.destroy(); }); }, // beforeDestroy: function() { // this.picker.destroy(); // }});
这样不仅可以减少 picker 资料物件,也可以使代码集中至真正处理的地方,以避免模糊焦点。
以下列出全部的钩子事件;
// https://github.com/vuejs/vue/blob/dev/src/shared/constants.js#L9export const LIFECYCLE_HOOKS = [ 'beforeCreate', 'created', 'beforeMount', 'mounted', 'beforeUpdate', 'updated', 'beforeDestroy', 'destroyed', 'activated', 'deactivated', 'errorCaptured', 'serverPrefetch']
事件监听方法
上面说明使用情境,现在来讲解各个方法的使用。
vm.$on( event, callback )
注册事件及其叫用的函式。
<div id="app"> <button @click="$emit('on')">on</button> <button @click="$emit('on2')">on2</button></div>
var vm = new Vue({ el: '#app', mounted: function() { let echoFunc = (echoStr) => () => {alert(echoStr)}; let echoOn = echoFunc('on'); let echoOn2 = echoFunc('on2'); this.$on('on', echoOn); this.$on(['on', 'on2'], echoOn2); }});
使用 $on
注册 on
事件,当触发 on
事件时,会触发 echoOn
方法$on
注册事件支援阵列形式,可以同时对多个事件注册相同的方法,例子中使用 echoOn2
同时注册 on
及 on2
事件vm.$once( event, callback )
注册事件及其叫用的函式,但只触发一次。
<div id="app"> <button @click="$emit('once')">once</button></div>
var vm = new Vue({ el: '#app', mounted: function() { let echoFunc = (echoStr) => () => {alert(echoStr)}; let echoOnce = echoFunc('once'); this.$once('once', echoOnce); }});
使用 $once
监听的事件只会触发一次。
vm.$off( [event, callback] )
删除监听事件。
<div id="app"> <button @click="$emit('once')">once</button> <button @click="$emit('on')">on</button> <button @click="$emit('on2')">on2</button> <button @click="$emit('off-all')">off all</button> <button @click="$emit('off-on')">off on</button> <button @click="$emit('off-echo-on2')">off echo on2</button> <button @click="$emit('off-on-and-once')">off on and once</button></div>
var vm = new Vue({ el: '#app', mounted: function() { let echoFunc = (echoStr) => () => {alert(echoStr)}; let echoOnce = echoFunc('once'); let echoOn = echoFunc('on'); let echoOn2 = echoFunc('on2'); this.$once('once', echoOnce); this.$on('on', echoOn); this.$on(['on', 'on2'], echoOn2); this.$on('off-all', () => { this.$off(); // 注销此实体上所有事件监听器 }); this.$on('off-on', () => { this.$off('on'); // 注销特定名称事件监听器 }); this.$on('off-echo-on2', () => { this.$off('on', echoOn2); // 注销特定名称的特定事件监听器 }); this.$on('off-on-and-once', () => { this.$off(['on', 'once']); // 注销多个特定名称的事件监听器 }); }});
如果没有任何参数,删除此实体上所有事件监听器第一个参数可以指定特定名称的事件,同时可以是阵列以选择多个特定名称的事件第二个参数如果没有指定,会将特定名称的所有事件删除第二个参数如果有指定事件,则将其特定事件删除代码
CodePen: ListenerCodePen: Dynamic ListenerCodePen: Vue pikaday总结
使用方法注册事件监听器使 Vue.js 的事件处理更加的灵活,并且在跟其他第三方库或是现存专案的整合上更有弹性。