因为面试中被问到了once的实现,所以特地来学习下
- $on:监听事件
- $off:移除监听事件
- $emit:触发事件
- $once:监听事件,只监听一次
原理
- vue示例上会创建一个对象来保存所有要监听的事件: vm._events = {};
- 当需要监听一个事件,就会忘vm._events里添加一个键值对,事件的名称为键,一个空数组为值。
- 监听事件的回调函数都会添加到对应的数组中,例如调用
vm.$on('event1', cb1);
vm.$on('event1', cb2);
vm.$on('event1', cb3);
// vm._events = { event1: [cb1, cb2, cb3] }
- 当调用一处监听函数是,就需要移除其对应函数组中的回调函数
vm.$off('event1', cb1);
// vm._events = { event1: [cb2, cb3] }
- 当执行
$emit触发事件时,所有的操作就是吧该事件对应数组里的回调函数都拿出来执行一遍
vm.$emit('event1');
// 此时cb2 和 cb3会执行
$once表示该事件只会执行一次,后续在触发就没用了
vm.$on('event2', cb4);
vm.$once('envent2', cb5);
vm.$emit('event2'); // cb4 和 cb5 会执行
vm.$emit('event2'); // cb4会执行,cb5不会执行
这些方法还有一些稍微负责一点的使用方式。
$on(['event1', 'event2'], cb) // 监听多个事件,触发同一个回调
$off(['event1', 'event2'], cb) // 移除多个事件
$off() // 移除事件
$off('event1') // 监听多个事件,触发同一个回调
$emit('event1', params1, parmas2) // 监听多个事件,触发同一个回调
$on
Vue.prototype.$on = function(event, fn) {
const vm = this
if(Array.isArray(event)) {
for(let i = 0,l = enent.length; i < l; i++) {
vm.$on(event[i], fn);
}
else {
(vm._events[event] || (vm._events[event] = [])).push(fn);
}
}
}
$off
Vue.prototype.$off = function(event, fn) {
const vm = this;
// 移除所有事件
if(!arguments.length) {
vm._events = Object.create(null);
return vm;
}
// 如果传入的event是数组,则对该数组里的每个事件在递归调用$off方法
if(Array,isArray(event)) {
for(let i = 0,l = enent.length; i < l; i++) {
vm.$off(event[i], fn);
}
return vm;
}
const cbs = vm._events[event];
// 如果不存在回调函数,则直接返回,无需其他操作
if(!cbs) {
return vm;
}
// 如果没有指定回调函数,则移除该事件下的所有回调函数
if(!fn) {
vm._events[event] = null
return vm
}
let cb;
let i = cbs.length;
while(i--) {
cb = cbs[i];
if(cb === fn || cb.fn === fn) {
cbs.splice(i, 1)
break
}
}
return vm;
}
$emit
Vue.prototype.$off = fucntion(event) {
const vm = this;
let cbs = vm._envents[event]
if(cbs) {
const args = arguments.slicce(1);
for(let i = 0, l = cbs.length; i < l; i++) {
cbs[i].apply(vm, args)
}
}
return vm
}
once
Vue.prototype.$once = function(event, fn) {
let vm = this;
function on() {
// 函数执行一次之后就移除
vm.$off(event, on)
// 执行on时,执行fn函数
fn.apply(vm, arguments)
}
// $off中通过 on.fn === fn来移除事件
on.fn = fn
vm.$on(event, on)
retrn vm
}