合成事件
SyntheticEvent | 合成事件详解
Event pooling
如上图所示,在
上图为大致的
-
事件注册:
React 在组件加载(mount) 和更新(update) 时, 其中的ReactDOMComponent 会对传入的事件属性进行处理,对相关事件进行注册和存储。document 中注册的事件不处理具体的事件,仅对事件进行分发。ReactBrowserEventEmitter 作为事件注册入口,担负着事件注册和事件触发。注册事件的回调函数由EventPluginHub 来统一管理,根据事件的类型(type) 和组件标识( _rootNodeID
) 为key 唯一标识事件并进行存储。 -
事件执行:事件执行时,
document 上绑定事件ReactEventListener.dispatchEvent 会对事件进行分发,根据之前存储的类型(type) 和组件标识( _rootNodeID
) 找到触发事件的组件。ReactEventEmitter 利用EventPluginHub 中注入(inject) 的plugins( 例如:SimpleEventPlugin 、EnterLeaveEventPlugin) 会将原生的DOM 事件转化成合成的事件,然后批量执行存储的回调函,回调函数的执行分为两步,第一步是将所有的合成事件放到事件队列里面,第二步是逐个执行。需要注意的是,浏览器原生会为每个事件的每个listener 创建一个事件对象,可以从这个事件对象获取到事件的引用。这会造成高额的内存分配,React 在启动时就会为每种对象分配内存池,用到某一个事件对象时就可以从这个内存池进行复用,节省内存。
外部事件触发
外部触发关闭
点击事件是
window.__myapp_container = document.getElementById("app");
React.render(<App />, window.__myapp_container);
然后在组件中我们动态的设置对于根元素的监听:
export default class ClickListener extends Component {
static propTypes = {
children: PropTypes.node.isRequired,
onClickOutside: PropTypes.func.isRequired
};
componentDidMount() {
window.__myapp_container.addEventListener(
"click",
this.handleDocumentClick
);
}
componentWillUnmount() {
window.__myapp_container.removeEventListener(
"click",
this.handleDocumentClick
);
} /* using fat arrow to bind to instance */
handleDocumentClick = evt => {
const area = ReactDOM.findDOMNode(this.refs.area);
if (!area.contains(evt.target)) {
this.props.onClickOutside(evt);
}
};
render() {
return (
<div ref="area">
{this.props.children}
</div>
);
}
}