diff --git a/libs/horizon/src/dom/DOMExternal.ts b/libs/horizon/src/dom/DOMExternal.ts index 077d0801..02f11e53 100644 --- a/libs/horizon/src/dom/DOMExternal.ts +++ b/libs/horizon/src/dom/DOMExternal.ts @@ -23,7 +23,7 @@ function createRoot(children: any, container: Container, callback?: Callback) { container._treeRoot = treeRoot; // 根节点挂接全量事件 - listenDelegatedEvents(container as Element); + // listenDelegatedEvents(container as Element); // 执行回调 if (typeof callback === 'function') { diff --git a/libs/horizon/src/dom/DOMPropertiesHandler/DOMPropertiesHandler.ts b/libs/horizon/src/dom/DOMPropertiesHandler/DOMPropertiesHandler.ts index c7f76f2f..1ac626a0 100644 --- a/libs/horizon/src/dom/DOMPropertiesHandler/DOMPropertiesHandler.ts +++ b/libs/horizon/src/dom/DOMPropertiesHandler/DOMPropertiesHandler.ts @@ -4,9 +4,11 @@ import { import { updateCommonProp } from './UpdateCommonProp'; import { setStyles } from './StyleHandler'; import { - listenNonDelegatedEvent + lazyDelegateOnRoot, + listenNonDelegatedEvent, } from '../../event/EventBinding'; import { isEventProp } from '../validators/ValidateProps'; +import { getCurrentRoot } from '../../renderer/TreeBuilder'; // 初始化DOM属性和更新 DOM 属性 export function setDomProps( @@ -27,8 +29,12 @@ export function setDomProps( setStyles(dom, propVal); } else if (isEventProp(propName)) { // 事件监听属性处理 + // TODO + const currentRoot = getCurrentRoot(); if (!allDelegatedHorizonEvents.has(propName)) { listenNonDelegatedEvent(propName, dom, propVal); + } else if (currentRoot && !currentRoot.delegatedEvents.has(propName)) { + lazyDelegateOnRoot(currentRoot, propName); } } else if (propName === 'children') { // 只处理纯文本子节点,其他children在VNode树中处理 const type = typeof propVal; @@ -147,6 +153,7 @@ export function compareProps( if (!allDelegatedHorizonEvents.has(propName)) { toUpdateProps[propName] = newPropValue; } + // TODO } else { toUpdateProps[propName] = newPropValue; } diff --git a/libs/horizon/src/dom/valueHandler/SelectValueHandler.ts b/libs/horizon/src/dom/valueHandler/SelectValueHandler.ts index 75812620..d7c5852f 100644 --- a/libs/horizon/src/dom/valueHandler/SelectValueHandler.ts +++ b/libs/horizon/src/dom/valueHandler/SelectValueHandler.ts @@ -43,7 +43,7 @@ export function getSelectPropsWithoutValue(dom: HorizonSelect, properties: Objec return { ...properties, value: undefined, - } + }; } export function updateSelectValue(dom: HorizonSelect, properties: IProperty, isInit: boolean = false) { diff --git a/libs/horizon/src/event/EventBinding.ts b/libs/horizon/src/event/EventBinding.ts index 54d3fdb4..66bf94a7 100644 --- a/libs/horizon/src/event/EventBinding.ts +++ b/libs/horizon/src/event/EventBinding.ts @@ -1,7 +1,7 @@ /** * 事件绑定实现,分为绑定委托事件和非委托事件 */ -import {allDelegatedNativeEvents} from './EventCollection'; +import { allDelegatedHorizonEvents, allDelegatedNativeEvents } from './EventCollection'; import {isDocument} from '../dom/utils/Common'; import { getNearestVNode, @@ -12,6 +12,7 @@ import {isMounted} from '../renderer/vnode/VNodeUtils'; import {SuspenseComponent} from '../renderer/vnode/VNodeTags'; import {handleEventMain} from './HorizonEventMain'; import {decorateNativeEvent} from './customEvents/EventFactory'; +import { VNode } from '../renderer/vnode/VNode'; const listeningMarker = '_horizonListening' + Math.random().toString(36).slice(4); @@ -26,18 +27,20 @@ function triggerDelegatedEvent( runDiscreteUpdates(); const nativeEventTarget = nativeEvent.target || nativeEvent.srcElement; - let targetVNode = getNearestVNode(nativeEventTarget); + const targetVNode = getNearestVNode(nativeEventTarget); - if (targetVNode !== null) { - if (isMounted(targetVNode)) { - if (targetVNode.tag === SuspenseComponent) { - targetVNode = null; - } - } else { - // vNode已销毁 - targetVNode = null; - } - } + // if (targetVNode !== null) { + // if (isMounted(targetVNode)) { + // if (targetVNode.tag === SuspenseComponent) { + // debugger + // targetVNode = null; + // } + // } else { + // debugger + // // vNode已销毁 + // targetVNode = null; + // } + // } handleEventMain(nativeEvtName, isCapture, nativeEvent, targetVNode, targetDom); } @@ -73,6 +76,16 @@ export function listenDelegatedEvents(dom: Element) { }); } +// 事件懒委托,当用户定义事件后,再进行委托到根节点 +export function lazyDelegateOnRoot(currentRoot: VNode, eventName: string) { + currentRoot.delegatedEvents.add(eventName); + + const isCapture = isCaptureEvent(eventName); + const nativeEvents = allDelegatedHorizonEvents.get(eventName); + nativeEvents.forEach(nativeEvents => { + listenToNativeEvent(nativeEvents, currentRoot.realNode, isCapture); + }); +} // 通过horizon事件名获取到native事件名 function getNativeEvtName(horizonEventName, capture) { let nativeName; diff --git a/libs/horizon/src/event/HorizonEventMain.ts b/libs/horizon/src/event/HorizonEventMain.ts index f0480724..35db487b 100644 --- a/libs/horizon/src/event/HorizonEventMain.ts +++ b/libs/horizon/src/event/HorizonEventMain.ts @@ -8,7 +8,6 @@ import { EVENT_TYPE_CAPTURE, } from './const'; import { getListeners as getChangeListeners } from './simulatedEvtHandler/ChangeEventHandler'; -import { getListeners as getSelectionListeners } from './simulatedEvtHandler/SelectionEventHandler'; import { setPropertyWritable, } from './utils'; @@ -81,6 +80,7 @@ function getProcessListeners( target, isCapture: boolean, ): ListenerUnitList { + // TODO 重复从树中获取监听器 // 触发普通委托事件 let listenerList: ListenerUnitList = getCommonListeners( nativeEvtName, @@ -100,15 +100,6 @@ function getProcessListeners( target, )); } - - if (horizonEventToNativeMap.get('onSelect').includes(nativeEvtName)) { - listenerList = listenerList.concat(getSelectionListeners( - nativeEvtName, - nativeEvent, - vNode, - target, - )); - } } return listenerList; } diff --git a/libs/horizon/src/event/const.ts b/libs/horizon/src/event/const.ts index 449663e9..c0e185a4 100644 --- a/libs/horizon/src/event/const.ts +++ b/libs/horizon/src/event/const.ts @@ -28,8 +28,7 @@ export const horizonEventToNativeMap = new Map([ ['onCompositionStart', ['compositionstart']], ['onCompositionUpdate', ['compositionupdate']], ['onChange', ['change', 'click', 'focusout', 'input']], - ['onSelect', ['focusout', 'contextmenu', 'dragend', 'focusin', - 'keydown', 'keyup', 'mousedown', 'mouseup', 'selectionchange']], + ['onSelect', ['select']], ['onAnimationEnd', ['animationend']], ['onAnimationIteration', ['animationiteration']], diff --git a/libs/horizon/src/event/simulatedEvtHandler/ChangeEventHandler.ts b/libs/horizon/src/event/simulatedEvtHandler/ChangeEventHandler.ts index c0ad947d..f1b781af 100644 --- a/libs/horizon/src/event/simulatedEvtHandler/ChangeEventHandler.ts +++ b/libs/horizon/src/event/simulatedEvtHandler/ChangeEventHandler.ts @@ -12,6 +12,11 @@ import {VNode} from '../../renderer/Types'; import {getDomTag} from '../../dom/utils/Common'; // 返回是否需要触发change事件标记 +// | 元素 | 事件 | 需要值变更 | +// | --- | --- | --------------- | +// | | click | YES | +// | | input / change | YES | function shouldTriggerChangeEvent(targetDom, evtName) { const { type } = targetDom; const domTag = getDomTag(targetDom); diff --git a/libs/horizon/src/event/utils.ts b/libs/horizon/src/event/utils.ts index 09d1b240..37ec6218 100644 --- a/libs/horizon/src/event/utils.ts +++ b/libs/horizon/src/event/utils.ts @@ -1,9 +1,7 @@ export function isInputElement(dom?: HTMLElement): boolean { - if (dom instanceof HTMLInputElement || dom instanceof HTMLTextAreaElement) { - return true; - } - return false; + return dom instanceof HTMLInputElement || dom instanceof HTMLTextAreaElement; + } export function setPropertyWritable(obj, propName) { diff --git a/libs/horizon/src/renderer/TreeBuilder.ts b/libs/horizon/src/renderer/TreeBuilder.ts index 12626215..32bc6c14 100644 --- a/libs/horizon/src/renderer/TreeBuilder.ts +++ b/libs/horizon/src/renderer/TreeBuilder.ts @@ -43,6 +43,11 @@ let unrecoverableErrorDuringBuild: any = null; // 当前运行的vNode节点 let processing: VNode | null = null; +let currentRoot: VNode | null = null; +export function getCurrentRoot() { + return currentRoot; +} + export function setProcessing(vNode: VNode | null) { processing = vNode; } @@ -267,7 +272,7 @@ function buildVNodeTree(treeRoot: VNode) { // 总体任务入口 function renderFromRoot(treeRoot) { runAsyncEffects(); - + currentRoot = treeRoot; // 1. 构建vNode树 buildVNodeTree(treeRoot); @@ -278,6 +283,7 @@ function renderFromRoot(treeRoot) { // 2. 提交变更 submitToRender(treeRoot); + currentRoot = null; if (window.__HORIZON_DEV_HOOK__) { const hook = window.__HORIZON_DEV_HOOK__; diff --git a/libs/horizon/src/renderer/submit/Submit.ts b/libs/horizon/src/renderer/submit/Submit.ts index e80f911a..d19dfea2 100644 --- a/libs/horizon/src/renderer/submit/Submit.ts +++ b/libs/horizon/src/renderer/submit/Submit.ts @@ -114,21 +114,21 @@ function submit(dirtyNodes: Array) { if ((node.flags & ResetText) === ResetText) { submitResetTextContent(node); } - + if ((node.flags & Ref) === Ref) { if (!node.isCreated) { // 需要执行 detachRef(node, true); } } - + isAdd = (node.flags & Addition) === Addition; isUpdate = (node.flags & Update) === Update; if (isAdd && isUpdate) { // Addition submitAddition(node); FlagUtils.removeFlag(node, Addition); - + // Update submitUpdate(node); } else { @@ -161,7 +161,7 @@ function afterSubmit(dirtyNodes: Array) { if ((node.flags & Update) === Update || (node.flags & Callback) === Callback) { callAfterSubmitLifeCycles(node); } - + if ((node.flags & Ref) === Ref) { attachRef(node); } diff --git a/libs/horizon/src/renderer/vnode/VNode.ts b/libs/horizon/src/renderer/vnode/VNode.ts index d3e6c23a..d67f0e73 100644 --- a/libs/horizon/src/renderer/vnode/VNode.ts +++ b/libs/horizon/src/renderer/vnode/VNode.ts @@ -74,7 +74,10 @@ export class VNode { suspenseState: SuspenseState; path = ''; // 保存从根到本节点的路径 + + // 根节点数据 toUpdateNodes: Set | null; // 保存要更新的节点 + delegatedEvents: Set belongClassVNode: VNode | null = null; // 记录JSXElement所属class vNode,处理ref的时候使用 @@ -94,6 +97,7 @@ export class VNode { this.realNode = realNode; this.task = null; this.toUpdateNodes = new Set(); + this.delegatedEvents = new Set(); this.updates = null; this.stateCallbacks = null; this.state = null;