本文同步發佈於:勇者鬥 Vue 龍
本文為 2019 鐵人賽 續篇
目前為止介紹了下面幾種事件監聽的時機:
v-on
監聽原生事件v-on
設定所需要監聽的事件,子組件用 $emit
觸發事件上面的方式都是在組件被定義時就要做的設定,也就是靜態的定義方式,但如果要在執行時去動態增減事件的監聽,這時就需要用到 $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 實體上設定各個 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'));
}
}
on
事件$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();
}
});
mounted
鉤子中建立 Pikaday 實體beforeDestroy
時叫用 Pikaday 的 destroy
方法這樣做有兩個壞處:
為了避免這個問題,我們可以使用 $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#L9
export 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']); // 註銷多個特定名稱的事件監聽器
});
}
});
使用方法註冊事件監聽器使 Vue.js 的事件處理更加的靈活,並且在跟其他第三方庫或是現存專案的整合上更有彈性。