当前位置: 首页 > Web前端 > vue.js

Vue事件源码分析

时间:2023-04-01 12:04:03 vue.js

1.vue.js执行时,会调用eventsMixin方法,为Vue原型添加事件相关的方法,组件(Vue实例)可以调用这些事件方法。调用$on方法在组件标签上添加自定义事件。该事件保存在组件的_events对象中。对象属性名是事件名,属性值是一组事件回调函数。调用$emit方法触发保存的自定义事件,找到根据事件名称设置的事件回调函数,然后依次调用该事件的回调函数,在$emit方法中第一个参数之后传入所有参数.调用$off以删除自定义事件。如果传入事件名称和回调函数两个参数,则移除事件中的回调函数。如果只传递事件名称,则该事件的所有回调函数都会被移除。调用$once方法在组件标签上添加自定义事件,触发一次后销毁。该方法中调用$on方法添加事件,同时封装事件的回调函数。当事件的回调函数被触发时,先调用$off去除回调函数,然后再执行原来的回调函数。functioneventsMixin(Vue){varhookRE=/^hook:/;Vue.prototype.$on=function(event,fn){varvm=this;如果(Array.isArray(event)){for(vari=0,l=event.length;i1?toArray(cbs):cbs;varargs=toArray(arguments,1);varinfo='事件handlerfor"'+event+'"';对于(vari=0,l=cbs.length;iTest事件编译成{on:{"test":test}}对象test被编译成{on:{"click":function($event){$event.stopPropagation();returntest($event)}}}object编译成{on:{"test":test}}对象编译事件成{"test":function($event){returntest(1)}}对象被编译成{nativeOn:{"click":function($event){returntest($event)}}对象组件option中的方法方法会被重新封装,在方法中保持this指向组件(Vue实例),即事件回调函数this指向所在组件。3、根据标签创建节点(VNode实例)时,将标签上的原生事件编译成数据对象,存储在节点的data属性上。组件标签上的自定义事件编译的数据对象存储在节点组件数据中的listerners属性中。原生事件是指原生标签上的事件或组件标签上添加了原生固定器的事件。//创建组件节点functioncreateComponent(Ctor,data,context,children,tag){if(isUndef(Ctor)){return}varbaseCtor=context.$options._base;//普通选项对象:将其转换为构造函数if(isObject(Ctor)){Ctor=baseCtor.extend(Ctor);}...varlisteners=data.on;//组件标签上的自定义事件data.on=data.nativeOn;//组件标签上的nativeevent...//返回占位符vnodevarname=Ctor.options.名称||标签;varvnode=newVNode(("vue-component-"+(Ctor.cid)+(name?("-"+name):'')),data,undefined,undefined,undefined,context,////data保存原生事件信息{Ctor:Ctor,propsData:propsData,listeners:listeners,tag:tag,children:children},//listeners保存组件标签上的自定义事件信息asyncFactory);returnvnode}3.调用updateComponentListeners来创建或更新组件标签上的自定义事件。调用组件的$on方法添加事件回调函数。添加前会封装回调函数,将原来的回调函数保存到封装函数的属性fns中,在封装的回调函数中调用fns。当事件的回调函数发生变化时,原生事件不应重新添加事件监听器,只需更新函数上的fns属性即可。vartarget;functionadd(event,fn){//添加组件绑定的事件标签target.$on(event,fn);}functionremove$1(event,fn){//移除组件绑定的事件标签上的事件target.$off(event,fn);}functioncreateOnceHandler(event,fn){//添加组件标签绑定的事件,事件触发后移除var_target=target;返回函数onceHandler(){varres=fn.apply(null,arguments);如果(res!==null){_target.$off(event,onceHandler);}};}functionupdateComponentListeners(vm,listeners,oldListeners){target=vm;updateListeners(listeners,oldListeners||{},add,remove$1,createOnceHandler,vm);//创建或更新自定义事件target=undefined;}functionupdateListeners(on,oldOn,add,remove$$1,createOnceHandler,vm){varname,def$$1,cur,old,event;for(nameinon){def$$1=cur=on[name];旧=oldOn[名称];事件=normalizeEvent(名称);//获取事件修饰符if(isUndef(cur)){process.env.NODE_ENV!=='production'&&警告('事件“'+event.name+'”的无效处理程序:得到'+String(cur),vm);}elseif(isUndef(old)){if(isUndef(cur.fns)){cur=on[name]=createFnInvoker(cur,vm);//封装回调函数:将原回调函数绑定到封装回调函数的fn属性上}if(isTrue(event.once)){cur=on[name]=createOnceHandler(event.name,cur,event.capture);}添加(事件名称,电流,事件捕获,事件被动,事件参数);//添加事件回调函数}elseif(cur!==old){old.fns=cur;//替换原来的回调函数on[name]=old;//替换为封装的回调函数}}for(nameinoldOn){if(isUndef(on[name])){event=normalizeEvent(name);删除$$1(事件名称,oldOn[名称],事件捕获);}}}//包装函数,把原来的函数保存在包装函数的fns属性上。改变原有功能时,只需修改fns属性即可。functioncreateFnInvoker(fns,vm){functioninvoker(){vararguments$1=arguments;varfns=invoker.fns;if(Array.isArray(fns)){varcloned=fns.slice();对于(vari=0;i=attachedTimestamp||e.timeStamp<=0||e.target.ownerDocument!==文件){returnoriginal.apply(this,arguments);}};}target$1.addEventListener(name,handler,supportsPassive?{capture:capture,passive:passive}:capture);}//移去原生DOM上的事件functionremove$2(name,handler,capture,_target){(_target||target$1).removeEventListener(name,handler._wrapper||handler,capture);}//更新原生事件函数updateDOMListeners(oldVnode,vnode){...varon=vnode.data.在||{};//新标签上的原生事件varoldOn=oldVnode.data.on||{};//标签上的原生事件target$1=vnode.elm;//标签对应的DOM元素的normalizeEvents(on);updateListeners(on,oldOn,add$1,remove$2,createOnceHandler$1,vnode.context);目标$1=未定义;}