react事件系统
本文讲解的react版本是16.13.1,在react17版本和react18版本中,会有差异,之后的文章会说到。
react事件系统大体可以分为以下三个部分:
- 初始化事件
- 注入事件插件,设置一些对象,主要为后面的事件绑定和事件触发做准备。
- 事件绑定
- 在react初始化DOM时,根据每个dom对应的fiber对象上是否有onClick这类事件props
- 如果有,将事件代理到document上(在react17之后,代理到react应用的container上),包装一个统一事件处理函数dispatchEvent作为listener。
- 事件触发
- 触发事件,事件冒泡到document,执行dispatchEvent。
- 根据e.target,找到事件源dom,通过dom,获取对应fiber,从当前fiber向上遍历,找到所有绑定了相同事件的父级fiber,把react dom上绑定的事件函数放到事件队列中。
- 模拟原生事件冒泡和捕获,执行全部事件。
接下来看源码是如何实现一套自己的事件系统。源码分析中,省略了一些不重要的代码,只展示了主要逻辑代码。
初始化事件系统
在初始化事件中,主要是注入事件插件,设置一些全局对象,为之后的事件绑定和事件触发做准备。例如在初始化过程中设置了registrationNameModules对象,记录所有事件对应要使用的事件插件。
var DOMEventPluginOrder = ['ResponderEventPlugin', 'SimpleEventPlugin', 'EnterLeaveEventPlugin', 'ChangeEventPlugin', 'SelectEventPlugin', 'BeforeInputEventPlugin'];
injectEventPluginOrder(DOMEventPluginOrder);
injectEventPluginsByName({
SimpleEventPlugin: SimpleEventPlugin,
EnterLeaveEventPlugin: EnterLeaveEventPlugin,
ChangeEventPlugin: ChangeEventPlugin,
SelectEventPlugin: SelectEventPlugin,
BeforeInputEventPlugin: BeforeInputEventPlugin
});
首先执行injectEventPluginOrder
- 拷贝了传入的
DOMEventPluginOrder,赋值给eventPluginOrder,后面会根据这个顺序生成plugins - 执行
recomputePluginOrdering
function injectEventPluginOrder(injectedEventPluginOrder) {
// Clone the ordering so it cannot be dynamically mutated.
eventPluginOrder = Array.prototype.slice.call(injectedEventPluginOrder);
recomputePluginOrdering();
}
执行injectEventPluginsByName
function injectEventPluginsByName(injectedNamesToPlugins) {
var isOrderingDirty = false;
for (var pluginName in injectedNamesToPlugins) {
var pluginModule = injectedNamesToPlugins[pluginName];
if (!namesToPlugins.hasOwnProperty(pluginName) || namesToPlugins[pluginName] !== pluginModule) {
namesToPlugins[pluginName] = pluginModule;
isOrderingDirty = true;
}
}
if (isOrderingDirty) {
recomputePluginOrdering();
}
}
- 遍历
injectedNamesToPlugins。把注入的所有插件,设置插件名称和插件模块的映射关系,记录到namesToPlugins上 - 最后执行
recomputePluginOrdering。
执行recomputePluginOrdering
function recomputePluginOrdering() {
if (!eventPluginOrder) {
return;
}
for (var pluginName in namesToPlugins) {
var pluginModule = namesToPlugins[pluginName];
var pluginIndex = eventPluginOrder.indexOf(pluginName);
if (plugins[pluginIndex]) {
continue;
}
plugins[pluginIndex] = pluginModule;
var publishedEvents = pluginModule.eventTypes;
for (var eventName in publishedEvents) {
publishEventForPlugin(publishedEvents[eventName], pluginModule, eventName)
}
}
}
- 遍历
namesToPlugins,每个循环中,看当前插件在eventPluginOrder中的下标位置,取出对应的插件模块,把plugins对应的下标赋值为插件模块。 - 获取当前遍历中的插件模块的
eventTypes,遍历eventTypes执行publishEventForPlugin。
在执行
publishEventForPlugin之前,我们需要了解在一开始注入的各种事件插件的具体结构,以便知道上述的pluginModule.eventTypes是什么,还有接下来要做什么。下面以ChangeEventPlugin为例:
var eventTypes$1 = {
change: {
phasedRegistrationNames: {
bubbled: 'onChange',
captured: 'onChangeCapture'
},
dependencies: [TOP_BLUR, TOP_CHANGE, TOP_CLICK, TOP_FOCUS, TOP_INPUT, TOP_KEY_DOWN, TOP_KEY_UP, TOP_SELECTION_CHANGE]
}
};
var ChangeEventPlugin = {
eventTypes: eventTypes$1,
_isInputEventSupported: isInputEventSupported,
extractEvents: function (topLevelType, targetInst, nativeEvent, nativeEventTarget, eventSystemFlags) {
...
}
};
- 所以上一步中,获取当前遍历中的插件模块的
eventTypes,遍历eventTypes执行publishEventForPlugin
var publishedEvents = pluginModule.eventTypes;
for (var eventName in publishedEvents) {
publishEventForPlugin(publishedEvents[eventName], pluginModule, eventName)
}
调用publishEventForPlugin:
第一个参数是:
{
phasedRegistrationNames: {
bubbled: 'onChange',
captured: 'onChangeCapture'
},
dependencies: [TOP_BLUR, TOP_CHANGE, TOP_CLICK, TOP_FOCUS, TOP_INPUT, TOP_KEY_DOWN, TOP_KEY_UP, TOP_SELECTION_CHANGE]
}
第二个参数:事件插件模块
第三个参数:事件名称,以ChangeEventPlugin为例就是 change,其他插件中eventTypes可能不像ChangeEventPlugin只有一个change属性,会有多个,原理相同,有多少个就执行多少次
function publishEventForPlugin(dispatchConfig, pluginModule, eventName) {
eventNameDispatchConfigs[eventName] = dispatchConfig;
var phasedRegistrationNames = dispatchConfig.phasedRegistrationNames;
if (phasedRegistrationNames) {
for (var phaseName in phasedRegistrationNames) {
if (phasedRegistrationNames.hasOwnProperty(phaseName)) {
var phasedRegistrationName = phasedRegistrationNames[phaseName];
publishRegistrationName(phasedRegistrationName, pluginModule, eventName);
}
}
return true;
}
return false;
}
- 设置
eventNameDispatchConfigs,将注入的所有插件的eventTypes属性里包含的事件名称用key记录下来,value对应dispatchConfig。生成下面格式:
{
change: ChangePlugin.eventTypes.change,
// ...other plugins
}
- 取出
dispatchConfig的phasedRegistrationName属性{ bubbled: 'onChange', captured: 'onChangeCapture' }遍历,执行publishRegistrationName, 以ChangeEventPlugin插件为例:
第一个参数分别传入onChange、onChangeCapture
第二个参数传入ChangeEventPlugin
第三个参数传入change
执行publishRegistrationName:
function publishRegistrationName(registrationName, pluginModule, eventName) {
// 1
registrationNameModules[registrationName] = pluginModule;
// 2
registrationNameDependencies[registrationName] = pluginModule.eventTypes[eventName].dependencies;
{
var lowerCasedName = registrationName.toLowerCase();
possibleRegistrationNames[lowerCasedName] = registrationName;
if (registrationName === 'onDoubleClick') {
possibleRegistrationNames.ondblclick = registrationName;
}
}
}
- 设置
registrationNameModules,记录事件和插件的映射关系,遍历完所有插件最后生成类似如下格式:
{
...
onChange: ChangePlugin,
onChangeCapture: ChangePlugin
...
}
- 设置registrationNameDependencies,记录事件和依赖事件的映射关系:
{
onChange: ChangePlugin.eventTypes.change.dependencies,
onChangeCapture: ChangePlugin.eventTypes.change.dependencies
}