Vue 事件的高級使用方法
事件方法
在Vue中提供了4中事件監聽方法,分別是:
- $on(event: string | Array, fn)
- $emit(event: string)
- $once(event: string, fn)
- $off(event?: string|Array, fn?)
1. $on
監聽當前實例上的自定義事件。事件可以由 vm.$emit 觸發。回調函數會接收所有傳入事件觸發函數的額外參數。
從上述傳參可以看出。第一個參數可以傳遞一個字符串,或者一個數組,如果傳遞的是數組,則會對數組中的每一個事件進行監聽,只要有一個觸發,就會執行后面的回調函數
mounted () { this.$on('event1', () => { console.log('ok'); }); this.$on(['event1', 'event2', 'event3'], () => { console.log('ok'); }); setTimeout(() => { this.$emit('event1'); }, 1000); }
當調用 $emit
之后,兩個 $on
都會被觸發
2. $emit
觸發當前實例上的事件。附加參數都會傳給監聽器回調。
此處的用法非常簡單,可以和 $on
配合使用,也可以和組件中的自定義事件配合使用
Vue.component('welcome-button', { template: ` <button v-on:click="$emit('welcome')"> Click me to be welcomed </button> ` }); <div id="emit-example-simple"> <welcome-button v-on:welcome="sayHi"></welcome-button> </div> methods: { sayHi: function () { alert('Hi!') } }
3. $once
監聽一個自定義事件,但是只觸發一次。一旦觸發之后,監聽器就會被移除。
4. $off
移除自定義事件監聽器
- 如果沒有提供參數,則移除所有的事件監聽器;
- 如果只提供了事件,則移除該事件所有的監聽器;
- 如果同時提供了事件與回調,則只移除這個回調的監聽器。
高級用法
1. 使用$on $emit 實現跨組件通信
有時兩個組件隔的比較遠的情況下,可以通過這兩個組件的共同父元素,或者跟節點來派發和監聽事件,達到通信的效果。
// components A <div> <button @click="clickHandler">click me</button> </div> export default { name: 'componentsA', method: { clickHandler() { this.$root.$emit('my-events'); } } } // components B export default { name: 'componentsB', mounted () { this.$root.$on('my-events', () => { console.log('ok'); }); } } // 這里的$root可以換成共同的父元素
2. hookEvents
父組件可以直接通過自定義事件的形式監聽子組件中聲明周期鈎子的變化,固定寫法 <componentsA @hook:update="func" />
, 目的是當使用了第三方組件,還想要知道里面的生命周期觸發了。可以使用此方法監聽
// child Events export default { name: 'Events2', data () { return { counter: 0 }; }, mounted () { setTimeout(() => { this.counter += 1; }, 2000); }, updated () { console.log('update'); } }; // parent components <Event2 @hook:updated="updateCounter"/> export default { components: { Events2 }, methods: { updateCounter () { console.log('hooks ok'); } } }
源碼分析
- 定義事件源碼位置:
vue/src/core/instance/events.js
export function eventsMixin (Vue: Class<Component>) { const hookRE = /^hook:/ // hookEvent Vue.prototype.$on = function (event: string | Array<string>, fn: Function): Component { // 除了可以使用字符串,還可以監聽一個數組,['event1', 'event2'] const vm: Component = this if (Array.isArray(event)) { for (let i = 0, l = event.length; i < l; i++) { vm.$on(event[i], fn) } } else { // 把事件名稱和回調函數存入vm._events中 (vm._events[event] || (vm._events[event] = [])).push(fn) // 一個事件可以對應多個回調函數 // optimize hook:event cost by using a boolean flag marked at registration // instead of a hash lookup if (hookRE.test(event)) { // 監聽生命周期的鈎子事件 vm._hasHookEvent = true // 只要有人監聽這個事件。在callHooks中就是會派發一個事件 } } return vm } Vue.prototype.$once = function (event: string, fn: Function): Component { const vm: Component = this function on () { vm.$off(event, on) // 執行一次回調函數后就立刻結束 fn.apply(vm, arguments) } on.fn = fn vm.$on(event, on) return vm } Vue.prototype.$off = function (event?: string | Array<string>, fn?: Function): Component { const vm: Component = this // all if (!arguments.length) { // 無參數,清除所有的事件監聽 vm._events = Object.create(null) return vm } // array of events if (Array.isArray(event)) { // 傳入的數組,把相關的事件移除 for (let i = 0, l = event.length; i < l; i++) { vm.$off(event[i], fn) } return vm } // specific event 解除特定事件 const cbs = vm._events[event] if (!cbs) { return vm } if (!fn) { // 如果用戶沒指定fn參數,相關的所有回調都清除 vm._events[event] = null return vm } // specific handler // 如果用戶指定fn參數,只移除對應的回調 let cb let i = cbs.length while (i--) { cb = cbs[i] if (cb === fn || cb.fn === fn) { cbs.splice(i, 1) break } } return vm } Vue.prototype.$emit = function (event: string): Component { // 事件派發 const vm: Component = this let cbs = vm._events[event] if (cbs) { cbs = cbs.length > 1 ? toArray(cbs) : cbs const args = toArray(arguments, 1) const info = `event handler for "${event}"` for (let i = 0, l = cbs.length; i < l; i++) { invokeWithErrorHandling(cbs[i], vm, args, vm, info) } } return vm } }
- 派發hookEvents位置:
vue/src/core/instance/lifecycle.js
export function callHook (vm: Component, hook: string) { if (vm._hasHookEvent) { // 如果標記了鈎子事件,則額外的派發一個自定義的事件出去 vm.$emit('hook:' + hook) // 比如:@hook:update="xxx" } }
測試代碼地址:https://github.com/Shenjieping/vue-events