写文章不容易,点个赞呗兄弟
专注 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};