React源码分析6 — React合成事件系统
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 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
});
}