著有《React 源码》《React 用到的一些算法》《javascript地月星》等多个专栏。欢迎关注。
文章不好写,要是有帮助别忘了点赞,收藏,评论 ~ 你的鼓励 是我继续挖干货的动力🔥。
另外,本文为原创内容,商业转载请联系作者获得授权,非商业转载需注明出处,感谢理解~
事件的收集
这一篇是对React 事件系统的设计原理的补充。在上一篇中只介绍了事件收集的起点,没有介绍事件收集的具体函数。
在dispatchEventsForPlugins内还调用了accumulateSinglePhaseListeners负责事件收集, targetFiber就是上一篇介绍过的事件收集的起点ancestorInst。
js
function accumulateSinglePhaseListeners(targetFiber, reactName, nativeEventType, inCapturePhase, accumulateTargetOnly, nativeEvent) {
var captureName = reactName !== null ? reactName + 'Capture' : null;
var reactEventName = inCapturePhase ? captureName : reactName;
var listeners = [];
var instance = targetFiber;
var lastHostComponent = null;
while (instance !== null) {//收集Fiber树路径上的所有事件回调函数
var _instance2 = instance,
stateNode = _instance2.stateNode,//HostComponents Fiber的真实DOM
tag = _instance2.tag; // 只要HostComponents (i.e. <div>)
if (tag === HostComponent && stateNode !== null) {//只要HostComponents
lastHostComponent = stateNode; // createEventHandle listeners
if (reactEventName !== null) {
var listener = getListener(instance, reactEventName); //当前Fiber节点的事件
if (listener != null) {
listeners.push(createDispatchListener(instance, listener, lastHostComponent));
}
}
}
if (accumulateTargetOnly) {
break;
}
// **这就是为什么前面HostPortal到rootA的点击事件也会被收集,因为到HostPortal后还继续向上冒泡**
instance = instance.return;
}
return listeners;
}
在props属性上获取事件:
js
function getListener(inst, registrationName) {
var stateNode = inst.stateNode;
if (stateNode === null) {
// Work in progress (ex: onload events in incremental mode).
return null;
}
// 拿到props
var props = getFiberCurrentPropsFromNode(stateNode);
if (props === null) {
// Work in progress.
return null;
}
// 从props中拿到事件。registrationName事件名。
var listener = props[registrationName];
if (shouldPreventMouseEvent(registrationName, inst.type, props)) {
return null;
}
if (listener && typeof listener !== 'function') {
throw new Error("Expected `" + registrationName + "` listener to be a function, instead got a value of `" + typeof listener + "` type.");
}
return listener;
}