diff --git a/libs/horizon/src/dom/DOMInternalKeys.ts b/libs/horizon/src/dom/DOMInternalKeys.ts index 2be5522e..a71e7502 100644 --- a/libs/horizon/src/dom/DOMInternalKeys.ts +++ b/libs/horizon/src/dom/DOMInternalKeys.ts @@ -91,7 +91,7 @@ export function getEventListeners(dom: EventTarget): Set { return elementListeners; } -export function getEventToListenerMap(target: EventTarget): Map { +export function getNonDelegatedListenerMap(target: EventTarget): Map { let eventsMap = target[internalKeys.nonDelegatedEvents]; if (!eventsMap) { eventsMap = new Map(); diff --git a/libs/horizon/src/dom/valueHandler/index.ts b/libs/horizon/src/dom/valueHandler/index.ts index ca2ba25b..88a35e5c 100644 --- a/libs/horizon/src/dom/valueHandler/index.ts +++ b/libs/horizon/src/dom/valueHandler/index.ts @@ -21,6 +21,7 @@ import { getTextareaPropsWithoutValue, updateTextareaValue, } from './TextareaValueHandler'; +import {getDomTag} from "../utils/Common"; // 获取元素除了被代理的值以外的属性 function getPropsWithoutValue(type: string, dom: HorizonDom, properties: IProperty) { @@ -72,7 +73,8 @@ function updateValue(type: string, dom: HorizonDom, properties: IProperty) { } } -function resetValue(dom: HorizonDom, type: string, properties: IProperty) { +function resetValue(dom: HorizonDom, properties: IProperty) { + const type = getDomTag(dom); switch (type) { case 'input': resetInputValue(dom, properties); diff --git a/libs/horizon/src/event/ControlledValueUpdater.ts b/libs/horizon/src/event/ControlledValueUpdater.ts index dde7f99b..5e565a1d 100644 --- a/libs/horizon/src/event/ControlledValueUpdater.ts +++ b/libs/horizon/src/event/ControlledValueUpdater.ts @@ -1,15 +1,13 @@ import {getVNodeProps} from '../dom/DOMInternalKeys'; import {resetValue} from '../dom/valueHandler'; -import {getDomTag} from '../dom/utils/Common'; -let updateList = null; +let updateList: Array | null = null; // 受控组件值重新赋值 function updateValue(target: Element) { const props = getVNodeProps(target); if (props) { - const type = getDomTag(target); - resetValue(target, type, props); + resetValue(target, props); } } diff --git a/libs/horizon/src/event/EventBinding.ts b/libs/horizon/src/event/EventBinding.ts index 068280ea..5fa168e6 100644 --- a/libs/horizon/src/event/EventBinding.ts +++ b/libs/horizon/src/event/EventBinding.ts @@ -1,20 +1,21 @@ /** - * 事件绑定实现 + * 事件绑定实现,分为绑定委托事件和非委托事件 */ import {allDelegatedNativeEvents} from './EventCollection'; import {isDocument} from '../dom/utils/Common'; import { getEventListeners, - getEventToListenerMap, + getNearestVNode, + getNonDelegatedListenerMap, } from '../dom/DOMInternalKeys'; -import {createCustomEventListener} from './WrapperListener'; import {CustomBaseEvent} from './customEvents/CustomBaseEvent'; +import {runDiscreteUpdates} from '../renderer/TreeBuilder'; +import {getEventTarget} from './utils'; +import {isMounted} from '../renderer/vnode/VNodeUtils'; +import {SuspenseComponent} from '../renderer/vnode/VNodeTags'; +import {handleEventMain} from './HorizonEventMain'; -const listeningMarker = - '_horizonListening' + - Math.random() - .toString(36) - .slice(4); +const listeningMarker = '_horizonListening' + Math.random().toString(36).slice(4); // 获取节点上已经委托事件名称 function getListenerSetKey(nativeEvtName: string, isCapture: boolean): string { @@ -22,6 +23,33 @@ function getListenerSetKey(nativeEvtName: string, isCapture: boolean): string { return `${nativeEvtName}__${sufix}`; } +// 触发委托事件 +function triggerDelegatedEvent( + nativeEvtName: string, + isCapture: boolean, + targetDom: EventTarget, + nativeEvent, // 事件对象event +) { + // 执行之前的调度事件 + runDiscreteUpdates(); + + const nativeEventTarget = getEventTarget(nativeEvent); + let targetVNode = getNearestVNode(nativeEventTarget); + + if (targetVNode !== null) { + if (isMounted(targetVNode)) { + if (targetVNode.tag === SuspenseComponent) { + targetVNode = null; + } + } else { + // vNode已销毁 + targetVNode = null; + } + } + handleEventMain(nativeEvtName, isCapture, nativeEvent, targetVNode, targetDom); +} + +// 监听委托事件 function listenToNativeEvent( nativeEvtName: string, delegatedElement: Element, @@ -37,12 +65,8 @@ function listenToNativeEvent( const listenerSetKey = getListenerSetKey(nativeEvtName, isCapture); if (!listenerSet.has(listenerSetKey)) { - const listener = createCustomEventListener( - target, - nativeEvtName, - isCapture, - ); - target.addEventListener(nativeEvtName, listener, !!isCapture); + const listener = triggerDelegatedEvent.bind(null, nativeEvtName, isCapture, target); + target.addEventListener(nativeEvtName, listener, isCapture); listenerSet.add(listenerSetKey); } } @@ -54,11 +78,11 @@ export function listenDelegatedEvents(dom: Element) { return; } dom[listeningMarker] = true; - allDelegatedNativeEvents.forEach((eventName: string) => { + allDelegatedNativeEvents.forEach((nativeEvtName: string) => { // 委托冒泡事件 - listenToNativeEvent(eventName, dom, false); + listenToNativeEvent(nativeEvtName, dom, false); // 委托捕获事件 - listenToNativeEvent(eventName, dom, true); + listenToNativeEvent(nativeEvtName, dom, true); }); } @@ -86,7 +110,7 @@ function getIsCapture(horizonEventName) { // 封装监听函数 function getWrapperListener(horizonEventName, nativeEvtName, targetElement, listener) { - return (event) => { + return event => { const customEvent = new CustomBaseEvent(horizonEventName, nativeEvtName, event, null, targetElement); listener(customEvent); }; @@ -102,19 +126,20 @@ export function listenNonDelegatedEvent( const nativeEvtName = getNativeEvtName(horizonEventName, isCapture); // 先判断是否存在老的监听事件,若存在则移除 - const eventToListenerMap = getEventToListenerMap(domElement); - if (eventToListenerMap.get(horizonEventName)) { - domElement.removeEventListener(nativeEvtName, eventToListenerMap.get(horizonEventName)); + const nonDelegatedListenerMap = getNonDelegatedListenerMap(domElement); + const currentListener = nonDelegatedListenerMap.get(horizonEventName); + if (currentListener) { + domElement.removeEventListener(nativeEvtName, currentListener); } if (typeof listener !== 'function') { - eventToListenerMap.delete(nativeEvtName); + nonDelegatedListenerMap.delete(nativeEvtName); return; } // 为了和委托事件对外行为一致,将事件对象封装成CustomBaseEvent const wrapperListener = getWrapperListener(horizonEventName, nativeEvtName, domElement, listener); // 添加新的监听 - eventToListenerMap.set(horizonEventName, wrapperListener); + nonDelegatedListenerMap.set(horizonEventName, wrapperListener); domElement.addEventListener(nativeEvtName, wrapperListener, isCapture); } diff --git a/libs/horizon/src/event/HorizonEventMain.ts b/libs/horizon/src/event/HorizonEventMain.ts index ad3dffda..fa0046ab 100644 --- a/libs/horizon/src/event/HorizonEventMain.ts +++ b/libs/horizon/src/event/HorizonEventMain.ts @@ -1,4 +1,4 @@ -import type { AnyNativeEvent, ProcessingListenerList } from './types'; +import type { AnyNativeEvent, ProcessingListenerList } from './Types'; import type { VNode } from '../renderer/Types'; import { @@ -34,12 +34,12 @@ function getCommonListeners( target: null | EventTarget, isCapture: boolean, ): ProcessingListenerList { - const customEventName = getCustomEventNameWithOn(CommonEventToHorizonMap[nativeEvtName]); - if (!customEventName) { + const horizonEvtName = getCustomEventNameWithOn(CommonEventToHorizonMap[nativeEvtName]); + if (!horizonEvtName) { return []; } - // 火狐浏览器兼容。火狐浏览器下功能键将触发keypress事件 火狐下keypress的charcode有值,keycode为0 + // 火狐浏览器兼容。火狐浏览器下功能键将触发keypress事件 火狐下keypress的charCode有值,keyCode为0 if (nativeEvtName === 'keypress' && uniqueCharCode(nativeEvent) === 0) { return []; } @@ -52,23 +52,22 @@ function getCommonListeners( if (nativeEvtName === 'focusin') { nativeEvtName = 'focus'; } + if (nativeEvtName === 'focusout') { nativeEvtName = 'blur'; } - const customEvent = createCommonCustomEvent(customEventName, nativeEvtName, nativeEvent, null, target); + const horizonEvent = createCommonCustomEvent(horizonEvtName, nativeEvtName, nativeEvent, null, target); return getListenersFromTree( vNode, - customEventName, - customEvent, + horizonEvtName, + horizonEvent, isCapture ? EVENT_TYPE_CAPTURE : EVENT_TYPE_BUBBLE, ); } // 按顺序执行事件队列 -export function processListeners( - processingEventsList: ProcessingListenerList -): void { +function processListeners(processingEventsList: ProcessingListenerList): void { processingEventsList.forEach(eventUnitList => { let lastVNode; eventUnitList.forEach(eventUnit => { @@ -88,7 +87,7 @@ export function processListeners( function getProcessListenersFacade( nativeEvtName: string, - vNode: VNode, + vNode: VNode | null, nativeEvent: AnyNativeEvent, target, isCapture: boolean @@ -150,15 +149,12 @@ function triggerHorizonEvents( nativeEvtName: string, isCapture: boolean, nativeEvent: AnyNativeEvent, - vNode: null | VNode, + vNode: VNode | null, ): void { const nativeEventTarget = getEventTarget(nativeEvent); - const processingListenerList = getProcessListenersFacade( - nativeEvtName, - vNode, - nativeEvent, - nativeEventTarget, - isCapture); + + // 获取委托事件队列 + const processingListenerList = getProcessListenersFacade(nativeEvtName, vNode, nativeEvent, nativeEventTarget, isCapture); // 处理触发的事件队列 processListeners(processingListenerList); @@ -168,6 +164,7 @@ function triggerHorizonEvents( // 其他事件正在执行中标记 let isInEventsExecution = false; +// 处理委托事件入口 export function handleEventMain( nativeEvtName: string, isCapture: boolean, @@ -192,13 +189,7 @@ export function handleEventMain( // 没有事件在执行,经过调度再执行事件 isInEventsExecution = true; try { - asyncUpdates(() => - triggerHorizonEvents( - nativeEvtName, - isCapture, - nativeEvent, - rootVNode, - )); + asyncUpdates(() => triggerHorizonEvents(nativeEvtName, isCapture, nativeEvent, rootVNode)); } finally { isInEventsExecution = false; if (shouldUpdateValue()) { diff --git a/libs/horizon/src/event/ListenerGetter.ts b/libs/horizon/src/event/ListenerGetter.ts index 3c233c48..5d9c9169 100644 --- a/libs/horizon/src/event/ListenerGetter.ts +++ b/libs/horizon/src/event/ListenerGetter.ts @@ -7,11 +7,7 @@ import {ProcessingListenerList, ListenerUnitList} from './Types'; import {CustomBaseEvent} from './customEvents/CustomBaseEvent'; // 返回是否应该阻止事件响应标记,disabled组件不响应鼠标事件 -function shouldPrevent( - name: string, - type: string, - props: Props, -): boolean { +function shouldPrevent(eventName: string, type: string, props: Props): boolean { const canPreventMouseEvents = [ 'onClick', 'onClickCapture', @@ -26,17 +22,14 @@ function shouldPrevent( 'onMouseEnter', ]; const interActiveElements = ['button', 'input', 'select', 'textarea']; - if (canPreventMouseEvents.includes(name)) { + if (canPreventMouseEvents.includes(eventName)) { return !!(props.disabled && interActiveElements.includes(type)); } return false; } // 从vnode属性中获取事件listener -function getListener( - vNode: VNode, - eventName: string, -): Function | null { +function getListener(vNode: VNode, eventName: string): Function | null { const realNode = vNode.realNode; if (realNode === null) { return null; @@ -60,14 +53,14 @@ function getListener( // 获取监听事件 export function getListenersFromTree( targetVNode: VNode | null, - name: string | null, + horizonEvtName: string | null, horizonEvent: CustomBaseEvent, eventType: string, ): ProcessingListenerList { - if (!name) { + if (!horizonEvtName) { return []; } - const captureName = name + EVENT_TYPE_CAPTURE; + const listeners: ListenerUnitList = []; let vNode = targetVNode; @@ -77,6 +70,7 @@ export function getListenersFromTree( const {realNode, tag} = vNode; if (tag === DomComponent && realNode !== null) { if (eventType === EVENT_TYPE_ALL || eventType === EVENT_TYPE_CAPTURE) { + const captureName = horizonEvtName + EVENT_TYPE_CAPTURE; const captureListener = getListener(vNode, captureName); if (captureListener) { listeners.unshift({ @@ -88,7 +82,7 @@ export function getListenersFromTree( } } if (eventType === EVENT_TYPE_ALL || eventType === EVENT_TYPE_BUBBLE) { - const bubbleListener = getListener(vNode, name); + const bubbleListener = getListener(vNode, horizonEvtName); if (bubbleListener) { listeners.push({ vNode, diff --git a/libs/horizon/src/event/WrapperListener.ts b/libs/horizon/src/event/WrapperListener.ts index ba5d309b..e69de29b 100644 --- a/libs/horizon/src/event/WrapperListener.ts +++ b/libs/horizon/src/event/WrapperListener.ts @@ -1,41 +0,0 @@ -import {isMounted} from '../renderer/vnode/VNodeUtils'; -import {SuspenseComponent} from '../renderer/vnode/VNodeTags'; -import {getNearestVNode} from '../dom/DOMInternalKeys'; -import {handleEventMain} from './HorizonEventMain'; -import {runDiscreteUpdates} from '../renderer/Renderer'; -import {getEventTarget} from './utils'; - -// 触发委托事件 -function triggerDelegatedEvent( - nativeEvtName: string, - isCapture: boolean, - targetDom: EventTarget, - nativeEvent, -) { - // 执行之前的调度事件 - runDiscreteUpdates(); - - const nativeEventTarget = getEventTarget(nativeEvent); - let targetVNode = getNearestVNode(nativeEventTarget); - - if (targetVNode !== null) { - if (isMounted(targetVNode)) { - if (targetVNode.tag === SuspenseComponent) { - targetVNode = null; - } - } else { - // vnode已销毁 - targetVNode = null; - } - } - handleEventMain(nativeEvtName, isCapture, nativeEvent, targetVNode, targetDom); -} - -// 生成委托事件的监听方法 -export function createCustomEventListener( - target: EventTarget, - nativeEvtName: string, - isCapture: boolean, -): EventListener { - return triggerDelegatedEvent.bind(null, nativeEvtName, isCapture, target); -} diff --git a/libs/horizon/src/event/customEvents/CustomBaseEvent.ts b/libs/horizon/src/event/customEvents/CustomBaseEvent.ts index e58b91c4..ddede323 100644 --- a/libs/horizon/src/event/customEvents/CustomBaseEvent.ts +++ b/libs/horizon/src/event/customEvents/CustomBaseEvent.ts @@ -67,7 +67,7 @@ export class CustomBaseEvent { customEvtName: string | null, nativeEvtName: string, nativeEvt: { [propName: string]: any }, - vNode: VNode, + vNode: VNode | null, target: null | EventTarget ) { // 复制原生属性到自定义事件 diff --git a/libs/horizon/src/event/utils.ts b/libs/horizon/src/event/utils.ts index a23e52da..182616a8 100644 --- a/libs/horizon/src/event/utils.ts +++ b/libs/horizon/src/event/utils.ts @@ -50,7 +50,5 @@ export function getCustomEventNameWithOn(name) { if (!name) { return ''; } - const capitalizedEvent = name[0].toUpperCase() + name.slice(1); - const horizonEventName = 'on' + capitalizedEvent; - return horizonEventName; + return 'on' + name[0].toUpperCase() + name.slice(1); } diff --git a/libs/horizon/src/renderer/ExecuteMode.ts b/libs/horizon/src/renderer/ExecuteMode.ts index 68c6f2c1..490c8595 100644 --- a/libs/horizon/src/renderer/ExecuteMode.ts +++ b/libs/horizon/src/renderer/ExecuteMode.ts @@ -2,14 +2,16 @@ export const ByAsync = 'BY_ASYNC'; export const BySync = 'BY_SYNC'; export const InRender = 'IN_RENDER'; +export const InEvent = 'IN_EVENT'; -type RenderMode = typeof ByAsync | typeof BySync | typeof InRender; +type RenderMode = typeof ByAsync | typeof BySync | typeof InRender | typeof InEvent; // 当前执行模式标记 let executeMode = { [ByAsync]: false, [BySync]: false, [InRender]: false, + [InEvent]: false, }; export function changeMode(mode: RenderMode, state = true) { @@ -21,7 +23,7 @@ export function checkMode(mode: RenderMode) { } export function isExecuting() { - return executeMode[ByAsync] || executeMode[BySync] || executeMode[InRender]; + return executeMode[ByAsync] || executeMode[BySync] || executeMode[InRender] || executeMode[InEvent]; } export function copyExecuteMode() { diff --git a/libs/horizon/src/renderer/TreeBuilder.ts b/libs/horizon/src/renderer/TreeBuilder.ts index ff7dd150..cf0bc5e3 100644 --- a/libs/horizon/src/renderer/TreeBuilder.ts +++ b/libs/horizon/src/renderer/TreeBuilder.ts @@ -22,10 +22,11 @@ import { findDomParent, getSiblingVNode } from './vnode/VNodeUtils'; import { ByAsync, BySync, + InRender, + InEvent, changeMode, checkMode, copyExecuteMode, - InRender, isExecuting, setExecuteMode } from './ExecuteMode'; @@ -331,7 +332,7 @@ export function runDiscreteUpdates() { export function asyncUpdates(fn, ...param) { const preMode = copyExecuteMode(); - changeMode(ByAsync, true); + changeMode(InEvent, true); try { return fn(...param); } finally {