4

好久没写博客了,前段时间太忙以至于平时的积累都记录在内网的wiki里,趁着这几天有空,将这段时间所积累的干货慢慢的分享出来,如果内容有不正确的地方,欢迎纠正。

本博客大概介绍一下react的事件机制,并给出整体的设计图,但不涉及react底层的源码结构分析。

现象:

demo1: <div onclick="handle()">ni</div>

demo2: render() { return <div onClick={this.handle}>ni</div> }

虽然两个例子都是通过标签内嵌的方式将click事件进行绑定,但其中的原理是不一样的,demo1是采用原生的事件处理,demo2是采用react的合成事件机制处理;

合成事件:

对于jsx来说,是采用了类似于DOM0的事件绑定的方式进行处理,它会收到一个合成事件对象(synthicevent),该对象集成了原生事件对象的所有特性,而且还是事件冒泡机制,并且能支持stopPropagation和preventDefault两种方法;

原生事件与合成事件的区别:

  1. 原生的事件绑定是采用小写onclick,而react则是采用大写onClick;
  2. 原生事件绑定的是一个js的字符串,而react采用的是一个函数的指针;

合成事件的实现机制:

事件机制原型图:

clipboard.png

该图大概的表示了react的事件机制的整体结构图,接下来具体说说它里面的原理。

  • 真正的监听者:

对于react来说,虽然事件是绑定在v-dom中,但其实真正的监听者只有一个,就是结构中最外层的document对象进行监听,主要采用了事件冒泡的方式,将v-dom中触发的事件包装成一个合成事件,然后通过事件冒泡的方式,最终冒泡到最外层的document监听和执行(就是事件委托);

clipboard.png

  • 事件注册:

事件注册是在组件生成的时候,将v-dom中所有的事件都对应的原生事件都注册在document的监听器中,例如onClick对应的原生事件是onclick,如果v-dom中有绑定了onClick,那么就会将对应的onclick事件注册在document中,整个注册过程可以三个阶段:

1) 将v-dom中所涉及到的绑定事件所对应的原生事件都在enqueuePutListener中绑定到document身上;

2) 将v-dom中所有事件的事件处理函数都存放在listenerBank中,存放的方式是以registrtionname和key作为索引存放,其中registrtionname是事件名,key是instance的id值,所以形式是:

   listenerBank[registrtionname][key] = listener

这样的好处是将可能要触发的事件分门别类,以及将对应的listener也分门别类存放;为了就是在事件触发的时候,能从listenerBank中取出同类型的listener存放在dispatchListener中;

3) 最后将dispatchEvent作为callback函数,放在addEventListener和removeEventListener里面,等待事件的触发;

  • 合成事件:

当事件触发的时候,不会直接将原生的事件发送到最外层的document中,而是经过处理,将处理后的事件发送到document中;事件合成的经过:获取原生事件,并通过原生事件的类型和所在组件的id值,在listenerBank中取出对应的listener函数,并存放在_dispatchListener队列中,然后将该实例存放到_dispatchInstance队列中,这样一来,可以将同类型的事件函数都按照顺序存放在_dispatchListener中,最后一同处理;

  • 事件发布:

当事件触发时,会先原生事件变成合成事件,然后传递到document中,然后document会通过dispatchEvent回调函数依次执行dispatchListener中同类型的事件监听函数。

  • 整个设计图:

clipboard.png

以上是对react的合成事件一个大概的介绍,里面还有很多细节和原理没说到,有兴趣的同学可以进一步研究一下源码的细节。


DragonChen
285 声望15 粉丝

下一篇是:Axios源码解析。