2
写文章不容易,点个赞呗兄弟
专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧
研究基于 Vue版本 【2.5.17】

如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧

【Vue原理】Event - 源码版 之 自定义事件

Vue 的自定义事件很简单,就是使用 观察者模式 进行事件的监听和分发

Vue 封装的这个观察者模式,可以说是很完善了,这个可以独立抽取出来的在其他项目中使用的代码,只需要做一点点改动,把事件存储器换个地方(Vue 放在了实例上)

我经常在项目中使用,就是为了解耦或者解决一些异步的问题

今天来详细探索 Vue 的 自定义事件

首先,Vue 的事件存储器放在那里?

没错,放在 vm._events 中

你看,比如你这样监听事件

公众号

看到实例上保存了你的事件
公众号


1、事件存储器

vm._events

看下这个事件存储器在哪里生成的

首先,实例在初始化的时候,给实例增加一个事件存储器 _events

Vue.prototype._init = function(options) {

    initEvents(vm);    

    //...初始化选项数据,解析模板,挂载dom等

}

function initEvents(vm) {
    vm._events = Object.create(null);
}

以后,所有这个实例监听的事件,就都存在这里了

那么,接下来就来看 自定义事件的源码了

下面的源码比较不太属于 Vue 的内容,比较独立,很实用,相信大家也都看得懂,这里主要起一个记录的作用

下面会有四个函数

绑定事件,$on

一次性绑定事件,$once

触发事件,$emit

解绑事件,$off


2、$on

注册事件,接收 事件名和回调,很清楚了,都能看得懂

Vue.prototype.$on = function(event, fn) {    

    var vm = this;   

    if (Array.isArray(event)) {        

        for (var i = 0,l = event.length; i < l; i++) {            
            this.$on(event[i], fn);
        }
    } 
    else { 
        (vm._events[event] || (vm._events[event] = [])).push(fn);

    }    

    // 为了链式调用
    return vm
};

3、$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
};

4、$emit

触发事件,接收事件名,然后拿到原本设置的回调,遍历调用

Vue.prototype.$emit = function(event) {  

    var vm = this;    

    var _events= event.toLowerCase();    

    var cbs = vm._events[_events]; 

    if (cbs) {

        cbs = cbs.length > 1 ? toArray(cbs) : cbs;        
        var args = toArray(arguments, 1);        

        for (var i = 0, l = cbs.length; i < l; i++) {

          cbs[i].apply(vm, args);
        }

    }    

    // 为了链式调用
    return vm
};

5、$off

取消监听事件或者移除监听回调

接收事件名 和 绑定时的事件回调

很简单的啦

Vue.prototype.$off = function(event, fn) {   

    var vm = this;    

    if (!arguments.length) {

        vm._events = Object.create(null);        

        return vm
    }    

    // 递归调用
    if (Array.isArray(event)) {        

        for (var i = 0, l = event.length; i < l; i++) { 
           this.$off(event[i], fn);
        }        
        return vm
    } 

    var cbs = vm._events[event];    

    if (!cbs) return vm    

    if (!fn) {

        vm._events[event] = null;        

        return vm

    }    

    // 去掉特定的函数
    if (fn) {        

        var cb;        

        var len = cbs.length;     

        // 遍历移除相应回调
        while (len--) {

            cb = cbs[len];            

            if (cb === fn || cb.fn === fn) {

                cbs.splice(len, 1);                

                break

            }
        }
    }    

    // 为了链式调用
    return vm
};

公众号


神仙朱
235 声望105 粉丝

不会认输