事件
- react采用了合成事件
- 简述:如我们写的onClick并没有绑定到真实dom上,而是属于fiber的属性,而事件是具有传播性的(冒泡和捕获),那么我们在根容器上绑定一个冒泡和捕获,然后按照执行的顺序去遍历fiber,拿到它属性中对应的on事件,然后给事件包装下执行即可。
- 可以统一解决浏览器兼容性并充分使用了事件委托机制
绑定
- 在crateRoot阶段进行的根容器绑定
- 通过bind,将前几个值固定,后续只是灵活的去
处理事件对象
dispatchDiscreteEvent.bind(null, domEventName, eventSystemFlags, targetContainer);
function dispatchDiscreteEvent(domEventName, eventSystemFlags, container, nativeEvent) {
dispatchEvent(domEventName, eventSystemFlags, container, nativeEvent);
}
let allNativeEvents = new Set(['click'......]);
allNativeEvents.forEach((domEventName) => {
listenToNativeEvent(domEventName, true, rootContainerElement);
listenToNativeEvent(domEventName, false, rootContainerElement);
});
事件触发
- 执行dispatchEvent
- 先
获取target
,通过多种形式去取,避免浏览器差异
function getEventTarget(nativeEvent) {
const target = nativeEvent.target || nativeEvent.srcElement || window;
return target;
}
- 然后通过
真实dom上的标识拿到fiber
- 在fiber生成真实dom的阶段,会将fiber通过一个标识,存储到真实dom中,为的是方便获取
- 这个key是一个动态的值,每次刷新页面都会重新生成,且存在内存中
props也是相同的操作
,也通过一个唯一标识存储在真实dom中
const randomKey = Math.random().toString(36).slice(2);
const internalInstanceKey = '__reactFiber$' + randomKey;
const internalPropsKey = "__reactProps$" + randomKey;
export function updateFiberProps(node, props) {
node[internalPropsKey] = props;
}
export function getFiberCurrentPropsFromNode(node) {
return node[internalPropsKey] || null;
}
export function precacheFiberNode(hostInst, node) {
node[internalInstanceKey] = hostInst;
}
- 然后通过fiber开始向上遍历,拿到对应的props中的对应事件和fiber的stateNode
- 先存起来,然后找完以后,处理事件源
- 将事件源的原始信息拷贝下,然后添加一些新的属性
- 并且在原型上处理preventDefault和stopPropagation事件,实现
跨平台
const assion = Object.assion;
function functionThatReturnsTrue() {
return true;
}
function functionThatReturnsFalse() {
return false;
}
const MouseEventInterface = {
clientX: 0,
clientY: 0
}
function createSyntheticEvent(inter) {
function SyntheticBaseEvent(
reactName, reactEventType, targetInst, nativeEvent, nativeEventTarget) {
this._reactName = reactName;
this.type = reactEventType;
this._targetInst = targetInst;
this.nativeEvent = nativeEvent;
this.target = nativeEventTarget;
for (const propName in inter) {
if (!inter.hasOwnProperty(propName)) {
continue;
}
this[propName] = nativeEvent[propName]
}
this.isDefaultPrevented = functionThatReturnsFalse;
this.isPropagationStopped = functionThatReturnsFalse;
return this;
}
assign(SyntheticBaseEvent.prototype, {
preventDefault() {
const event = this.nativeEvent;
if (event.preventDefault) {
event.preventDefault();
} else {
event.returnValue = false;
}
this.isDefaultPrevented = functionThatReturnsTrue;
},
stopPropagation() {
const event = this.nativeEvent;
if (event.stopPropagation) {
event.stopPropagation();
} else {
event.cancelBubble = true;
}
this.isPropagationStopped = functionThatReturnsTrue;
}
});
return SyntheticBaseEvent;
}
export const SyntheticMouseEvent = createSyntheticEvent(MouseEventInterface);
- 开始依次是在收集到的函数,每次执行前,先将stateNode给到event.currentTarget,所以它是在实时变化的,并且将处理好的事件源传给函数
- 阻止冒泡和捕获因为包装了一层,所以可以在实例上拿到是否阻止,如果阻止就break,不执行后续操作事件了