vue的 $on,$emit,$off,$once
Api 中的解釋:
-
$on(eventName:string|Array
, callback) 監聽事件 - 監聽當前例項上的自定義事件。事件可以由 vm.$emit 觸發。回撥函式會接收所有傳入事件觸發函式的額外引數。
-
$once(eventName: string, callback) 監聽事件
- 監聽一個自定義事件,但是隻觸發一次。一旦觸發之後,監聽器就會被移除。
-
$off(eventName: string| Array
, callback) 移除自定義事件監聽器 1. 如果沒有提供引數,則移除所有的事件監聽器; 2. 如果只提供了事件,則移除該事件所有的監聽器; 3. 如果同時提供了事件與回撥,則只移除這個回撥的監聽器。
-
使用 $emit(eventName, [..args]) 觸發事件
- 觸發當前例項上的事件。附加引數都會傳給監聽器回撥
$on 實現原理
Vue.prototype.$on = function (event, fn) {
var vm = this;
if (Array.isArray(event)) {
for (var i = 0, l = event.length; i < l; i++) {
vm.$on(event[i], fn);
}
} else {
(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;
}
}
return vm
};
$once 實現原理
Vue.prototype.$once = function (event, fn) {
var vm = this;
function on () {
vm.$off(event, on);
fn.apply(vm, arguments);
}
on.fn = fn;
vm.$on(event, on);
return vm
};
$off
Vue.prototype.$off = function (event, fn) {
var vm = this;
// all
if (!arguments.length) {
vm._events = Object.create(null);
return vm
}
// array of events
if (Array.isArray(event)) {
for (var i$1 = 0, l = event.length; i$1 < l; i$1++) {
vm.$off(event[i$1], fn);
}
return vm
}
// specific event
var cbs = vm._events[event];
if (!cbs) {
return vm
}
if (!fn) {
vm._events[event] = null;
return vm
}
// specific handler
var cb;
var i = cbs.length;
while (i--) {
cb = cbs[i];
if (cb === fn || cb.fn === fn) {
cbs.splice(i, 1);
break
}
}
return vm
};
$emit 實現原理
Vue.prototype.$emit = function (event) {
var vm = this;
{
var lowerCaseEvent = event.toLowerCase();
if (lowerCaseEvent !== event && vm._events[lowerCaseEvent]) {
tip(
"Event \"" + lowerCaseEvent + "\" is emitted in component " +
(formatComponentName(vm)) + " but the handler is registered for \"" + event + "\". " +
"Note that HTML attributes are case-insensitive and you cannot use " +
"v-on to listen to camelCase events when using in-DOM templates. " +
"You should probably use \"" + (hyphenate(event)) + "\" instead of \"" + event + "\"."
);
}
}
var cbs = vm._events[event];
if (cbs) {
cbs = cbs.length > 1 ? toArray(cbs) : cbs;
var args = toArray(arguments, 1);
var info = "event handler for \"" + event + "\"";
for (var i = 0, l = cbs.length; i < l; i++) {
invokeWithErrorHandling(cbs[i], vm, args, vm, info);
}
}
return vm
};
用法:監聽當前例項上的自定義事件。事件可以由 vm.$emit 觸發。回撥函式會接收所有傳入事件觸發函式的額外引數。
應用
- 多次繫結,$on, $once 將多次呼叫,$off 呼叫一次將解綁所有相同名字的$on, $once 監聽事件
<template>
<div class="hello">
<button @click="onEmit">OnEmit</button>
<button @click="onOff">OnOff</button>
<button @click="onOn">on</button>
</div>
</template>
<script>
export default {
name: 'Test',
mounted() {
},
methods: {
onEmit() {
this.$emit('onTest', 1);
this.$emit('onTest', 2)
this.$emit('onTest', 3)
this.$emit('onTest', 4)
this.$emit('onTestOnce', 5);
this.$emit('onTestOnce', 6);
this.$emit('onTestOnce', 7);
this.$emit('onTestOnce', 8);
},
onOff() {
this.$off('onTest');
},
onOn(){
this.$on('onTest', (args) => {
console.log(args)
})
this.$once('onTestOnce', (args) => {
console.log(args)
})
}
}
}
</script>
子元件到父元件通訊
<hello @pfn="parentFn"></hello>
<script>
Vue.component('hello', {
template: '<button @click="fn">按鈕</button>',
methods: {
// 子元件:通過$emit呼叫
fn() {
this.$emit('pfn', '這是子元件傳遞給父元件的資料')
}
}
})
new Vue({
methods: {
// 父元件:提供方法
parentFn(data) {
console.log('父元件:', data)
}
}
})
</script>
非父子元件通訊
- 元件 A.vue
<template>
<div @click="onAon">
元件a
</div>
</template>
<script>
// import bus from "@/components/bus";
export default {
name: 'A',
methods: {
onAon() {
this.$bus.$emit('bTest')
}
}
}
</script>
元件B.vue
<template>
<div>
b 元件
</div>
</template>
<script>
// import bus from "@/components/bus";
export default {
mounted() {
this.$bus.$on('bTest', () => {
console.log('---- 觸發b元件監聽 ------')
})
}
}
</script>
- bus.js
import Vue from "vue";
let bus = new Vue();
export default bus;
注:可以將bus 全域性引入