From 9c3985bccd84f6d4c16cdaa59aa8f659c3681346 Mon Sep 17 00:00:00 2001 From: * <8> Date: Fri, 21 Jan 2022 14:57:00 +0800 Subject: [PATCH 1/4] Match-id-933351a5e5fc787d4d40d8a208c8d4c97281c275 --- libs/horizon/src/dom/DOMInternalKeys.ts | 2 +- .../src/event/ControlledValueUpdater.ts | 2 +- libs/horizon/src/event/EventBinding.ts | 35 ++++++++----------- libs/horizon/src/event/HorizonEventMain.ts | 29 ++++++--------- libs/horizon/src/event/ListenerGetter.ts | 22 +++++------- libs/horizon/src/event/WrapperListener.ts | 4 +-- libs/horizon/src/event/utils.ts | 4 +-- 7 files changed, 38 insertions(+), 60 deletions(-) diff --git a/libs/horizon/src/dom/DOMInternalKeys.ts b/libs/horizon/src/dom/DOMInternalKeys.ts index 3b59dd2d..068eb829 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 = target[internalKeys.nonDelegatedEvents] = new Map(); diff --git a/libs/horizon/src/event/ControlledValueUpdater.ts b/libs/horizon/src/event/ControlledValueUpdater.ts index dde7f99b..417d9e13 100644 --- a/libs/horizon/src/event/ControlledValueUpdater.ts +++ b/libs/horizon/src/event/ControlledValueUpdater.ts @@ -2,7 +2,7 @@ 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) { diff --git a/libs/horizon/src/event/EventBinding.ts b/libs/horizon/src/event/EventBinding.ts index 068280ea..ee56bda4 100644 --- a/libs/horizon/src/event/EventBinding.ts +++ b/libs/horizon/src/event/EventBinding.ts @@ -5,16 +5,12 @@ import {allDelegatedNativeEvents} from './EventCollection'; import {isDocument} from '../dom/utils/Common'; import { getEventListeners, - getEventToListenerMap, + getNonDelegatedListenerMap, } from '../dom/DOMInternalKeys'; import {createCustomEventListener} from './WrapperListener'; import {CustomBaseEvent} from './customEvents/CustomBaseEvent'; -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 { @@ -37,12 +33,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 = createCustomEventListener(target, nativeEvtName, isCapture); + target.addEventListener(nativeEvtName, listener, isCapture); listenerSet.add(listenerSetKey); } } @@ -54,11 +46,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 +78,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 +94,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 1ee6ba8b..0b861c26 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 => { @@ -148,7 +147,7 @@ function triggerHorizonEvents( nativeEvtName: string, isCapture: boolean, nativeEvent: AnyNativeEvent, - vNode: null | VNode, + vNode: VNode, ): void { const nativeEventTarget = getEventTarget(nativeEvent); const processingListenerList = getProcessListenersFacade(nativeEvtName, vNode, nativeEvent, nativeEventTarget, isCapture); @@ -184,13 +183,7 @@ export function handleEventMain( // 没有事件在执行,经过调度再执行事件 isInEventsExecution = true; try { - return asyncUpdates(() => - triggerHorizonEvents( - nativeEvtName, - isCapture, - nativeEvent, - rootVNode, - )); + return 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 9c73299c..0c0f0a6a 100644 --- a/libs/horizon/src/event/WrapperListener.ts +++ b/libs/horizon/src/event/WrapperListener.ts @@ -19,7 +19,7 @@ function triggerDelegatedEvent( nativeEvtName: string, isCapture: boolean, targetDom: EventTarget, - nativeEvent, + nativeEvent, // 事件对象event ) { // 执行之前的调度事件 runDiscreteUpdates(); @@ -33,7 +33,7 @@ function triggerDelegatedEvent( targetVNode = null; } } else { - // vnode已销毁 + // vNode已销毁 targetVNode = null; } } 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); } From 979ffbdddfc46dcc8cfb54e81a784f52c8ed9280 Mon Sep 17 00:00:00 2001 From: * <8> Date: Thu, 27 Jan 2022 16:33:26 +0800 Subject: [PATCH 2/4] Match-id-dc205a4168fcc04ddea513da53c95b9241cd320d --- libs/horizon/src/dom/valueHandler/index.ts | 4 +- .../src/event/ControlledValueUpdater.ts | 4 +- libs/horizon/src/event/EventBinding.ts | 38 +++++++++++++++-- libs/horizon/src/event/HorizonEventMain.ts | 7 +++- libs/horizon/src/event/WrapperListener.ts | 41 ------------------- .../src/event/customEvents/CustomBaseEvent.ts | 2 +- libs/horizon/src/renderer/ExecuteMode.ts | 6 ++- libs/horizon/src/renderer/TreeBuilder.ts | 5 ++- 8 files changed, 52 insertions(+), 55 deletions(-) delete mode 100644 libs/horizon/src/event/WrapperListener.ts 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 417d9e13..5e565a1d 100644 --- a/libs/horizon/src/event/ControlledValueUpdater.ts +++ b/libs/horizon/src/event/ControlledValueUpdater.ts @@ -1,6 +1,5 @@ import {getVNodeProps} from '../dom/DOMInternalKeys'; import {resetValue} from '../dom/valueHandler'; -import {getDomTag} from '../dom/utils/Common'; let updateList: Array | null = null; @@ -8,8 +7,7 @@ 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 ee56bda4..5fa168e6 100644 --- a/libs/horizon/src/event/EventBinding.ts +++ b/libs/horizon/src/event/EventBinding.ts @@ -1,14 +1,19 @@ /** - * 事件绑定实现 + * 事件绑定实现,分为绑定委托事件和非委托事件 */ import {allDelegatedNativeEvents} from './EventCollection'; import {isDocument} from '../dom/utils/Common'; import { getEventListeners, + 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); @@ -18,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, @@ -33,7 +65,7 @@ function listenToNativeEvent( const listenerSetKey = getListenerSetKey(nativeEvtName, isCapture); if (!listenerSet.has(listenerSetKey)) { - const listener = createCustomEventListener(target, nativeEvtName, isCapture); + const listener = triggerDelegatedEvent.bind(null, nativeEvtName, isCapture, target); target.addEventListener(nativeEvtName, listener, isCapture); listenerSet.add(listenerSetKey); } diff --git a/libs/horizon/src/event/HorizonEventMain.ts b/libs/horizon/src/event/HorizonEventMain.ts index 0b861c26..d80e3028 100644 --- a/libs/horizon/src/event/HorizonEventMain.ts +++ b/libs/horizon/src/event/HorizonEventMain.ts @@ -87,7 +87,7 @@ function processListeners(processingEventsList: ProcessingListenerList): void { function getProcessListenersFacade( nativeEvtName: string, - vNode: VNode, + vNode: VNode | null, nativeEvent: AnyNativeEvent, target, isCapture: boolean @@ -147,9 +147,11 @@ function triggerHorizonEvents( nativeEvtName: string, isCapture: boolean, nativeEvent: AnyNativeEvent, - vNode: VNode, + vNode: VNode | null, ): void { const nativeEventTarget = getEventTarget(nativeEvent); + + // 获取委托事件队列 const processingListenerList = getProcessListenersFacade(nativeEvtName, vNode, nativeEvent, nativeEventTarget, isCapture); // 处理触发的事件队列 @@ -160,6 +162,7 @@ function triggerHorizonEvents( // 其他事件正在执行中标记 let isInEventsExecution = false; +// 处理委托事件入口 export function handleEventMain( nativeEvtName: string, isCapture: boolean, diff --git a/libs/horizon/src/event/WrapperListener.ts b/libs/horizon/src/event/WrapperListener.ts deleted file mode 100644 index 0c0f0a6a..00000000 --- a/libs/horizon/src/event/WrapperListener.ts +++ /dev/null @@ -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'; - -// 生成委托事件的监听方法 -export function createCustomEventListener( - target: EventTarget, - nativeEvtName: string, - isCapture: boolean, -): EventListener { - return triggerDelegatedEvent.bind(null, nativeEvtName, isCapture, target); -} - -// 触发委托事件 -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); -} diff --git a/libs/horizon/src/event/customEvents/CustomBaseEvent.ts b/libs/horizon/src/event/customEvents/CustomBaseEvent.ts index 0ebce67a..ab0a339c 100644 --- a/libs/horizon/src/event/customEvents/CustomBaseEvent.ts +++ b/libs/horizon/src/event/customEvents/CustomBaseEvent.ts @@ -22,7 +22,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/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 a84b5c3d..07b3afff 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'; @@ -329,7 +330,7 @@ export function runDiscreteUpdates() { export function asyncUpdates(fn, ...param) { const preMode = copyExecuteMode(); - changeMode(ByAsync, true); + changeMode(InEvent, true); try { return fn(...param); } finally { From 06752cdf279c67992600d66bb1108c8f4748d602 Mon Sep 17 00:00:00 2001 From: * <8> Date: Thu, 27 Jan 2022 17:47:31 +0800 Subject: [PATCH 3/4] Match-id-a3b867276dc7741860be264419d6379d8d182100 --- libs/horizon/src/dom/DOMExternal.ts | 6 +- libs/horizon/src/renderer/Renderer.ts | 4 +- .../src/renderer/diff/nodeDiffComparator.ts | 16 ++-- .../src/renderer/render/MemoComponent.ts | 7 +- .../src/renderer/render/SuspenseComponent.ts | 9 +- .../src/renderer/vnode/VNodeCreator.ts | 88 +++++++++++-------- 6 files changed, 70 insertions(+), 60 deletions(-) diff --git a/libs/horizon/src/dom/DOMExternal.ts b/libs/horizon/src/dom/DOMExternal.ts index e9404cb7..077d0801 100644 --- a/libs/horizon/src/dom/DOMExternal.ts +++ b/libs/horizon/src/dom/DOMExternal.ts @@ -1,13 +1,13 @@ import { - asyncUpdates, createVNode, getFirstCustomDom, + asyncUpdates, getFirstCustomDom, syncUpdates, startUpdate, + createTreeRootVNode, } from '../renderer/Renderer'; import {createPortal} from '../renderer/components/CreatePortal'; import type {Container} from './DOMOperator'; import {isElement} from './utils/Common'; import {listenDelegatedEvents} from '../event/EventBinding'; import {findDOMByClassInst} from '../renderer/vnode/VNodeUtils'; -import {TreeRoot} from '../renderer/vnode/VNodeTags'; import {Callback} from '../renderer/UpdateHandler'; function createRoot(children: any, container: Container, callback?: Callback) { @@ -19,7 +19,7 @@ function createRoot(children: any, container: Container, callback?: Callback) { } // 调度器创建根节点,并给容器dom赋vNode结构体 - const treeRoot = createVNode(TreeRoot, container); + const treeRoot = createTreeRootVNode(container); container._treeRoot = treeRoot; // 根节点挂接全量事件 diff --git a/libs/horizon/src/renderer/Renderer.ts b/libs/horizon/src/renderer/Renderer.ts index d5cf89da..8928805a 100644 --- a/libs/horizon/src/renderer/Renderer.ts +++ b/libs/horizon/src/renderer/Renderer.ts @@ -9,9 +9,9 @@ import { } from './TreeBuilder'; import { runAsyncEffects } from './submit/HookEffectHandler'; import { Callback, newUpdate, pushUpdate } from './UpdateHandler'; -import { getFirstChild } from './vnode/VNodeUtils'; -export { createVNode } from './vnode/VNodeCreator'; + +export { createVNode, createTreeRootVNode } from './vnode/VNodeCreator'; export { createPortal } from './components/CreatePortal'; export { asyncUpdates, diff --git a/libs/horizon/src/renderer/diff/nodeDiffComparator.ts b/libs/horizon/src/renderer/diff/nodeDiffComparator.ts index 09624161..797687b5 100644 --- a/libs/horizon/src/renderer/diff/nodeDiffComparator.ts +++ b/libs/horizon/src/renderer/diff/nodeDiffComparator.ts @@ -2,7 +2,7 @@ import type { VNode } from '../Types'; import { FlagUtils } from '../vnode/VNodeFlags'; import { TYPE_COMMON_ELEMENT, TYPE_FRAGMENT, TYPE_PORTAL } from '../../external/JSXElementType'; import { DomText, DomPortal, Fragment, DomComponent } from '../vnode/VNodeTags'; -import {updateVNode, createVNode, createVNodeFromElement, updateVNodePath} from '../vnode/VNodeCreator'; +import {updateVNode, createVNodeFromElement, updateVNodePath, createFragmentVNode, createPortalVNode, createDomTextVNode} from '../vnode/VNodeCreator'; import { isSameType, getIteratorFn, @@ -113,7 +113,7 @@ function getNewNode(parentNode: VNode, newChild: any, oldNode: VNode | null) { switch (newNodeType) { case DiffCategory.TEXT_NODE: { if (oldNode === null || oldNode.tag !== DomText) { - resultNode = createVNode(DomText, String(newChild)); + resultNode = createDomTextVNode(String(newChild)); } else { resultNode = updateVNode(oldNode, String(newChild)); } @@ -121,7 +121,7 @@ function getNewNode(parentNode: VNode, newChild: any, oldNode: VNode | null) { } case DiffCategory.ARR_NODE: { if (oldNode === null || oldNode.tag !== Fragment) { - resultNode = createVNode(Fragment, null, newChild); + resultNode = createFragmentVNode(null, newChild); } else { resultNode = updateVNode(oldNode, newChild); } @@ -132,7 +132,7 @@ function getNewNode(parentNode: VNode, newChild: any, oldNode: VNode | null) { if (newChild.type === TYPE_FRAGMENT) { if (oldNode === null || oldNode.tag !== Fragment) { const key = oldNode !== null ? oldNode.key : newChild.key; - resultNode = createVNode(Fragment, key, newChild.props.children); + resultNode = createFragmentVNode(key, newChild.props.children); } else { resultNode = updateVNode(oldNode, newChild); } @@ -151,7 +151,7 @@ function getNewNode(parentNode: VNode, newChild: any, oldNode: VNode | null) { break; } else if (newChild.vtype === TYPE_PORTAL) { if (oldNode === null || oldNode.tag !== DomPortal || oldNode.outerDom !== newChild.outerDom) { - resultNode = createVNode(DomPortal, newChild); + resultNode = createPortalVNode(newChild); } else { resultNode = updateVNode(oldNode, newChild.children || []); } @@ -504,7 +504,7 @@ function diffStringNodeHandler( deleteVNodes(parentNode, firstChildVNode.next); newTextNode.next = null; } else { - newTextNode = createVNode(DomText, String(newChild)); + newTextNode = createDomTextVNode(String(newChild)); deleteVNodes(parentNode, firstChildVNode); } @@ -562,7 +562,7 @@ function diffObjectNodeHandler( if (resultNode === null) { // 新建 if (newChild.type === TYPE_FRAGMENT) { - resultNode = createVNode(Fragment, newChild.key, newChild.props.children); + resultNode = createFragmentVNode(newChild.key, newChild.props.children); } else { resultNode = createVNodeFromElement(newChild); resultNode.ref = newChild.ref; @@ -580,7 +580,7 @@ function diffObjectNodeHandler( } if (resultNode === null) { // 新建 - resultNode = createVNode(DomPortal, newChild); + resultNode = createPortalVNode(newChild); } } diff --git a/libs/horizon/src/renderer/render/MemoComponent.ts b/libs/horizon/src/renderer/render/MemoComponent.ts index d4a10d22..a0cd75f3 100644 --- a/libs/horizon/src/renderer/render/MemoComponent.ts +++ b/libs/horizon/src/renderer/render/MemoComponent.ts @@ -1,14 +1,13 @@ import type {VNode} from '../Types'; import {mergeDefaultProps} from './LazyComponent'; -import {updateVNode, createVNode, onlyUpdateChildVNodes, updateVNodePath} from '../vnode/VNodeCreator'; +import {updateVNode, onlyUpdateChildVNodes, updateVNodePath, createFragmentVNode, createUndeterminedVNode} from '../vnode/VNodeCreator'; import {shallowCompare} from '../utils/compare'; import { TYPE_FRAGMENT, TYPE_PROFILER, TYPE_STRICT_MODE, } from '../../external/JSXElementType'; -import {Fragment} from '../vnode/VNodeTags'; export function bubbleRender() {} @@ -24,9 +23,9 @@ export function captureMemoComponent( let newChild = null; const type = Component.type; if (type === TYPE_STRICT_MODE || type === TYPE_FRAGMENT || type === TYPE_PROFILER) { - newChild = createVNode(Fragment, null, newProps.children); + newChild = createFragmentVNode(null, newProps.children); } else { - newChild = createVNode('props', type, null, newProps, processing); + newChild = createUndeterminedVNode(type, null, newProps); } newChild.parent = processing; newChild.ref = processing.ref; diff --git a/libs/horizon/src/renderer/render/SuspenseComponent.ts b/libs/horizon/src/renderer/render/SuspenseComponent.ts index b601a551..c2d6773b 100644 --- a/libs/horizon/src/renderer/render/SuspenseComponent.ts +++ b/libs/horizon/src/renderer/render/SuspenseComponent.ts @@ -1,12 +1,11 @@ import type {VNode, PromiseType} from '../Types'; import {FlagUtils, Interrupted} from '../vnode/VNodeFlags'; -import {createVNode, onlyUpdateChildVNodes, updateVNode, updateVNodePath} from '../vnode/VNodeCreator'; +import {onlyUpdateChildVNodes, updateVNode, updateVNodePath, createFragmentVNode} from '../vnode/VNodeCreator'; import { ClassComponent, IncompleteClassComponent, SuspenseComponent, - Fragment, } from '../vnode/VNodeTags'; import {pushForceUpdate} from '../UpdateHandler'; import {launchUpdateFromVNode, tryRenderFromRoot} from '../TreeBuilder'; @@ -31,12 +30,12 @@ function createFallback(processing: VNode, fallbackChildren) { if (oldFallbackFragment !== null) { fallbackFragment = updateVNode(oldFallbackFragment, fallbackChildren); } else { - fallbackFragment = createVNode(Fragment, null, fallbackChildren); + fallbackFragment = createFragmentVNode(null, fallbackChildren); FlagUtils.markAddition(fallbackFragment); } } else { // 创建 - fallbackFragment = createVNode(Fragment, null, fallbackChildren); + fallbackFragment = createFragmentVNode(null, fallbackChildren); } processing.child = childFragment; @@ -72,7 +71,7 @@ function createSuspenseChildren(processing: VNode, newChildren) { // SuspenseComponent 中使用 processing.suspenseChildStatus = SuspenseChildStatus.ShowChild; } else { - childFragment = createVNode(Fragment, null, newChildren); + childFragment = createFragmentVNode(null, newChildren); } childFragment.parent = processing; diff --git a/libs/horizon/src/renderer/vnode/VNodeCreator.ts b/libs/horizon/src/renderer/vnode/VNodeCreator.ts index 5feb31f4..11abe1b1 100644 --- a/libs/horizon/src/renderer/vnode/VNodeCreator.ts +++ b/libs/horizon/src/renderer/vnode/VNodeCreator.ts @@ -87,59 +87,71 @@ export function updateVNode(vNode: VNode, vNodeProps?: any): VNode { function getVNodeTag(type: any) { let vNodeTag = ClsOrFunComponent; let isLazy = false; + const componentType = typeof type; - if (typeof type === 'function') { + if (componentType === 'function') { if (isClassComponent(type)) { vNodeTag = ClassComponent; } - } else if (typeof type === 'string') { + } else if (componentType === 'string') { vNodeTag = DomComponent; } else if (type === TYPE_SUSPENSE) { vNodeTag = SuspenseComponent; - } else if (typeof type === 'object' && type !== null && typeMap[type.vtype]) { + } else if (componentType === 'object' && type !== null && typeMap[type.vtype]) { vNodeTag = typeMap[type.vtype]; isLazy = type.vtype === TYPE_LAZY; } else { - throw Error(`Component type is invalid, got: ${type == null ? type : typeof type}`); + throw Error(`Component type is invalid, got: ${type == null ? type : componentType}`); } return { vNodeTag, isLazy }; } +export function createFragmentVNode(fragmentKey, fragmentProps) { + const vNode = newVirtualNode(Fragment, fragmentKey, fragmentProps); + vNode.shouldUpdate = true; + return vNode; +} + +export function createDomTextVNode(content) { + const vNode = newVirtualNode(DomText, null, content); + vNode.shouldUpdate = true; + return vNode; +} + +export function createPortalVNode(portal) { + const children = portal.children ?? []; + const vNode = newVirtualNode(DomPortal, portal.key, children); + vNode.shouldUpdate = true; + vNode.outerDom = portal.outerDom; + return vNode; +} + +export function createUndeterminedVNode(type, key, props) { + const { vNodeTag, isLazy } = getVNodeTag(type); + + const vNode = newVirtualNode(vNodeTag, key, props); + vNode.type = type; + vNode.shouldUpdate = true; + + // lazy类型的特殊处理 + vNode.isLazyComponent = isLazy; + if (isLazy) { + vNode.lazyType = type; + } + return vNode; +} + +export function createTreeRootVNode(container) { + const vNode = newVirtualNode(TreeRoot, null, null, container); + vNode.path.push(0); + createUpdateArray(vNode); + return vNode; +} + +// TODO: 暂时保留给测试用例使用,后续修改测试用例 export function createVNode(tag: VNodeTag | string, ...secondArg) { let vNode = null; switch (tag) { - case Fragment: - const [fragmentKey, fragmentProps] = secondArg; - vNode = newVirtualNode(Fragment, fragmentKey, fragmentProps); - vNode.shouldUpdate = true; - break; - case DomText: - const content = secondArg[0]; - vNode = newVirtualNode(DomText, null, content); - vNode.shouldUpdate = true; - break; - case DomPortal: - const portal = secondArg[0]; - const children = portal.children ?? []; - vNode = newVirtualNode(DomPortal, portal.key, children); - vNode.shouldUpdate = true; - vNode.outerDom = portal.outerDom; - break; - case 'props': - const [type, key, props] = secondArg; - - const { vNodeTag, isLazy } = getVNodeTag(type); - - vNode = newVirtualNode(vNodeTag, key, props); - vNode.type = type; - vNode.shouldUpdate = true; - - // lazy类型的特殊处理 - vNode.isLazyComponent = isLazy; - if (isLazy) { - vNode.lazyType = type; - } - break; case TreeRoot: // 创建treeRoot vNode = newVirtualNode(TreeRoot, null, null, secondArg[0]); @@ -162,9 +174,9 @@ export function createVNodeFromElement(element: JSXElement): VNode { const props = element.props; if (type === TYPE_STRICT_MODE || type === TYPE_FRAGMENT || type === TYPE_PROFILER) { - return createVNode(Fragment, key, props.children); + return createFragmentVNode(key, props.children); } else { - return createVNode('props', type, key, props); + return createUndeterminedVNode(type, key, props); } } From 99684271e65a8030477c7fcc7d46f6312e2f7943 Mon Sep 17 00:00:00 2001 From: * <8> Date: Thu, 27 Jan 2022 17:47:57 +0800 Subject: [PATCH 4/4] Match-id-02c30c435c688da20368ac4a0e1561f3d195c443 --- libs/horizon/src/event/customEvents/CustomBaseEvent.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libs/horizon/src/event/customEvents/CustomBaseEvent.ts b/libs/horizon/src/event/customEvents/CustomBaseEvent.ts index e58b91c4..3725c9c0 100644 --- a/libs/horizon/src/event/customEvents/CustomBaseEvent.ts +++ b/libs/horizon/src/event/customEvents/CustomBaseEvent.ts @@ -35,9 +35,11 @@ function extendAttribute(target, source) { ]; const length = attributes.length; + let attr; + let type; for (let i = 0; i < length; i++) { - const attr = attributes[i]; - const type = source[attr]; + attr = attributes[i]; + type = source[attr]; if (type !== 'undefined') { if (type === 'function') { target[attr] = function() {