资讯专栏INFORMATION COLUMN

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

chaosx110 / 1987人阅读

摘要:文章涉及到的源码是基于版本,虽然不是最新版本但是也不会影响我们对事件机制的整体把握和理解。到这里事件注册就完事儿了。

前言

这是 react 事件机制的第三节 - 事件注册,通过本文你将了解react 事件的注册过程,以及在这个过程中主要经过了哪些关键步骤,同时结合源码进行验证和增强理解。

文章涉及到的源码是基于 react15.6.1版本,虽然不是最新版本但是也不会影响我们对 react 事件机制的整体把握和理解。

文中不会说非常细节的内容,而是会把大概的流程和原理性的内容进行介绍,做到对整体流程有个认知和理解。

内容大纲

主要做两件事 (事件注册、事件存储)

大致流程

具体执行过程

总结

1. 主要做两件事

按照我的理解,react 事件注册过程其实主要做了2件事:

a. 事件注册

b. 事件存储

a. 事件注册 - 组件挂载阶段,根据组件内的声明的事件类型-onclick,onchange 等,给 document 上添加事件 -addEventListener,并指定统一的事件处理程序 dispatchEvent。

b. 事件存储 - 就是把 react 组件内的所有事件统一的存放到一个地方,也就是缓存起来,可以理解成放入一个对象内,为了在触发事件的时候可以查找到对应的方法去执行。

再配个图

2. 大致流程

上面大致说了事件注册需要完成的两个目标,那完成目标的过程需要经过哪些关键处理呢?

首先 react 拿到将要挂载的组件的虚拟 dom(其实就是 react dom, 类似一个对象),然后处理react dom 的 props ,判断属性内是否有声明为事件的属性,比如onclick,这个时候得到事件类型 click 和对应的事件处理程序 fn,然后直行后面3步

a. 执行事件注册

b. 将react dom ,事件类型,处理函数 fn 放入数组存储

c. 组件挂载完成后,处理 b 步骤生成的数组,经过遍历把事件处理函数存储到listenerBank中

再配个图

3.具体执行过程

3.1 得先从 jsx 说起

看个最熟悉的代码,也是我们日常的写法

handleFatherClick=()=>{

    }

    handleChildClick=()=>{

    }

    render(){
        return 
child
}

经过 babel 编译后,可以看到最终调用的方法是react.createElement,而且声明的事件类型和回调也是一个props。

react.createElement执行的结果会返回一个所谓的虚拟 dom(react element 或者 react dom),看下图

3. 2 开始处理props,拿到事件类型和回调 fn

ReactDOMComponent在进行组件加载(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和事件类型查找到对应的事件。

再加个简易图

看源码:

function enqueuePutListener(inst, registrationName, listener, transaction) {

  var containerInfo = inst._hostContainerInfo;
  var isDocumentFragment = containerInfo._node && containerInfo._node.nodeType === DOC_FRAGMENT_TYPE;
  var doc = isDocumentFragment ? containerInfo._node : containerInfo._ownerDocument;
  listenTo(registrationName, doc);//这个方法上面已说完


  //这里涉及到了事务,事物会在以后的章节再介绍,主要看事件注册
  //下面的代码是将putListener放入数组,当组件挂载完后会依次执行数组的回调。也就是putListener会依次执行
  transaction.getReactMountReady().enqueue(putListener, {
    inst: inst,//组件实例
    registrationName: registrationName,//事件类型 click
    listener: listener //事件回调 fn
  });
}

function putListener() {
  var listenerToPut = this;
  //放入数组,回调队列
  EventPluginHub.putListener(listenerToPut.inst, listenerToPut.registrationName, listenerToPut.listener);
}

大致的流程就是执行完listenTo(事件注册),然后执行 putListener 方法进行事件存储,所有的事件都会存储到一个对象中 - listenerBank,具体由EventPluginHub进行管理。

 //拿到组件唯一标识 id
    var getDictionaryKey = function getDictionaryKey(inst) {

      return "." + inst._rootNodeID;

    }

   putListener: function putListener(inst, registrationName, listener) {

    //得到组件 id
        var key = getDictionaryKey(inst);

        //得到listenerBank对象中指定事件类型的对象
        var bankForRegistrationName = listenerBank[registrationName] || (listenerBank[registrationName] = {});

        //存储回调 fn
        bankForRegistrationName[key] = listener;

        //....
  }

listenerBank其实就是一个二级 map,这样的结构更方便事件的查找。

这里的组件 id 就是组件的唯一标识,然后和fn 进行关联,在触发阶段就可以找到相关的事件回调。

看下listenerBank结构:

看到这个结构是不是很熟悉呢?就是我们平常使用的 object.

到这里大致的流程已经说完,是不是感觉有点明白又不大明白。

没关系,再来个详细的图,重新理解下

4.最后

本文主要是从整体流程上介绍了下 react 事件中事件的注册过程,并没有深入到源码的细节,有兴趣的小伙儿可以自查下源码,也希望本文能够带给你一些启发,若文章有表述不清或有问题的地方欢迎留言交流。

更多精彩内容欢迎关注我的公众号-前端张大胖

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/104428.html

相关文章

  • 结合源码彻底理解 react事件机制原理 02 - 对于合成的理解

    摘要:前言这是事件机制系列文章的第二篇对于合成的理解,咱们就来说说合成这个名词。在给注册事件的时候也是对兼容性做了处理。总结以上就是我对于合成这个名词的理解,其实内部还处理了很多,我只是略微简单的举了几个栗子。 showImg(https://segmentfault.com/img/bVbtvI3?w=1048&h=550); 前言 这是react事件机制系列文章的第二篇-对于合成的理解,...

    nihao 评论0 收藏0
  • 结合源码彻底理解 react事件机制原理 01 - 对事件机制的初步理解和验证

    摘要:前言这是事件机制的第一篇,主要内容有表象理解,验证,意义和思考。因为合成事件的触发是基于浏览器的事件机制来实现的,通过冒泡机制冒泡到最顶层元素,然后再由统一去处理。合成事件的阻止冒泡不会影响原生事件。 showImg(https://segmentfault.com/img/bVbtvP2?w=800&h=420); 前言 这是 react 事件机制的第一篇,主要内容有:表象理解,验证...

    muddyway 评论0 收藏0
  • 前端小册 - 结合源码彻底理解 react 事件机制

    摘要:对事件机制的初步理解和验证对于合成的理解事件注册机制事件执行本文基于进行分析,虽然不是最新版本但是也不会影响我们对事件机制的整体把握和理解。最后希望通过本文可以让你对事件机制有更清晰的认识和理解。 showImg(https://segmentfault.com/img/bVbtvI3?w=1048&h=550); 前言 写这个文章也算是实现19年的一个 flag,研究一个知识点并且把...

    YJNldm 评论0 收藏0
  • 结合源码彻底理解 react事件机制原理 04 - 事件执行

    摘要:文章涉及到的源码是基于版本,虽然不是最新版本但是也不会影响我们对事件机制的整体把握和理解。总结本文主要是从整体流程上介绍了下事件触发的过程。 showImg(https://segmentfault.com/img/bVbtvI3?w=1048&h=550); 前言 这是 react 事件机制的第四节-事件执行,一起研究下在这个过程中主要经过了哪些关键步骤,本文也是react 事件机制...

    marser 评论0 收藏0
  • 重磅:前端 MVVM 与 FRP 的升阶实践 —— ReRest 可视化编程

    摘要:是前端开发领域新兴的方法论体系,它继承了与编程理念,在技术上有不少创新。但专利与开源协议是平行的两个世界,改底层也不大容易解决问题。此外,要求在中结合各属性的是否变化,判断是否该触发更新。 ReRest (Reactive Resource State Transfer) 是前端开发领域新兴的方法论体系,它继承了 MVVM 与 FRP 编程理念,在技术上有不少创新。本文从专利稿修改而来...

    Cciradih 评论0 收藏0

发表评论

0条评论

chaosx110

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<