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

结合源码深入理解react事件机制原理03-事件注册

时间:2023-04-03 15:52:20 Node.js

前言这是react事件机制的第三篇-事件注册。通过本文,你将了解React事件的注册过程,以及这个过程中经过了哪些关键步骤,并结合源码进行验证,增进理解。本文涉及的源码基于react15.6.1版本,虽然不是最新版本,但不影响我们对react事件机制的整体把握和理解。文章不会讲很详细的内容,会介绍大概的流程和原理性的内容,让大家对整个流程有个认知和了解。内容大纲主要做了两件事(事件注册、事件存储)。大致流程和具体执行流程总结1.主要做了两件事按照我的理解,react事件注册流程其实主要做了两件事:a.活动报名B.事件存储事件注册-组件挂载阶段,根据组件中声明的事件类型-onclick、onchange等,在文档中添加一个事件-addEventListener,并指定一个统一的事件处理器dispatchEvent。b.事件存储——就是把react组件中的所有事件都存储在一个地方,也就是缓存起来,可以理解为把它们放在一个对象中,这样当一个事件被触发的时候,就可以找到对应的方法去执行.再补一张图2.大体流程上面大致说了事件注册需要完成的两个目标。在完成目标的过程中需要完成哪些关键流程?首先react获取要挂载的组件的虚拟dom(其实就是reactdom,类似于一个对象),然后处理reactdom的props,判断属性中是否有声明为事件的属性,比如onclick.此时获取到事件类型click和对应的事件处理函数fn,然后直接进入接下来的3步a。执行事件注册b.将reactdom、事件类型、处理函数fn放入数组存储c中。组件挂载后,处理b步生成的数组,遍历后将事件处理函数存入listenerBank中,再匹配图3。具体执行过程3.1我们先从jsx开始,看大家最熟悉的代码,也是我们日常的写法}>child

}用babel编译后可以看到最后的调用方法是react.createElement,并且声明的事件类型和回调也是道具。react.createElement执行的结果会返回一个所谓的虚拟dom(reactelement或reactdom),见图3。2开始处理props,获取事件类型和回调fnReactDOMComponent加载(mountComponent)、更新(updateComponent)时需要处理props(_updateDOMProperties):可以看registrationNameModules的内容,这里不再赘述。3.3注册事件和事件存储【注册事件】然后上面的代码执行到这个方法enqueuePutListener(this,propKey,nextProp,transaction);在该方法中,事件注册和事件存储,包括冒泡和捕获处理,根据当前组件实例,获取最高父类——也就是document,然后执行方法listenTo——这也是事件绑定处理最关键的方法源码文件:ReactBrowerEventEmitter.js最后执行EventListener.listen(冒泡)或者EventListener.capture(捕获),只看冒泡注册,其实addEventListener的第三个参数为false,可以看到也是兼容的用ie注册事件时。上面看不到dispatchEvent的定义,下面可以看到传入dispatchEvent方法的代码。活动注册在这里完成。【事件存储】接下来就是开始事件的存储了。在react中,所有的事件触发器都是通过dispatchEvent方法统一派发的,而不是在注册的时候直接注册声明的回调。让我们看看如何存储它。【事件存储总结】react将所有的事件与事件类型和react组件进行关联,并将这种关系保存在一个map中,即一个对象(键值对),然后当事件被触发时,根据当前的查找按组件ID和事件类型对应的事件。加个简单图片看源码:functionenqueuePutListener(inst,registrationName,listener,transaction){varcontainerInfo=inst._hostContainerInfo;varisDocumentFragment=containerInfo._node&&containerInfo._node.nodeType===DOC_FRAGMENT_TYPE;vardoc=isDocumentFragment._node:containerInfo._ownerDocument;listenTo(registrationName,doc);//这个方法上面已经讲完了//这个涉及到事务,后面章节会介绍,主要是基于事件注册的//下面的代码是把putListener放到一个数组中,和当组件挂载后,数组的回调会依次执行。即putListener会执行transaction.getReactMountReady().enqueue(putListener,{inst:inst,//组件实例registrationName:registrationName,//事件类型点击监听器:listener//事件回调fn});}functionputListener(){varlistenerToPut=this;//放入数组,回调队列中,所有事件都会存储在一个对象——listenerBank中,具体由EventPluginHub管理。//获取组件的唯一IDvargetDictionaryKey=functiongetDictionaryKey(inst){return'.'+inst._rootNodeID;}putListener:functionputListener(inst,registrationName,listener){//获取组件idvarkey=getDictionaryKey(inst);//获取listenerBank对象中指定事件类型的对象varbankForRegistrationName=listenerBank[registrationName]||(listenerBank[registrationName]={});//存储回调fnbankForRegistrationName[key]=listener;//....}listenerBank其实是一个二级映射,方便查找事件。这里的componentid是组件的唯一标识,然后和fn关联起来,在触发阶段就可以找到相关的事件回调。看看listenerBank结构:这个结构是不是很眼熟?它是我们通常使用的对象。大体的流程到这里就说完了,是不是感觉有点清楚又不是很清楚。没关系,再来一张详细的图,重新理解一下4.最后,本文主要从整体流程上介绍事件在react事件中的注册过程,不深入源码细节.有兴趣的小伙伴可以自行查看源码,也希望本文能给大家带来一些启发。文章有不清楚的地方或者有疑问,欢迎留言交流。更多精彩内容,请关注我的公众号-前端张大发