React源码分析6 — React合成事件系统

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

React源码分析6 — React合成事件系统

本文章来自于阿里云云栖社区

摘要: React源码系列文章,请多支持:[React源码分析1 —组件和对象的创建(createClass,createElement)](https:///articles/72905) [R eact源码分析2 —React组件插入DOM流程](http://www.

React源码系列文章,请多支持:

React源码分析1 —组件和对象的创建(createClass,createElement)

React源码分析2 — React组件插入DOM流程

React源码分析3 — React生命周期详解

React源码分析4 — setState机制

React源码分析5 -- 组件通信,refs,key,ReactDOM

React源码分析6 — React合成事件系统

1 React合成事件特点

React自己实现了一套高效的事件注册,存储,分发和重用逻辑,在DOM事件体系基础上做了很大改进,减少了内存消耗,简化了事件逻辑,并最大化的解决了IE等浏览器的不兼容问题。与DOM事件体系相比,它有如下特点

1. React组件上声明的事件最终绑定到了document这个DOM节点上,而不是

React组件对应的DOM节点。故只有document这个节点上面才绑定了DOM 原生事件,其他节点没有绑定事件。这样简化了DOM原生事件,减少了内存开销

2. React以队列的方式,从触发事件的组件向父组件回溯,调用它们在JSX中声

明的callback。也就是React自身实现了一套事件冒泡机制。我们没办法用event.stopPropagation()来停止事件传播,应该使用event.preventDefault()

3. React有一套自己的合成事件SyntheticEvent,不同类型的事件会构造不同的

SyntheticEvent

4. React使用对象池来管理合成事件对象的创建和销毁,这样减少了垃圾的生成和

新对象内存的分配,大大提高了性能

那么这些特性是如何实现的呢,下面和大家一起一探究竟。

2 React事件系统

先看Facebook给出的React事件系统框图

浏览器事件(如用户点击了某个button)触发后,DOM将event传给

ReactEventListener,它将事件分发到当前组件及以上的父组件。然后由

ReactEventEmitter对每个组件进行事件的执行,先构造React合成事件,然后以queue的方式调用JSX中声明的callback进行事件回调。

涉及到的主要类如下

ReactEventListener:负责事件注册和事件分发。React将DOM事件全都注册到document这个节点上,这个我们在事件注册小节详细讲。事件分发主要调用dispatchEvent进行,从事件触发组件开始,向父元素遍历。我们在事件执行小节详细讲。

ReactEventEmitter:负责每个组件上事件的执行。

EventPluginHub:负责事件的存储,合成事件以对象池的方式实现创建和销毁,大大提高了性能。

SimpleEventPlugin等plugin:根据不同的事件类型,构造不同的合成事件。如focus对应的React合成事件为SyntheticFocusEvent

2 事件注册

JSX中声明一个React事件十分简单,比如

render() {

return (

(event) => {console.log(JSON.stringify(event))}

}

/>

);

}

那么它是如何被注册到React事件系统中的呢?

还是先得从组件创建和更新的入口方法mountComponent和updateComponent 说起。在这两个方法中,都会调用到_updateDOMProperties方法,对JSX中声明的组件属性进行处理。源码如下

_updateDOMProperties: function(lastProps, nextProps, transactio n){

... // 前面代码太长,省略一部分

elseif (registrationNameModules.hasOwnProperty(propKey)) {

// 如果是props这个对象直接声明的属性,而不是从原型链中继承而来的,则处理它

// nextProp表示要创建或者更新的属性,而lastProp则表示上一次的属性// 对于mountComponent,lastProp为null。updateComponent二者都不为null。unmountComponent则nextProp为null

if (nextProp) {

// mountComponent和updateComponent中,enqueuePutListener注册事件

enqueuePutListener(this, propKey, nextProp, transaction);

} elseif (lastProp) {

// unmountComponent中,删除注册的listener,防止内存泄漏deleteListener(this, propKey);

}

}

}

下面我们来看enqueuePutListener,它负责注册JSX中声明的事件。源码如下// inst: React Component对象

// registrationName: React合成事件名,如onClick

// listener: React事件回调方法,如onClick=callback中的callback // transaction: mountComponent或updateComponent所处的事务流中,R eact都是基于事务流的

function enqueuePutListener(inst, registrationName, listener, tr ansaction){

if (transaction instanceof ReactServerRenderingTransaction) { return;

}

var containerInfo = inst._hostContainerInfo;

var isDocumentFragment = containerInfo._node &&containerInfo._no de.nodeType === DOC_FRAGMENT_TYPE;

// 找到document

var doc = isDocumentFragment ? containerInfo._node :containerIn fo._ownerDocument;

// 注册事件,将事件注册到document上

listenTo(registrationName, doc);

// 存储事件,放入事务队列中

transaction.getReactMountReady().enqueue(putListener, {

inst: inst,

registrationName: registrationName,

listener: listener

});

}