这段时间一直在看Vue的源码。源码很复杂,所以结合资料和理解想出了一条主线,然后根据主线剥离出一些其他的知识点。然后一个一个的学习每个知识点。这里主要是分析事件系统的实现。正文1.了解使用方法在分析几个API的使用方法之前:vm.$on(event,callback)parameter:{string|Array}事件(array只在2.2.0+支持){Function}回调用法:$on事件需要两个参数,一个是当前被监听实例上的事件名,一个是回调函数由事件触发。回调函数接受事件触发时传递的附加参数。示例:vm.$on('test',function(msg){console.log(msg)})vm.$emit('test','hi')//=>"hi"vm.$once(event,callback)$once事件一般和$on事件一样使用,但是事件只支持字符串,也就是只支持单个事件。并在事件再次触发后移除监听器。示例vm.$once('testonce',function(msg){console.log(msg)})vm.$off([event,callback])参数:{string|Array}事件(仅在2.2.2+支持数组){Function}[callback]用法:移除自定义事件监听器如果没有提供参数,则移除所有事件监听器如果只提供事件,则移除所有事件监听器;if如果同时提供了一个事件和一个回调,则只移除这个回调的监听器。示例:vm.$off()vm.$off('test')vm.$off('test1',function(msg){console.log(msg)})vm.$off(['test1','test2'],function(msg){console.log(msg)})vm.$emit(event,[..args])parameter:{string}event要触发的事件名称[...args]可选用法:在当前实例上触发事件。附加参数传递给侦听器回调。示例vm.$emit('test','Triggercustomevent')2.源码分析事件的初始化当我们使用自定义事件的API时,肯定有一个地方可以存放我们的事件和回调的地方。vue中大部分的初始化工作都在core/instance/init.js的initMixin方法中。所以我们可以在initMixin中看到initEvents方法。//initEventsexportfunctioninitEvents(vm:Component){vm._events=Object.create(null)vm._hasHookEvent=false//初始化父附加事件constlisteners=vm.$options._parentListenersif(listeners){updateComponentListeners(vm,listeners)}}上面代码可以看到,在初始化vue事件的时候,vm实例上挂载了一个_events的空对象。我们后面自称的自定义事件都存在于其中。因为vue本身有子组件,在组件嵌套的时候使用父组件事件。所以可以通过updateComponentListeners方法将父组件的事件监听器(比如点击)传递给子组件。(这里不过多讨论)自定义事件的挂载方法自定义事件的挂载是在eventsMixin方法中实现的。在这里,四个方法挂在了Vue的原型上。Vue.prototype.$onVue.prototype.$onceVue.prototype.$offVue.prototype.$emitVue.prototype.$onVue.prototype.$on=function的实现(event:string|Array,fn:Function):Component{constvm:Component=thisif(Array.isArray(event)){for(leti=0,l=event.length;i,fn?:Function):Component{constvm:Component=this//all//如果不传参数,直接清除所有挂在_events对象上的事件。if(!arguments.length){vm._events=Object.create(null)returnvm}//事件数组//如果第一个参数是数组,则在循环中调用this.$off方法if(Array.isArray(event)){for(leti=0,l=event.length;i1?toArray(cbs):cbsconstargs=toArray(arguments,1)//循环遍历调用所有自定义事件。for(leti=0,l=cbs.length;i