当前位置: 首页 > 后端技术 > Node.js

结合源码彻底理解react事件机制的原理04-事件执行

时间:2023-04-03 13:32:50 Node.js

前言这是react事件机制的第四篇-事件执行。下面我们一起来研究一下这个过程中经过了哪些关键步骤。这篇文章也是react事件机制的完结希望这篇文章能让大家对react事件执行的原理有一定的了解。本文涉及的源码基于react15.6.1版本,虽然不是最新版本,但不影响我们对react事件机制的整体把握和理解。回顾先简单回顾一下上一篇文章,事件注册的结果是将所有的事件回调保存在一个对象中,那么在事件触发的过程中,上面的对象有什么用呢?其实就是用来找事件回调的。内容概要根据我的理解,事件触发流程总结为以下主要步骤1.进入统一的事件分发函数(dispatchEvent)2.结合原生事件找到当前节点对应的ReactDOMComponent对象3.合成事件3.1根据当前事件按类型生成指定的合成对象3.2封装原生事件和冒泡机制3.3查找当前节点及其所有父节点3.4在listenerBank中查找事件回调并合成为事件(合成事件结束)4.回调的批量处理合成事件中的事件(事件触发完成结束)最好有图举个栗子。在说具体过程之前,我们先来看一个栗子。后面的分析也是基于这个栗子。handleFatherClick=(e)=>{console.log('父亲点击');}handleChildClick=(e)=>{console.log('子点击');}render(){return父亲child

}看到这段熟悉的代码,我们已经知道了执行结果。当我点击子div时,父亲的事件会同时被触发。1、进入统一事件分发函数(dispatchEvent)当我点击子div时,浏览器会在此时捕获事件,然后冒泡后,将事件冒泡到文档中,交给统一事件处理函数dispatchEvent用于处理处理。(在上一篇文章中,我们说过文档上已经注册了一个统一的事件处理函数dispatchEvent)2、结合原生事件找到当前节点对应的ReactDOMComponent对象。对应的ReactDOMComponent实例已经在native事件对象中保留,应该是在挂载阶段已经保存了ReactDOMComponent实例的内容。3.开始事件的合成。冒泡的处理和事件回调的查找都在合成阶段完成。3.1根据当前事件类型找到对应的合成类,然后生成合成对象//进行事件合成,根据事件类型获取指定的合成类varSimpleEventPlugin={eventTypes:eventTypes,extractEvents:functionextractEvents(topLevelType,targetInst,nativeEvent,nativeEventTarget){vardispatchConfig=topLevelEventsToDispatchConfig[topLevelType];//代码已被省略....varEventConstructor;switch(topLevelType){//代码已经省略了....case'topClick'://【这里有个迷惑的地方】topLevelType=topClick,执行到这里,但是这里没有操作if(nativeEvent.按钮===2){返回null;}//代码已经省略....case'topContextMenu'://and会在这里执行得到鼠标合成类EventConstructor=SyntheticMouseEvent;休息;case'topAnimationEnd':case'topAnimationIteration':case'topAnimationStart':EventConstructor=SyntheticAnimationEvent;//动画类合成事件中断;案例'topWheel':EventConstructor=SyntheticWheelEvent;//鼠标滚轮合成事件中断;case'topCopy':case'topCut':case'topPaste':EventConstructor=SyntheticClipboardEvent;休息;}varevent=EventConstructor.getPooled(dispatchConfig,targetInst,nativeTarget,nativeEvent);EventPropagators.accumulateTwoPhaseDispatches(事件);returnevent;//最终会返回合成的事件对象}3.2封装原生事件和冒泡机制这一步会将原生事件对象附加到合成对象本身,并添加事件的默认行为处理和冒泡机制/****@param{obj}dispatchConfig配置对象包含当前事件依赖["topClick"],冒泡捕获事件对应的名称bubbled:"onClick",captured:"onClickCapture"*@param{obj}targetInst组件实例ReactDomComponent*@param{obj}nativeEvent原生事件对象*@param{obj}nativeEventTarget事件源e.target=div.child*/functionSyntheticEvent(dispatchConfig,targetInst,nativeEvent,nativeEventTarget){this.dispatchConfig=调度配置;this._targetInst=targetInst;这个.nativeEvent=nativeEvent;//保存native对象到this.nativeEvent//这里代码省略.....vardefaultPrevented=nativeEvent.defaultPrevented!=null?nativeEvent.defaultPrevented:nativeEvent.returnValue===false;//处理事件if(defaultPrevented){this.isDefaultPrevented=emptyFunction.thatReturnsTrue;的默认行为}else{this.isDefaultPrevented=emptyFunction.thatReturnsFalse;}//处理事件冒泡,thatReturnsFalse默认返回false,即不阻止冒泡this.isPropagationStopped=emptyFunction.thatReturnsFalse;returnthis;}下面是添加的默认行为和冒泡机制的处理方法。其实就是改变当前合成对象的属性值。调用方法后,属性值为true,会防止默认行为或冒泡我们看代码//在合成类的原型中添加preventDefault和stopPropagation方法_assign(SyntheticEvent.prototype,{preventDefault:functionpreventDefault(){//....略this.isDefaultPrevented=emptyFunction.thatReturnsTrue;},stopPropagation:functionstopPropagation(){//....省略this.isPropagationStopped=emptyFunction.thatReturnsTrue;});看emptyFunction的代码就明白了3.3根据当前节点实例找到其所有父实例存放在path/****@param{obj}inst当前节点实例*@param{function}fn处理方法*@param{obj}arg合成事件对象*/functiontraverseTwoPhase(inst,fn,arg){varpath=[];//存储所有ExampleReactDOMComponentwhile(inst){path.push(inst);inst=inst._hostParent;//层次关系}vari;for(i=path.length;i-->0;){fn(path[i],'captured',arg);//过程捕获,逆向处理数组}for(i=0;i