当我们在组件上设置事件处理器时,React并不会在改DOM元素上直接绑定事件处理器,而是在react内部自定义一套事件系统,在这个系统上进行统一的事件订阅和分发。
react利用事件委托机制在Document上统一监听DOM事件,在根据触发的target将事件分发到具体的组件实例,实际我们在事件里面拿到的event其实并不是原始的DOM事件对象,而是一个合成事件对象
为什么需要事件系统
- 抹平浏览器之间的兼容性差异,react还会通过其他事件来模拟一些低版本不兼容的事件
- 事件合成,自定义高级事件,比如onChange事件,为表单元素定义了统一的值来变动事件
- 优化。比如利用事件委托,大部分事件最终绑定了Document,而不是dom节点本身,这样简化了dom事件处理逻辑,减少了内存的开销。react自己实现了一套模拟事件冒泡的机制
- react干预了事件的分发。Fiber架构,优化了用户的交互体验,干预事件的分发,不同的事件有不同的优先级。高的优先级事件可以中断渲染,让用户代码即使响应用户交互
插件机制
事件系统使用了插件机制来管理不同行为的事件。这些插件分别处理自己负责的事件类型。最后合成事件
1 .simpleEventPlugin:简单事件,click,input,keyDown,mouseOver,mouseOut,pointOver,pointOut
2 .EnterLeavEventPlugin:mouseEnter,mouseLeave,pointEnter,pointLeave.这类事件比较特殊
- 不支持冒泡
- enter事件会给所有进入的元素发送事件,如果树级层次比较深,大量的mouseenter会触发导致性能问题
- react使用*over/out来模拟enter/*leave事件
3 .changeEventPlugin:change事件是react的一个自定义事件,旨在规范表单元素变动事件,支持input,textarea,select
4 .selectEventPlugin:和change事件一样,为表单元素规范了select事件,用于input,textarea,contentEditable元素
5 .beforeInputEventPlugin:beforeInput事件以及composition事件的处理
ReactEventListener
1 .事件处理器,在这里进行事件处理器的绑定。当dom触发事件时,从这里开始调度分发到react组件树
ReactEventEmitter
1 .暴露接口给React组件层用于添加事件订阅
EventPropagator
1 .负责批量执行事件队列和事件处理器,比如事件冒泡
SyntheticEvent
1 .合成事件的基类
2 .可以对应dom的event事件,但是react为了减少内存损耗和垃圾回收
2 .使用一个对象池来构建和释放事件对象
事件分类和优先级
事件分类
- 离散事件:click,blur,focus,submit,tuchStart离散就触发
- 用户阻塞事件 事件会阻塞用户的交互:touchMove,mouseMove,scrroll,drag,dragOver
- 可持续事件 load,error,loadStart,abort,animationend,这个优先级越高,也就是应该立即同步执行。可连续执行,不会被打断
事件优先级
- immediate:马上同步执行,且不会被中断
- UserBlocking:一般是用户交互的结果,需要及时得到反馈
- Normal:应对那些不需要立即感受到的任务,比如网络请求
- low:可以放后,最后执行到了就可以
- idle:没有必要做的任务
绑定事件
1 .在props初始化和更新时会进行事件绑定。
2 .首先会判断元素是否是媒体类型,媒体类型的元素无法在document上面监听,直接在元素上进行绑定
3 .反之就是在document上绑定,需要两个东西
- 事件依赖表:mouseEnter依赖mouseover/mouseout
- reactBrowerEventemitter维护的“已订阅事件表”,事件处理只需要在document订阅一次。所以比在每个元素上订阅事件省略很多资源
分发事件
1 .为了避免频繁创建和释放事件对象导致性能损耗(对象创建和垃圾回收),React使用一个事件池来负责管理事件对象,使用完的事件对象会放回池中,以备后续的复用