ReactBrowserEventEmitter模块区别EventPulginHub模块,后者用于缓存或获取组件实例的绑定函数,前者为节点绑定切实的回调函数,该回调函数执行过程中构建合成事件对象,获取组件实例的绑定回调并执行,若有state变更,则重绘组件。
'use strict';
var _assign = require('object-assign');
// 获取事件插件模块中各事件的依赖
var EventPluginRegistry = require('./EventPluginRegistry');
// 为ReactBrowserEventEmitter.ReactEventListener提供_handleTopLevel方法
// 该方法用于构建合成事件对象及执行该对象的绑定回调
var ReactEventEmitterMixin = require('./ReactEventEmitterMixin');
// scroll或resize事件事件发生时缓存滚动偏移量
var ViewportMetrics = require('./ViewportMetrics');
// 获取当前浏览器支持的animationend、animationiteration、animationstart、transitionend事件名
var getVendorPrefixedEventName = require('./getVendorPrefixedEventName');
// 判断是否支持某事件
var isEventSupported = require('./isEventSupported');
var hasEventPageXY;
var alreadyListeningTo = {};
var isMonitoringScrollValue = false;
var reactTopListenersCounter = 0;
// 普通事件名到top事件名的映射
var topEventMapping = {
topAbort: 'abort',
topAnimationEnd: getVendorPrefixedEventName('animationend') || 'animationend',
topAnimationIteration: getVendorPrefixedEventName('animationiteration') || 'animationiteration',
topAnimationStart: getVendorPrefixedEventName('animationstart') || 'animationstart',
topBlur: 'blur',
topCanPlay: 'canplay',
topCanPlayThrough: 'canplaythrough',
topChange: 'change',
topClick: 'click',
topCompositionEnd: 'compositionend',
topCompositionStart: 'compositionstart',
topCompositionUpdate: 'compositionupdate',
topContextMenu: 'contextmenu',
topCopy: 'copy',
topCut: 'cut',
topDoubleClick: 'dblclick',
topDrag: 'drag',
topDragEnd: 'dragend',
topDragEnter: 'dragenter',
topDragExit: 'dragexit',
topDragLeave: 'dragleave',
topDragOver: 'dragover',
topDragStart: 'dragstart',
topDrop: 'drop',
topDurationChange: 'durationchange',
topEmptied: 'emptied',
topEncrypted: 'encrypted',
topEnded: 'ended',
topError: 'error',
topFocus: 'focus',
topInput: 'input',
topKeyDown: 'keydown',
topKeyPress: 'keypress',
topKeyUp: 'keyup',
topLoadedData: 'loadeddata',
topLoadedMetadata: 'loadedmetadata',
topLoadStart: 'loadstart',
topMouseDown: 'mousedown',
topMouseMove: 'mousemove',
topMouseOut: 'mouseout',
topMouseOver: 'mouseover',
topMouseUp: 'mouseup',
topPaste: 'paste',
topPause: 'pause',
topPlay: 'play',
topPlaying: 'playing',
topProgress: 'progress',
topRateChange: 'ratechange',
topScroll: 'scroll',
topSeeked: 'seeked',
topSeeking: 'seeking',
topSelectionChange: 'selectionchange',
topStalled: 'stalled',
topSuspend: 'suspend',
topTextInput: 'textInput',
topTimeUpdate: 'timeupdate',
topTouchCancel: 'touchcancel',
topTouchEnd: 'touchend',
topTouchMove: 'touchmove',
topTouchStart: 'touchstart',
topTransitionEnd: getVendorPrefixedEventName('transitionend') || 'transitionend',
topVolumeChange: 'volumechange',
topWaiting: 'waiting',
topWheel: 'wheel'
};
// 缓存document绑定事件记录不与其他fragment下的document节点绑定事件数据起冲突
var topListenersIDKey = '_reactListenersID' + String(Math.random()).slice(2);
// document节点mountAt已经绑定事件,不必再次绑定
function getListeningForDocument(mountAt) {
// IE8 `mountAt` is a host object and doesn't have `hasOwnProperty`
if (!Object.prototype.hasOwnProperty.call(mountAt, topListenersIDKey)) {
mountAt[topListenersIDKey] = reactTopListenersCounter++;
alreadyListeningTo[mountAt[topListenersIDKey]] = {};
}
return alreadyListeningTo[mountAt[topListenersIDKey]];
}
// react事件机制:
// 通过ReactDefaultInjection模块注入ReactEventListener模块,设为ReactBrowserEventEmitter.ReactEventListener属性
// ReactBrowserEventEmitter为ReactEventListener属性提供_handleTopLevel方法,即ReactEventEmitterMixin模块的handleTopLevel
// 该方法中调用EventPluginHub.extractEvents方法执行各事件插件如SimpleEventPlugin等的extractEvents方法,构建合成事件对象
// 再通过runEventQueueInBatch函数调用EventPluginHub的enqueueEvents、processEventQueue方法,执行合成事件对象的绑定回调函数
// EventPluginHub模块通过EventPluginRegistry调用已注册的事件插件的extractEvents方法,完成合成事件对象的构建
// 同时提供enqueueEvents、processEventQueue方法,执行合成事件对象的绑定回调函数,该过程调用EventPluginUtils模块中的方法
// 提供putListener将组件实例的事件回调存储在缓存中;deleteListener、deleteAllListeners删除缓存中绑定事件回调函数
// EventPluginRegistry用于加载并存储、或获取各类事件插件
// SimpleEventPlugin等事件插件提供extractEvents方法构建合成事件对象,并解决兼容性问题
// 合成事件对象构建过程调用EventPropagators的accumulateTwoPhaseDispatches等方法,为该对象添加绑定事件的回调函数及其相应组件
// EventPropagators模块为SyntheticEvent等合成对象实例添加_dispatchListeners、_dispatchInstances属性
// 即组件实例绑定的事件回调函数,该过程调用EventPluginUtils模块中的方法,用于获取父组件的冒泡、捕获事件
// EventPluginUtils模块为合成事件对象添加绑定的回调函数及关联的组件实例,或者获取绑定的回调函数并执行
// ReactBrowserEventEmitter各接口调用ReactEventListener模块方法,绑定事件,在浏览器事件触发后
// 构建合成事件对象,并调用合成事件对象的绑定回调函数;若此过程存在组件state变更,则重绘组件
//
// react事件书写时分为两个过程
// 其一调用ReactBrowserEventEmitter的trapBubbledEvent等方法为节点或文档绑定事件
// 回调函数即ReactBrowserEventEmitter.ReactEventListener.dispatchEvent执行组件实例的绑定函数,state变更时予以重绘组件
// 其二调用EventPluginHub的putListener方法将组件实例的绑定回调函数存储到缓存中
// 事件触发后,ReactEventListener.dispatchEvent将把该回调函数赋予合成事件对象的_dispatchListener属性
//
// +------------+ .
// | DOM | .
// +------------+ .
// | .
// v .
// +------------+ .
// | ReactEvent | .
// | Listener | .
// +------------+ . +-----------+
// | . +--------+|SimpleEvent|
// | . | |Plugin |
// +-----|------+ . v +-----------+
// | | | . +--------------+ +------------+
// | +-----------.--->|EventPluginHub| | Event |
// | | . | | +-----------+ | Propagators|
// | ReactEvent | . | | |TapEvent | |------------|
// | Emitter | . | |<---+|Plugin | |other plugin|
// | | . | | +-----------+ | utilities |
// | +-----------.--->| | +------------+
// | | | . +--------------+
// +-----|------+ . ^ +-----------+
// | . | |Enter/Leave|
// + . +-------+|Plugin |
// +-------------+ . +-----------+
// | application | .
// |-------------| .
// | | .
// | | .
// +-------------+ .
// 调用ReactBrowserEventEmitter.ReactEventListener方法以react方式监听节点事件
// 绑定ReactEventListener.dispatchEvent派发事件方法,回调中构建合成事件对象并执行合成事件对的象绑定回调
// listenTo方法为document节点绑定事件,使事件向fragment上层组件传播
var ReactBrowserEventEmitter = _assign({}, ReactEventEmitterMixin, {
// 通过ReactDefaultInjection模块赋值为ReactEventListener模块
// 并向ReactEventListener模块添加_handleTopLevel=ReactEventEmitterMixin.handleTopLevel
ReactEventListener: null,
injection: {
injectReactEventListener: function (ReactEventListener) {
// ReactBrowserEventEmitter._handleTopLevel通过浅拷贝,赋值为ReactEventEmitterMixin.handleTopLevel
ReactEventListener.setHandleTopLevel(ReactBrowserEventEmitter.handleTopLevel);
// 通过ReactDefaultInjection模块赋值为ReactEventListener模块
ReactBrowserEventEmitter.ReactEventListener = ReactEventListener;
}
},
// 是否允许派发react事件,即在组件重绘后调用合成事件对象的绑定回调函数
setEnabled: function (enabled) {
if (ReactBrowserEventEmitter.ReactEventListener) {
ReactBrowserEventEmitter.ReactEventListener.setEnabled(enabled);
}
},
// 判断是否允许派发react事件
isEnabled: function () {
return !!(ReactBrowserEventEmitter.ReactEventListener && ReactBrowserEventEmitter.ReactEventListener.isEnabled());
},
// firefox、safari、ie兼容性问题,或者不触发document节点的冒泡事件,或者可以冒泡到document上,却不能冒泡到window对象
// document节点由react机制创建,作为fragment下的节点,需要绑定事件,实现事件向fragment上层组件传播
// 参数registrationName为普通事件名如"click",contentDocumentHandle为document节点
listenTo: function (registrationName, contentDocumentHandle) {
var mountAt = contentDocumentHandle;
// 获取fragment下document节点已绑定事件的记录
var isListening = getListeningForDocument(mountAt);
// 获取事件插件中各事件的依赖,如topClick等
var dependencies = EventPluginRegistry.registrationNameDependencies[registrationName];
for (var i = 0; i < dependencies.length; i++) {
var dependency = dependencies[i];
if (!(isListening.hasOwnProperty(dependency) && isListening[dependency])) {
if (dependency === 'topWheel') {
if (isEventSupported('wheel')) {
ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent('topWheel', 'wheel', mountAt);
} else if (isEventSupported('mousewheel')) {
ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent('topWheel', 'mousewheel', mountAt);
} else {
// Firefox needs to capture a different mouse scroll event.
// @see http://www.quirksmode.org/dom/events/tests/scroll.html
ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent('topWheel', 'DOMMouseScroll', mountAt);
}
} else if (dependency === 'topScroll') {
if (isEventSupported('scroll', true)) {
ReactBrowserEventEmitter.ReactEventListener.trapCapturedEvent('topScroll', 'scroll', mountAt);
} else {
ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent('topScroll', 'scroll', ReactBrowserEventEmitter.ReactEventListener.WINDOW_HANDLE);
}
} else if (dependency === 'topFocus' || dependency === 'topBlur') {
if (isEventSupported('focus', true)) {
ReactBrowserEventEmitter.ReactEventListener.trapCapturedEvent('topFocus', 'focus', mountAt);
ReactBrowserEventEmitter.ReactEventListener.trapCapturedEvent('topBlur', 'blur', mountAt);
} else if (isEventSupported('focusin')) {
// IE has `focusin` and `focusout` events which bubble.
// @see http://www.quirksmode.org/blog/archives/2008/04/delegating_the.html
ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent('topFocus', 'focusin', mountAt);
ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent('topBlur', 'focusout', mountAt);
}
// 保证只为document节点绑定一次focus、blur事件
isListening.topBlur = true;
isListening.topFocus = true;
} else if (topEventMapping.hasOwnProperty(dependency)) {
ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(dependency, topEventMapping[dependency], mountAt);
}
isListening[dependency] = true;
}
}
},
// 冒泡方式绑定事件,回调设为ReactEventListener.dispatchEvent,构建合成事件对象并执行该对象的回调
// 参数topLevelType如topClick,handlerBaseName如click,handle为绑定事件的节点
trapBubbledEvent: function (topLevelType, handlerBaseName, handle) {
return ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(topLevelType, handlerBaseName, handle);
},
// 捕获方式绑定事件,回调设为ReactEventListener.dispatchEvent,构建合成事件对象并执行该对象的回调
trapCapturedEvent: function (topLevelType, handlerBaseName, handle) {
return ReactBrowserEventEmitter.ReactEventListener.trapCapturedEvent(topLevelType, handlerBaseName, handle);
},
// 判断浏览器是否支持event.pageX|pageY属性
supportsEventPageXY: function () {
if (!document.createEvent) {
return false;
}
var ev = document.createEvent('MouseEvent');
return ev != null && 'pageX' in ev;
},
// 浏览器不支持event.pageX|pageY属性时,scroll或resize事件触发时将滚动偏移量赋值给ViewportMetrics
ensureScrollValueMonitoring: function () {
if (hasEventPageXY === undefined) {
hasEventPageXY = ReactBrowserEventEmitter.supportsEventPageXY();
}
if (!hasEventPageXY && !isMonitoringScrollValue) {
var refresh = ViewportMetrics.refreshScrollValues;
ReactBrowserEventEmitter.ReactEventListener.monitorScrollValue(refresh);
isMonitoringScrollValue = true;
}
}
});
module.exports = ReactBrowserEventEmitter;
本文深入解析React事件系统的工作原理,包括ReactBrowserEventEmitter模块的作用、事件的绑定与触发流程、合成事件的创建与执行机制等关键内容。
1万+

被折叠的 条评论
为什么被折叠?



