From fa55d306c2b239e1edbf773d43e033331757357a Mon Sep 17 00:00:00 2001 From: * <8> Date: Mon, 24 Jan 2022 10:39:44 +0800 Subject: [PATCH 01/14] Match-id-f3cdec2dbb028df973e8e9cee81f90ed90bf0ae4 --- .../DOMPropertiesHandler/UpdateCommonProp.ts | 6 ++-- libs/horizon/src/dom/SelectionRangeHandler.ts | 4 +-- libs/horizon/src/dom/utils/Interface.ts | 2 +- .../src/dom/validators/PropertiesData.ts | 8 ++--- libs/horizon/src/event/HorizonEventMain.ts | 33 +++++++++++-------- libs/horizon/src/event/Types.ts | 8 ++--- libs/horizon/src/event/const.ts | 3 +- .../src/event/customEvents/CustomBaseEvent.ts | 13 +++++--- libs/horizon/src/renderer/hooks/BaseHook.ts | 8 +++-- .../src/renderer/hooks/UseReducerHook.ts | 13 ++++---- .../src/renderer/submit/LifeCycleHandler.ts | 25 ++++++++------ libs/horizon/src/renderer/vnode/VNode.ts | 22 ++++++------- libs/horizon/src/renderer/vnode/VNodeFlags.ts | 3 +- 13 files changed, 84 insertions(+), 64 deletions(-) diff --git a/libs/horizon/src/dom/DOMPropertiesHandler/UpdateCommonProp.ts b/libs/horizon/src/dom/DOMPropertiesHandler/UpdateCommonProp.ts index e9a6fce1..5ac5e058 100644 --- a/libs/horizon/src/dom/DOMPropertiesHandler/UpdateCommonProp.ts +++ b/libs/horizon/src/dom/DOMPropertiesHandler/UpdateCommonProp.ts @@ -14,9 +14,9 @@ const svgHumpAttr = new Set(['allowReorder', 'autoReverse', 'baseFrequency', 'ba 'maskContentUnits', 'maskUnits', 'numOctaves', 'pathLength', 'patternContentUnits', 'patternTransform,', 'patternUnits', 'pointsAtX', 'pointsAtY', 'pointsAtZ', 'preserveAlpha', 'preserveAspectRatio', 'primitiveUnits', 'referrerPolicy', 'refX', 'refY', 'repeatCount', 'repeatDur', 'requiredExtensions', 'requiredFeatures', - 'specularConstant', 'specularExponent', 'spreadMethod', 'startOffset', 'stdDeviation', 'stitchTiles', 'surfaceScale', - 'systemLanguage', 'tableValues', 'targetX', 'targetY', 'textLength', 'viewBox', 'viewTarget', 'xChannelSelector', - 'yChannelSelector', 'zoomAndPan']); + 'specularConstant', 'specularExponent', 'spreadMethod', 'startOffset', 'stdDeviation', + 'stitchTiles', 'surfaceScale','systemLanguage', 'tableValues', 'targetX', 'targetY', + 'textLength','viewBox', 'viewTarget', 'xChannelSelector','yChannelSelector', 'zoomAndPan']); /** * 给 dom 设置属性 diff --git a/libs/horizon/src/dom/SelectionRangeHandler.ts b/libs/horizon/src/dom/SelectionRangeHandler.ts index f5bde5bf..f498174d 100644 --- a/libs/horizon/src/dom/SelectionRangeHandler.ts +++ b/libs/horizon/src/dom/SelectionRangeHandler.ts @@ -110,8 +110,8 @@ export interface selectionData { selectionRange: { start: number; end: number; - } -}; + }; +} // 防止选择范围内的信息因为节点删除或其他原因导致的信息丢失 export function resetSelectionRange(preSelectionRangeData: selectionData) { diff --git a/libs/horizon/src/dom/utils/Interface.ts b/libs/horizon/src/dom/utils/Interface.ts index 62887f72..b4805619 100644 --- a/libs/horizon/src/dom/utils/Interface.ts +++ b/libs/horizon/src/dom/utils/Interface.ts @@ -1,5 +1,5 @@ export interface IProperty { - [propName: string]: any + [propName: string]: any; } export interface HorizonSelect extends HTMLSelectElement { diff --git a/libs/horizon/src/dom/validators/PropertiesData.ts b/libs/horizon/src/dom/validators/PropertiesData.ts index c11e5fdf..9eb90b7b 100644 --- a/libs/horizon/src/dom/validators/PropertiesData.ts +++ b/libs/horizon/src/dom/validators/PropertiesData.ts @@ -9,10 +9,10 @@ export enum PROPERTY_TYPE { }; export type PropDetails = { - propName: string, - type: PROPERTY_TYPE, - attrName: string, - attrNS: string | null, + propName: string; + type: PROPERTY_TYPE; + attrName: string; + attrNS: string | null; }; export function getPropDetails(name: string): PropDetails | null { diff --git a/libs/horizon/src/event/HorizonEventMain.ts b/libs/horizon/src/event/HorizonEventMain.ts index 1ee6ba8b..55f34969 100644 --- a/libs/horizon/src/event/HorizonEventMain.ts +++ b/libs/horizon/src/event/HorizonEventMain.ts @@ -1,5 +1,5 @@ -import type {AnyNativeEvent, ProcessingListenerList} from './types'; -import type {VNode} from '../renderer/Types'; +import type { AnyNativeEvent, ProcessingListenerList } from './types'; +import type { VNode } from '../renderer/Types'; import { CommonEventToHorizonMap, @@ -11,20 +11,20 @@ import { throwCaughtEventError, runListenerAndCatchFirstError, } from './EventError'; -import {getListeners as getBeforeInputListeners} from './simulatedEvtHandler/BeforeInputEventHandler'; -import {getListeners as getCompositionListeners} from './simulatedEvtHandler/CompositionEventHandler'; -import {getListeners as getChangeListeners} from './simulatedEvtHandler/ChangeEventHandler'; -import {getListeners as getSelectionListeners} from './simulatedEvtHandler/SelectionEventHandler'; +import { getListeners as getBeforeInputListeners } from './simulatedEvtHandler/BeforeInputEventHandler'; +import { getListeners as getCompositionListeners } from './simulatedEvtHandler/CompositionEventHandler'; +import { getListeners as getChangeListeners } from './simulatedEvtHandler/ChangeEventHandler'; +import { getListeners as getSelectionListeners } from './simulatedEvtHandler/SelectionEventHandler'; import { getCustomEventNameWithOn, uniqueCharCode, getEventTarget } from './utils'; -import {createCommonCustomEvent} from './customEvents/EventFactory'; -import {getListenersFromTree} from './ListenerGetter'; -import {shouldUpdateValue, updateControlledValue} from './ControlledValueUpdater'; -import {asyncUpdates, runDiscreteUpdates} from '../renderer/Renderer'; -import {getExactNode} from '../renderer/vnode/VNodeUtils'; +import { createCommonCustomEvent } from './customEvents/EventFactory'; +import { getListenersFromTree } from './ListenerGetter'; +import { shouldUpdateValue, updateControlledValue } from './ControlledValueUpdater'; +import { asyncUpdates, runDiscreteUpdates } from '../renderer/Renderer'; +import { getExactNode } from '../renderer/vnode/VNodeUtils'; // 获取事件触发的普通事件监听方法队列 function getCommonListeners( @@ -61,7 +61,7 @@ function getCommonListeners( vNode, customEventName, customEvent, - isCapture ? EVENT_TYPE_CAPTURE: EVENT_TYPE_BUBBLE, + isCapture ? EVENT_TYPE_CAPTURE : EVENT_TYPE_BUBBLE, ); } @@ -72,7 +72,7 @@ export function processListeners( processingEventsList.forEach(eventUnitList => { let lastVNode; eventUnitList.forEach(eventUnit => { - const {vNode, currentTarget, listener, event} = eventUnit; + const { vNode, currentTarget, listener, event } = eventUnit; if (vNode !== lastVNode && event.isPropagationStopped()) { return; } @@ -151,7 +151,12 @@ function triggerHorizonEvents( vNode: null | VNode, ): void { const nativeEventTarget = getEventTarget(nativeEvent); - const processingListenerList = getProcessListenersFacade(nativeEvtName, vNode, nativeEvent, nativeEventTarget, isCapture); + const processingListenerList = getProcessListenersFacade( + nativeEvtName, + vNode, + nativeEvent, + nativeEventTarget, + isCapture); // 处理触发的事件队列 processListeners(processingListenerList); diff --git a/libs/horizon/src/event/Types.ts b/libs/horizon/src/event/Types.ts index ac9bab0f..fb67cf86 100644 --- a/libs/horizon/src/event/Types.ts +++ b/libs/horizon/src/event/Types.ts @@ -5,10 +5,10 @@ import {CustomBaseEvent} from './customEvents/CustomBaseEvent'; export type AnyNativeEvent = KeyboardEvent | MouseEvent | TouchEvent | UIEvent | Event; export type ListenerUnit = { - vNode: null | VNode, - listener: Function, - currentTarget: EventTarget, - event: CustomBaseEvent, + vNode: null | VNode; + listener: Function; + currentTarget: EventTarget; + event: CustomBaseEvent; }; export type ListenerUnitList = Array; diff --git a/libs/horizon/src/event/const.ts b/libs/horizon/src/event/const.ts index 2274bc03..20c954ae 100644 --- a/libs/horizon/src/event/const.ts +++ b/libs/horizon/src/event/const.ts @@ -35,7 +35,8 @@ export const horizonEventToNativeMap = new Map([ ['onCompositionUpdate', ['compositionupdate']], ['onBeforeInput', ['compositionend', 'keypress', 'textInput']], ['onChange', ['change', 'click', 'focusout', 'input',]], - ['onSelect', ['focusout', 'contextmenu', 'dragend', 'focusin', 'keydown', 'keyup', 'mousedown', 'mouseup', 'selectionchange']], + ['onSelect', ['focusout', 'contextmenu', 'dragend', 'focusin', + 'keydown', 'keyup', 'mousedown', 'mouseup', 'selectionchange']], ['onAnimationEnd', [STYLE_AMT_END]], ['onAnimationIteration', [STYLE_AMT_ITERATION]], diff --git a/libs/horizon/src/event/customEvents/CustomBaseEvent.ts b/libs/horizon/src/event/customEvents/CustomBaseEvent.ts index 0ebce67a..d4595d31 100644 --- a/libs/horizon/src/event/customEvents/CustomBaseEvent.ts +++ b/libs/horizon/src/event/customEvents/CustomBaseEvent.ts @@ -36,7 +36,9 @@ export class CustomBaseEvent { this.target = target; this.timeStamp = nativeEvt.timeStamp || Date.now(); - const defaultPrevented = nativeEvt.defaultPrevented != null ? nativeEvt.defaultPrevented : nativeEvt.returnValue === false; + const defaultPrevented = nativeEvt.defaultPrevented != null ? + nativeEvt.defaultPrevented : + nativeEvt.returnValue === false; this.defaultPrevented = defaultPrevented; this.preventDefault = this.preventDefault.bind(this); @@ -90,11 +92,14 @@ function extendAttribute(target, source) { // FocusEvent 'relatedTarget', // KeyboardEvent - 'key', 'keyCode', 'charCode', 'code', 'location', 'ctrlKey', 'shiftKey', 'altKey', 'metaKey', 'repeat', 'locale', 'getModifierState', 'clipboardData', + 'key', 'keyCode', 'charCode', 'code', 'location', 'ctrlKey', 'shiftKey', + 'altKey', 'metaKey', 'repeat', 'locale', 'getModifierState', 'clipboardData', // MouseEvent - 'button', 'buttons', 'clientX', 'clientY', 'movementX', 'movementY', 'pageX', 'pageY', 'screenX', 'screenY', 'currentTarget', + 'button', 'buttons', 'clientX', 'clientY', 'movementX', 'movementY', + 'pageX', 'pageY', 'screenX', 'screenY', 'currentTarget', // PointerEvent - 'pointerId', 'width', 'height', 'pressure', 'tangentialPressure', 'tiltX', 'tiltY', 'twist', 'pointerType', 'isPrimary', + 'pointerId', 'width', 'height', 'pressure', 'tangentialPressure', + 'tiltX', 'tiltY', 'twist', 'pointerType', 'isPrimary', // TouchEvent 'touches', 'targetTouches', 'changedTouches', // TransitionEvent diff --git a/libs/horizon/src/renderer/hooks/BaseHook.ts b/libs/horizon/src/renderer/hooks/BaseHook.ts index 18e59db1..8c3f3605 100644 --- a/libs/horizon/src/renderer/hooks/BaseHook.ts +++ b/libs/horizon/src/renderer/hooks/BaseHook.ts @@ -1,5 +1,5 @@ -import type {VNode} from '../Types'; -import type {Hook} from './HookType'; +import type { VNode } from '../Types'; +import type { Hook } from './HookType'; let processingVNode: VNode = null; @@ -56,7 +56,9 @@ export function getNextHook(hook: Hook, hooks: Array>) // processing中的hook和上一次执行中的hook,需要同时往前走, // 原因:1.比对hook的数量有没有变化(非必要);2.从上一次执行中的hook获取removeEffect export function getCurrentHook(): Hook { - currentHook = currentHook !== null ? getNextHook(currentHook, processingVNode.hooks) : (processingVNode.hooks[0] || null); + currentHook = currentHook !== null ? + getNextHook(currentHook, processingVNode.hooks) : + (processingVNode.hooks[0] || null); if (lastTimeHook !== null) { lastTimeHook = getNextHook(lastTimeHook, processingVNode.oldHooks); diff --git a/libs/horizon/src/renderer/hooks/UseReducerHook.ts b/libs/horizon/src/renderer/hooks/UseReducerHook.ts index 4c241152..58fbb7f6 100644 --- a/libs/horizon/src/renderer/hooks/UseReducerHook.ts +++ b/libs/horizon/src/renderer/hooks/UseReducerHook.ts @@ -1,4 +1,4 @@ -import type {Hook, Reducer, Trigger, Update} from './HookType'; +import type { Hook, Reducer, Trigger, Update } from './HookType'; import { createHook, getCurrentHook, @@ -8,12 +8,13 @@ import { import { launchUpdateFromVNode } from '../TreeBuilder'; -import {isSame} from '../utils/compare'; -import {setStateChange} from '../render/FunctionComponent'; -import {getHookStage, HookStage} from './HookStage'; -import type {VNode} from '../Types'; +import { isSame } from '../utils/compare'; +import { setStateChange } from '../render/FunctionComponent'; +import { getHookStage, HookStage } from './HookStage'; +import type { VNode } from '../Types'; -export function useReducerImpl(reducer: (S, A) => S, initArg: P, init?: (P) => S, isUseState?: boolean): [S, Trigger] { +export function useReducerImpl(reducer: (S, A) => + S, initArg: P, init?: (P) => S, isUseState?: boolean): [S, Trigger] { const stage = getHookStage(); if (stage === null) { throwNotInFuncError(); diff --git a/libs/horizon/src/renderer/submit/LifeCycleHandler.ts b/libs/horizon/src/renderer/submit/LifeCycleHandler.ts index 5e2fd84b..78a308d4 100644 --- a/libs/horizon/src/renderer/submit/LifeCycleHandler.ts +++ b/libs/horizon/src/renderer/submit/LifeCycleHandler.ts @@ -2,10 +2,10 @@ * 该文件负责把更新应用到界面上 以及 和生命周期的相关调用 */ -import type {Container} from '../../dom/DOMOperator'; -import type {RefType, VNode} from '../Types'; +import type { Container } from '../../dom/DOMOperator'; +import type { RefType, VNode } from '../Types'; -import {listenToPromise, SuspenseChildStatus} from '../render/SuspenseComponent'; +import { listenToPromise, SuspenseChildStatus } from '../render/SuspenseComponent'; import { FunctionComponent, ForwardRef, @@ -17,8 +17,8 @@ import { SuspenseComponent, MemoComponent, } from '../vnode/VNodeTags'; -import {FlagUtils, ResetText} from '../vnode/VNodeFlags'; -import {mergeDefaultProps} from '../render/LazyComponent'; +import { FlagUtils, ResetText } from '../vnode/VNodeFlags'; +import { mergeDefaultProps } from '../render/LazyComponent'; import { submitDomUpdate, clearText, @@ -29,15 +29,20 @@ import { unHideDom, clearContainer, } from '../../dom/DOMOperator'; -import {callEffectRemove, callUseEffects, callUseLayoutEffectCreate, callUseLayoutEffectRemove} from './HookEffectHandler'; -import {handleSubmitError} from '../ErrorHandler'; +import { + callEffectRemove, + callUseEffects, + callUseLayoutEffectCreate, + callUseLayoutEffectRemove +} from './HookEffectHandler'; +import { handleSubmitError } from '../ErrorHandler'; import { travelVNodeTree, clearVNode, isDomVNode, findDomParent, getSiblingDom, } from '../vnode/VNodeUtils'; -import {shouldAutoFocus} from '../../dom/utils/Common'; +import { shouldAutoFocus } from '../../dom/utils/Common'; function callComponentWillUnmount(vNode: VNode, instance: any) { try { @@ -227,7 +232,7 @@ function unmountNestedVNodes(vNode: VNode): void { } function submitAddition(vNode: VNode): void { - const {parent, parentDom} = findDomParent(vNode); + const { parent, parentDom } = findDomParent(vNode); if (parent.flags.ResetText) { // 在insert之前先reset @@ -244,7 +249,7 @@ function insertOrAppendPlacementNode( beforeDom: Element | null, parent: Element | Container, ): void { - const {tag, realNode} = node; + const { tag, realNode } = node; if (isDomVNode(node)) { if (beforeDom) { diff --git a/libs/horizon/src/renderer/vnode/VNode.ts b/libs/horizon/src/renderer/vnode/VNode.ts index 810c7fb5..71c0856e 100644 --- a/libs/horizon/src/renderer/vnode/VNode.ts +++ b/libs/horizon/src/renderer/vnode/VNode.ts @@ -49,17 +49,17 @@ export class VNode { // 因为LazyComponent会修改type属性,为了在diff中判断是否可以复用,需要增加一个lazyType lazyType: any = null; flags: { - Addition?: boolean, - Update?: boolean, - Deletion?: boolean, - ResetText?: boolean, - Callback?: boolean, - DidCapture?: boolean, - Ref?: boolean, - Snapshot?: boolean, - Interrupted?: boolean, - ShouldCapture?: boolean, - ForceUpdate?: boolean, + Addition?: boolean; + Update?: boolean; + Deletion?: boolean; + ResetText?: boolean; + Callback?: boolean; + DidCapture?: boolean; + Ref?: boolean; + Snapshot?: boolean; + Interrupted?: boolean; + ShouldCapture?: boolean; + ForceUpdate?: boolean; } = {}; // one tree相关属性 diff --git a/libs/horizon/src/renderer/vnode/VNodeFlags.ts b/libs/horizon/src/renderer/vnode/VNodeFlags.ts index 15e25131..62e02be1 100644 --- a/libs/horizon/src/renderer/vnode/VNodeFlags.ts +++ b/libs/horizon/src/renderer/vnode/VNodeFlags.ts @@ -19,7 +19,8 @@ export const ShouldCapture = 'ShouldCapture'; // For suspense export const ForceUpdate = 'ForceUpdate'; -const FlagArr = [Addition, Update, Deletion, ResetText, Callback, DidCapture, Ref, Snapshot, Interrupted, ShouldCapture, ForceUpdate]; +const FlagArr = [Addition, Update, Deletion, ResetText, Callback, + DidCapture, Ref, Snapshot, Interrupted, ShouldCapture, ForceUpdate]; const LifecycleEffectArr = [Update, Callback, Ref, Snapshot]; From 356874599a8be56289e614c47d836d6465a5deb1 Mon Sep 17 00:00:00 2001 From: * <8> Date: Tue, 25 Jan 2022 09:33:01 +0800 Subject: [PATCH 02/14] Match-id-b3144e462876f69f20c7c56f50a88fa9cd532298 --- libs/horizon/src/dom/DOMInternalKeys.ts | 2 +- .../src/renderer/diff/nodeDiffComparator.ts | 10 +++- .../src/renderer/submit/LifeCycleHandler.ts | 53 ++++++++++++++++++- libs/horizon/src/renderer/submit/Submit.ts | 7 ++- libs/horizon/src/renderer/vnode/VNode.ts | 3 +- libs/horizon/src/renderer/vnode/VNodeFlags.ts | 7 ++- 6 files changed, 74 insertions(+), 8 deletions(-) diff --git a/libs/horizon/src/dom/DOMInternalKeys.ts b/libs/horizon/src/dom/DOMInternalKeys.ts index dec6fed1..724ae509 100644 --- a/libs/horizon/src/dom/DOMInternalKeys.ts +++ b/libs/horizon/src/dom/DOMInternalKeys.ts @@ -16,7 +16,7 @@ import { const prefix = '_horizon'; -const internalKeys = { +export const internalKeys = { VNode: `${prefix}VNode`, props: `${prefix}Props`, events: `${prefix}Events`, diff --git a/libs/horizon/src/renderer/diff/nodeDiffComparator.ts b/libs/horizon/src/renderer/diff/nodeDiffComparator.ts index cf2a7993..b805f71b 100644 --- a/libs/horizon/src/renderer/diff/nodeDiffComparator.ts +++ b/libs/horizon/src/renderer/diff/nodeDiffComparator.ts @@ -1,7 +1,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 } from '../vnode/VNodeTags'; +import { DomText, DomPortal, Fragment, DomComponent } from '../vnode/VNodeTags'; import {updateVNode, createVNode, createVNodeFromElement, updateVNodePath} from '../vnode/VNodeCreator'; import { isSameType, @@ -320,7 +320,13 @@ function diffArrayNodes( // 3. 新节点已经处理完成 if (leftIdx === rightIdx) { if (isComparing) { - deleteVNodes(parentNode, oldNode, rightEndOldNode); + if (firstChild && parentNode.tag === DomComponent && newChildren.length === 0) { + // if (false) { + FlagUtils.markClear(parentNode); + parentNode.ClearChild = firstChild; + } else { + deleteVNodes(parentNode, oldNode, rightEndOldNode); + } } if (rightNewNode) { diff --git a/libs/horizon/src/renderer/submit/LifeCycleHandler.ts b/libs/horizon/src/renderer/submit/LifeCycleHandler.ts index 78a308d4..190763cf 100644 --- a/libs/horizon/src/renderer/submit/LifeCycleHandler.ts +++ b/libs/horizon/src/renderer/submit/LifeCycleHandler.ts @@ -17,7 +17,7 @@ import { SuspenseComponent, MemoComponent, } from '../vnode/VNodeTags'; -import { FlagUtils, ResetText } from '../vnode/VNodeFlags'; +import { FlagUtils, ResetText, Clear } from '../vnode/VNodeFlags'; import { mergeDefaultProps } from '../render/LazyComponent'; import { submitDomUpdate, @@ -43,6 +43,7 @@ import { findDomParent, getSiblingDom, } from '../vnode/VNodeUtils'; import { shouldAutoFocus } from '../../dom/utils/Common'; +import { internalKeys } from '../../dom/DOMInternalKeys'; function callComponentWillUnmount(vNode: VNode, instance: any) { try { @@ -307,6 +308,55 @@ function unmountDomComponents(vNode: VNode): void { }); } +function submitClear(vNode: VNode): void { + const realNode = vNode.realNode; + const cloneDom = realNode.cloneNode(false); // 复制节点后horizon的两个属性消失 + cloneDom[internalKeys.VNode] = realNode[internalKeys.VNode]; + cloneDom[internalKeys.props] = realNode[internalKeys.props]; + // 删除节点 + let currentParentIsValid = false; + + // 这两个变量要一起更新 + let currentParent; + + travelVNodeTree(vNode, (node) => { + if (!currentParentIsValid) { + const parentObj = findDomParent(node); + currentParent = parentObj.parentDom; + currentParentIsValid = true; + } + const tag = node.tag; + if (tag === DomComponent || tag === DomText) { + // 卸载子vNode,递归遍历子vNode + unmountNestedVNodes(node.ClearChild); // node.child 为空,需要添加原有的child + + // 在所有子项都卸载后,删除dom树中的节点 + removeChildDom(currentParent, node.realNode); + console.log(currentParent); + currentParent.append(cloneDom); + vNode.realNode = cloneDom; + FlagUtils.removeFlag(vNode, Clear); + vNode.ClearChild = null; + } else if (tag === DomPortal) { + console.log(DomPortal); + if (node.child !== null) { + currentParent = node.outerDom; + } + } else { + unmountVNode(node); + } + }, (node) => { + // 如果是dom不用再遍历child + const tag = node.tag; + return tag === DomComponent || tag === DomText; + }, null, (node) => { + if (node.tag === DomPortal) { + // 当离开portal,需要重新设置parent + currentParentIsValid = false; + } + }); +} + function submitDeletion(vNode: VNode): void { // 遍历所有子节点:删除dom节点,detach ref 和 调用componentWillUnmount() unmountDomComponents(vNode); @@ -353,6 +403,7 @@ export { submitResetTextContent, submitAddition, submitDeletion, + submitClear, submitUpdate, callAfterSubmitLifeCycles, attachRef, diff --git a/libs/horizon/src/renderer/submit/Submit.ts b/libs/horizon/src/renderer/submit/Submit.ts index e76f0b97..446569bb 100644 --- a/libs/horizon/src/renderer/submit/Submit.ts +++ b/libs/horizon/src/renderer/submit/Submit.ts @@ -9,7 +9,7 @@ import { attachRef, callAfterSubmitLifeCycles, callBeforeSubmitLifeCycles, submitDeletion, submitAddition, - submitResetTextContent, submitUpdate, detachRef, + submitResetTextContent, submitUpdate, detachRef, submitClear, } from './LifeCycleHandler'; import {tryRenderRoot, setProcessing} from '../TreeBuilder'; import { @@ -121,7 +121,7 @@ function submit(dirtyNodes: Array) { } } - const {Addition, Update, Deletion} = node.flags; + const {Addition, Update, Deletion, Clear} = node.flags; if (Addition && Update) { // Addition submitAddition(node); @@ -138,6 +138,9 @@ function submit(dirtyNodes: Array) { } else if (Deletion) { submitDeletion(node); } + if (Clear) { + submitClear(node); + } } } catch (error) { throwIfTrue(node === null, 'Should be working on an effect.'); diff --git a/libs/horizon/src/renderer/vnode/VNode.ts b/libs/horizon/src/renderer/vnode/VNode.ts index 71c0856e..9cac9cc2 100644 --- a/libs/horizon/src/renderer/vnode/VNode.ts +++ b/libs/horizon/src/renderer/vnode/VNode.ts @@ -60,8 +60,9 @@ export class VNode { Interrupted?: boolean; ShouldCapture?: boolean; ForceUpdate?: boolean; + Clear?: boolean; } = {}; - + ClearChild: VNode|null = null; // one tree相关属性 isCreated: boolean = true; oldHooks: Array> = []; // 保存上一次执行的hook diff --git a/libs/horizon/src/renderer/vnode/VNodeFlags.ts b/libs/horizon/src/renderer/vnode/VNodeFlags.ts index 62e02be1..449d6baa 100644 --- a/libs/horizon/src/renderer/vnode/VNodeFlags.ts +++ b/libs/horizon/src/renderer/vnode/VNodeFlags.ts @@ -18,9 +18,10 @@ export const Interrupted = 'Interrupted'; export const ShouldCapture = 'ShouldCapture'; // For suspense export const ForceUpdate = 'ForceUpdate'; +export const Clear = 'Clear'; const FlagArr = [Addition, Update, Deletion, ResetText, Callback, - DidCapture, Ref, Snapshot, Interrupted, ShouldCapture, ForceUpdate]; + DidCapture, Ref, Snapshot, Interrupted, ShouldCapture, ForceUpdate, Clear]; const LifecycleEffectArr = [Update, Callback, Ref, Snapshot]; @@ -90,5 +91,9 @@ export class FlagUtils { static markForceUpdate(node: VNode) { node.flags.ForceUpdate = true; } + + static markClear(node: VNode) { + node.flags.Clear = true; + } } From 2d97a19610fe909a996e527b0938dc254cb5980c Mon Sep 17 00:00:00 2001 From: * <8> Date: Tue, 25 Jan 2022 11:37:51 +0800 Subject: [PATCH 03/14] Match-id-87d7a306856b74030f13b40cb209b873d055d41e --- libs/horizon/src/dom/DOMExternal.ts | 50 ++-- libs/horizon/src/dom/DOMInternalKeys.ts | 5 +- libs/horizon/src/dom/DOMOperator.ts | 36 ++- .../DOMPropertiesHandler.ts | 42 +-- libs/horizon/src/event/HorizonEventMain.ts | 9 +- libs/horizon/src/event/WrapperListener.ts | 18 +- .../src/event/customEvents/CustomBaseEvent.ts | 85 +++--- .../event/customEvents/CustomKeyboardEvent.ts | 2 +- .../BeforeInputEventHandler.ts | 2 +- .../SelectionEventHandler.ts | 4 +- .../render/IncompleteClassComponent.ts | 24 +- .../src/renderer/render/LazyComponent.ts | 35 +-- .../src/renderer/render/SuspenseComponent.ts | 261 +++++++++--------- libs/horizon/src/renderer/render/TreeRoot.ts | 8 +- .../render/class/ClassLifeCycleProcessor.ts | 12 +- 15 files changed, 297 insertions(+), 296 deletions(-) diff --git a/libs/horizon/src/dom/DOMExternal.ts b/libs/horizon/src/dom/DOMExternal.ts index 237e8de2..02742120 100644 --- a/libs/horizon/src/dom/DOMExternal.ts +++ b/libs/horizon/src/dom/DOMExternal.ts @@ -10,30 +10,6 @@ import {findDOMByClassInst} from '../renderer/vnode/VNodeUtils'; import {TreeRoot} from '../renderer/vnode/VNodeTags'; import {Callback} from '../renderer/UpdateHandler'; -function executeRender( - children: any, - container: Container, - callback?: Callback, -) { - let treeRoot = container._treeRoot; - - if (!treeRoot) { - treeRoot = createRoot(children, container, callback); - } else { // container被render过 - if (typeof callback === 'function') { - const cb = callback; - callback = function () { - const instance = getFirstCustomDom(treeRoot); - cb.call(instance); - }; - } - // 执行更新操作 - startUpdate(children, treeRoot, callback); - } - - return getFirstCustomDom(treeRoot); -} - function createRoot(children: any, container: Container, callback?: Callback) { // 清空容器 let child = container.lastChild; @@ -66,8 +42,32 @@ function createRoot(children: any, container: Container, callback?: Callback) { return treeRoot; } +function executeRender( + children: any, + container: Container, + callback?: Callback, +) { + let treeRoot = container._treeRoot; + + if (!treeRoot) { + treeRoot = createRoot(children, container, callback); + } else { // container被render过 + if (typeof callback === 'function') { + const cb = callback; + callback = function () { + const instance = getFirstCustomDom(treeRoot); + cb.call(instance); + }; + } + // 执行更新操作 + startUpdate(children, treeRoot, callback); + } + + return getFirstCustomDom(treeRoot); +} + function findDOMNode(domOrEle: Element): null | Element | Text { - if (domOrEle == null) { + if (!domOrEle) { return null; } diff --git a/libs/horizon/src/dom/DOMInternalKeys.ts b/libs/horizon/src/dom/DOMInternalKeys.ts index dec6fed1..55621aae 100644 --- a/libs/horizon/src/dom/DOMInternalKeys.ts +++ b/libs/horizon/src/dom/DOMInternalKeys.ts @@ -72,7 +72,7 @@ export function getNearestVNode(dom: Node): null | VNode { } // 获取 vNode 上的属性相关信息 -export function getVNodeProps(dom: Element | Text): Props { +export function getVNodeProps(dom: Element | Text): Props | null{ return dom[internalKeys.props] || null; } @@ -93,7 +93,8 @@ export function getEventListeners(dom: EventTarget): Set { export function getEventToListenerMap(target: EventTarget): Map { let eventsMap = target[internalKeys.nonDelegatedEvents]; if (!eventsMap) { - eventsMap = target[internalKeys.nonDelegatedEvents] = new Map(); + eventsMap = new Map(); + target[internalKeys.nonDelegatedEvents] = new Map(); } return eventsMap; } diff --git a/libs/horizon/src/dom/DOMOperator.ts b/libs/horizon/src/dom/DOMOperator.ts index b475375a..9033a028 100644 --- a/libs/horizon/src/dom/DOMOperator.ts +++ b/libs/horizon/src/dom/DOMOperator.ts @@ -5,13 +5,13 @@ import { import { createDom, } from './utils/DomCreator'; -import {getSelectionInfo, resetSelectionRange, selectionData} from './SelectionRangeHandler'; -import {isElement, isComment, isDocument, isDocumentFragment, getDomTag, shouldAutoFocus} from './utils/Common'; -import {NSS} from './utils/DomCreator'; -import {adjustStyleValue} from './DOMPropertiesHandler/StyleHandler'; +import { getSelectionInfo, resetSelectionRange, selectionData } from './SelectionRangeHandler'; +import { isElement, isComment, isDocument, isDocumentFragment, getDomTag, shouldAutoFocus } from './utils/Common'; +import { NSS } from './utils/DomCreator'; +import { adjustStyleValue } from './DOMPropertiesHandler/StyleHandler'; -import {listenDelegatedEvents} from '../event/EventBinding'; -import type {VNode} from '../renderer/Types'; +import { listenDelegatedEvents } from '../event/EventBinding'; +import type { VNode } from '../renderer/Types'; import { setInitValue, getPropsWithoutValue, @@ -21,10 +21,10 @@ import { compareProps, setDomProps, updateDomProps } from './DOMPropertiesHandler/DOMPropertiesHandler'; -import {isNativeElement, validateProps} from './validators/ValidateProps'; -import {watchValueChange} from './valueHandler/ValueChangeHandler'; -import {DomComponent, DomText} from '../renderer/vnode/VNodeTags'; -import {updateCommonProp} from './DOMPropertiesHandler/UpdateCommonProp'; +import { isNativeElement, validateProps } from './validators/ValidateProps'; +import { watchValueChange } from './valueHandler/ValueChangeHandler'; +import { DomComponent, DomText } from '../renderer/vnode/VNodeTags'; +import { updateCommonProp } from './DOMPropertiesHandler/UpdateCommonProp'; export type Props = { autoFocus?: boolean; @@ -55,13 +55,7 @@ function getChildNS(parentNS: string | null, tagName: string): string { // 获取容器 export function getNSCtx(dom: Container, parentNS: string, type: string): string { - let namespace; - if (dom) { - namespace = getChildNS(dom.namespaceURI ?? null, dom.nodeName); - } else { - namespace = getChildNS(parentNS, type); - } - return namespace; + return dom ? getChildNS(dom.namespaceURI ?? null, dom.nodeName) : getChildNS(parentNS, type); } export function prepareForSubmit(): void { @@ -156,7 +150,7 @@ export function newTextDom( // 提交vNode的类型为Component或者Text的更新 export function submitDomUpdate(tag: string, vNode: VNode) { const newProps = vNode.props; - const element: Element = vNode.realNode; + const element: Element | null = vNode.realNode; if (tag === DomComponent) { // DomComponent类型 @@ -179,8 +173,10 @@ export function submitDomUpdate(tag: string, vNode: VNode) { } } } else if (tag === DomText) { - // text类型 - element.textContent = newProps; + if (element != null) { + // text类型 + element.textContent = newProps; + } } } diff --git a/libs/horizon/src/dom/DOMPropertiesHandler/DOMPropertiesHandler.ts b/libs/horizon/src/dom/DOMPropertiesHandler/DOMPropertiesHandler.ts index 3d5f7d36..5016d811 100644 --- a/libs/horizon/src/dom/DOMPropertiesHandler/DOMPropertiesHandler.ts +++ b/libs/horizon/src/dom/DOMPropertiesHandler/DOMPropertiesHandler.ts @@ -8,6 +8,27 @@ import { } from '../../event/EventBinding'; import { isEventProp, isNativeElement } from '../validators/ValidateProps'; +function updateOneProp(dom, propName, propVal, isNativeTag, isInit?: boolean) { + if (propName === 'style') { + setStyles(dom, propVal); + } else if (propName === 'dangerouslySetInnerHTML') { + dom.innerHTML = propVal.__html; + } else if (propName === 'children') { // 只处理纯文本子节点,其他children在VNode树中处理 + if (typeof propVal === 'string' || typeof propVal === 'number') { + dom.textContent = String(propVal); + } + } else if (isEventProp(propName)) { + // 事件监听属性处理 + if (!allDelegatedHorizonEvents.has(propName)) { + listenNonDelegatedEvent(propName, dom, propVal); + } + } else { + if (!isInit || (isInit && propVal != null)) { + updateCommonProp(dom, propName, propVal, isNativeTag); + } + } +} + // 初始化DOM属性 export function setDomProps( tagName: string, @@ -38,27 +59,6 @@ export function updateDomProps( } } -function updateOneProp(dom, propName, propVal, isNativeTag, isInit?: boolean) { - if (propName === 'style') { - setStyles(dom, propVal); - } else if (propName === 'dangerouslySetInnerHTML') { - dom.innerHTML = propVal.__html; - } else if (propName === 'children') { // 只处理纯文本子节点,其他children在VNode树中处理 - if (typeof propVal === 'string' || typeof propVal === 'number') { - dom.textContent = String(propVal); - } - } else if (isEventProp(propName)) { - // 事件监听属性处理 - if (!allDelegatedHorizonEvents.has(propName)) { - listenNonDelegatedEvent(propName, dom, propVal); - } - } else { - if (!isInit || (isInit && propVal != null)) { - updateCommonProp(dom, propName, propVal, isNativeTag); - } - } -} - // 找出两个 DOM 属性的差别,生成需要更新的属性集合 export function compareProps( oldProps: Object, diff --git a/libs/horizon/src/event/HorizonEventMain.ts b/libs/horizon/src/event/HorizonEventMain.ts index 55f34969..ad3dffda 100644 --- a/libs/horizon/src/event/HorizonEventMain.ts +++ b/libs/horizon/src/event/HorizonEventMain.ts @@ -122,7 +122,9 @@ function getProcessListenersFacade( )); } - if (nativeEvtName === 'compositionend' || nativeEvtName === 'compositionstart' || nativeEvtName === 'compositionupdate') { + if (nativeEvtName === 'compositionend' || + nativeEvtName === 'compositionstart' || + nativeEvtName === 'compositionupdate') { processingListenerList = processingListenerList.concat(getCompositionListeners( nativeEvtName, nativeEvent, @@ -183,13 +185,14 @@ export function handleEventMain( // 有事件正在执行,同步执行事件 if (isInEventsExecution) { - return triggerHorizonEvents(nativeEvtName, isCapture, nativeEvent, rootVNode); + triggerHorizonEvents(nativeEvtName, isCapture, nativeEvent, rootVNode); + return; } // 没有事件在执行,经过调度再执行事件 isInEventsExecution = true; try { - return asyncUpdates(() => + asyncUpdates(() => triggerHorizonEvents( nativeEvtName, isCapture, diff --git a/libs/horizon/src/event/WrapperListener.ts b/libs/horizon/src/event/WrapperListener.ts index 9c73299c..ba5d309b 100644 --- a/libs/horizon/src/event/WrapperListener.ts +++ b/libs/horizon/src/event/WrapperListener.ts @@ -5,15 +5,6 @@ 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, @@ -39,3 +30,12 @@ function triggerDelegatedEvent( } 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 d4595d31..5ea40d96 100644 --- a/libs/horizon/src/event/customEvents/CustomBaseEvent.ts +++ b/libs/horizon/src/event/customEvents/CustomBaseEvent.ts @@ -4,6 +4,48 @@ import {VNode} from '../../renderer/Types'; +// 从原生事件中复制属性到自定义事件中 +function extendAttribute(target, source) { + const attributes = [ + // AnimationEvent + 'animationName', 'elapsedTime', 'pseudoElement', + // CompositionEvent、InputEvent + 'data', + // DragEvent + 'dataTransfer', + // FocusEvent + 'relatedTarget', + // KeyboardEvent + 'key', 'keyCode', 'charCode', 'code', 'location', 'ctrlKey', 'shiftKey', + 'altKey', 'metaKey', 'repeat', 'locale', 'getModifierState', 'clipboardData', + // MouseEvent + 'button', 'buttons', 'clientX', 'clientY', 'movementX', 'movementY', + 'pageX', 'pageY', 'screenX', 'screenY', 'currentTarget', + // PointerEvent + 'pointerId', 'width', 'height', 'pressure', 'tangentialPressure', + 'tiltX', 'tiltY', 'twist', 'pointerType', 'isPrimary', + // TouchEvent + 'touches', 'targetTouches', 'changedTouches', + // TransitionEvent + 'propertyName', + // UIEvent + 'view', 'detail', + // WheelEvent + 'deltaX', 'deltaY', 'deltaZ', 'deltaMode', + ]; + + attributes.forEach(attr => { + if (typeof source[attr] !== 'undefined') { + if (typeof source[attr] === 'function') { + target[attr] = function() { + return source[attr].apply(source, arguments); + }; + } else { + target[attr] = source[attr]; + } + } + }) +} export class CustomBaseEvent { data: string; @@ -79,46 +121,3 @@ export class CustomBaseEvent { this.isPropagationStopped = () => true; } } - -// 从原生事件中复制属性到自定义事件中 -function extendAttribute(target, source) { - const attributes = [ - // AnimationEvent - 'animationName', 'elapsedTime', 'pseudoElement', - // CompositionEvent、InputEvent - 'data', - // DragEvent - 'dataTransfer', - // FocusEvent - 'relatedTarget', - // KeyboardEvent - 'key', 'keyCode', 'charCode', 'code', 'location', 'ctrlKey', 'shiftKey', - 'altKey', 'metaKey', 'repeat', 'locale', 'getModifierState', 'clipboardData', - // MouseEvent - 'button', 'buttons', 'clientX', 'clientY', 'movementX', 'movementY', - 'pageX', 'pageY', 'screenX', 'screenY', 'currentTarget', - // PointerEvent - 'pointerId', 'width', 'height', 'pressure', 'tangentialPressure', - 'tiltX', 'tiltY', 'twist', 'pointerType', 'isPrimary', - // TouchEvent - 'touches', 'targetTouches', 'changedTouches', - // TransitionEvent - 'propertyName', - // UIEvent - 'view', 'detail', - // WheelEvent - 'deltaX', 'deltaY', 'deltaZ', 'deltaMode', - ]; - - attributes.forEach(attr => { - if (typeof source[attr] !== 'undefined') { - if (typeof source[attr] === 'function') { - target[attr] = function() { - return source[attr].apply(source, arguments); - }; - } else { - target[attr] = source[attr]; - } - } - }) -} diff --git a/libs/horizon/src/event/customEvents/CustomKeyboardEvent.ts b/libs/horizon/src/event/customEvents/CustomKeyboardEvent.ts index 33c6d13c..f4d0ae89 100644 --- a/libs/horizon/src/event/customEvents/CustomKeyboardEvent.ts +++ b/libs/horizon/src/event/customEvents/CustomKeyboardEvent.ts @@ -72,7 +72,7 @@ export class CustomKeyboardEvent extends CustomBaseEvent { super(customEvtName, nativeEvtName, nativeEvt, vNode, target); this.key = getKey(nativeEvt); this.charCode = nativeEvtName === 'keypress' ? uniqueCharCode(nativeEvt) : 0; - this.keyCode = (nativeEvtName === 'keydown' || nativeEvtName === 'keyup') ? nativeEvt.keyCode : 0; + this.keyCode = nativeEvtName === 'keydown' || nativeEvtName === 'keyup' ? nativeEvt.keyCode : 0; this.which = this.charCode || this.keyCode; } } diff --git a/libs/horizon/src/event/simulatedEvtHandler/BeforeInputEventHandler.ts b/libs/horizon/src/event/simulatedEvtHandler/BeforeInputEventHandler.ts index 833db40a..1a18ba27 100644 --- a/libs/horizon/src/event/simulatedEvtHandler/BeforeInputEventHandler.ts +++ b/libs/horizon/src/event/simulatedEvtHandler/BeforeInputEventHandler.ts @@ -9,7 +9,7 @@ const SPACE_CHAR = String.fromCharCode(CHAR_CODE_SPACE); function getInputCharsByNative( eventName: string, nativeEvent: any, -): string | void { +): string | null | void { if (eventName === 'compositionend') { return (nativeEvent.detail && nativeEvent.detail.data) || null; } diff --git a/libs/horizon/src/event/simulatedEvtHandler/SelectionEventHandler.ts b/libs/horizon/src/event/simulatedEvtHandler/SelectionEventHandler.ts index 81bc1fcc..87724137 100644 --- a/libs/horizon/src/event/simulatedEvtHandler/SelectionEventHandler.ts +++ b/libs/horizon/src/event/simulatedEvtHandler/SelectionEventHandler.ts @@ -13,7 +13,7 @@ const horizonEventName = 'onSelect' let currentElement = null; let currentVNode = null; -let lastSelection = null; +let lastSelection: Selection | null = null; function initTargetCache(dom, vNode) { if (isTextInputElement(dom) || dom.contentEditable === 'true') { @@ -85,7 +85,7 @@ export function getListeners( target: null | EventTarget, ): ProcessingListenerList { const targetNode = vNode ? getDom(vNode) : window; - let eventUnitList = []; + let eventUnitList: ProcessingListenerList = []; switch (name) { case 'focusin': initTargetCache(targetNode, vNode); diff --git a/libs/horizon/src/renderer/render/IncompleteClassComponent.ts b/libs/horizon/src/renderer/render/IncompleteClassComponent.ts index 7de50b1a..2c50ed18 100644 --- a/libs/horizon/src/renderer/render/IncompleteClassComponent.ts +++ b/libs/horizon/src/renderer/render/IncompleteClassComponent.ts @@ -10,7 +10,18 @@ import { cacheOldCtx, } from '../components/context/CompatibleContext'; -export function captureRender(processing: VNode): Array | null { +function captureIncompleteClassComponent(processing, Component, nextProps) { + processing.tag = ClassComponent; + + const hasOldContext = isOldProvider(Component); + cacheOldCtx(processing, hasOldContext); + + resetDepContexts(processing); + + return getIncompleteClassComponent(Component, processing, nextProps); +} + +export function captureRender(processing: VNode): VNode | null { const Component = processing.type; const unresolvedProps = processing.props; const resolvedProps = @@ -28,14 +39,3 @@ export function bubbleRender(processing: VNode) { resetOldCtx(processing); } } - -function captureIncompleteClassComponent(processing, Component, nextProps) { - processing.tag = ClassComponent; - - const hasOldContext = isOldProvider(Component); - cacheOldCtx(processing, hasOldContext); - - resetDepContexts(processing); - - return getIncompleteClassComponent(Component, processing, nextProps); -} diff --git a/libs/horizon/src/renderer/render/LazyComponent.ts b/libs/horizon/src/renderer/render/LazyComponent.ts index f3dcc954..5277c1d1 100644 --- a/libs/horizon/src/renderer/render/LazyComponent.ts +++ b/libs/horizon/src/renderer/render/LazyComponent.ts @@ -13,10 +13,6 @@ import { captureFunctionComponent } from './FunctionComponent'; import { captureClassComponent } from './ClassComponent'; import { captureMemoComponent } from './MemoComponent'; -export function captureRender(processing: VNode, shouldUpdate: boolean): VNode | null { - return captureLazyComponent(processing, processing.type, shouldUpdate); -} - export function bubbleRender() { } const LazyRendererMap = { @@ -26,6 +22,20 @@ const LazyRendererMap = { [MemoComponent]: captureMemoComponent, }; +export function mergeDefaultProps(Component: any, props: object): object { + if (Component && Component.defaultProps) { + const clonedProps = { ...props }; + const defaultProps = Component.defaultProps; + Object.keys(defaultProps).forEach(key => { + if (clonedProps[key] === undefined) { + clonedProps[key] = defaultProps[key]; + } + }); + return clonedProps; + } + return props; +} + function captureLazyComponent( processing, lazyComponent, @@ -43,7 +53,8 @@ function captureLazyComponent( // 加载得到的Component存在type中 processing.type = Component; - const lazyVNodeTag = processing.tag = getLazyVNodeTag(Component); + const lazyVNodeTag = getLazyVNodeTag(Component); + processing.tag = getLazyVNodeTag(Component); const lazyVNodeProps = mergeDefaultProps(Component, processing.props); const lazyRender = LazyRendererMap[lazyVNodeTag]; @@ -68,16 +79,6 @@ function captureLazyComponent( } } -export function mergeDefaultProps(Component: any, props: object): object { - if (Component && Component.defaultProps) { - const clonedProps = { ...props }; - const defaultProps = Component.defaultProps; - Object.keys(defaultProps).forEach(key => { - if (clonedProps[key] === undefined) { - clonedProps[key] = defaultProps[key]; - } - }); - return clonedProps; - } - return props; +export function captureRender(processing: VNode, shouldUpdate: boolean): VNode | null { + return captureLazyComponent(processing, processing.type, shouldUpdate); } diff --git a/libs/horizon/src/renderer/render/SuspenseComponent.ts b/libs/horizon/src/renderer/render/SuspenseComponent.ts index d0cb6f9e..200c0c3d 100644 --- a/libs/horizon/src/renderer/render/SuspenseComponent.ts +++ b/libs/horizon/src/renderer/render/SuspenseComponent.ts @@ -19,107 +19,6 @@ export enum SuspenseChildStatus { ShowFallback = 'showFallback', } -export function captureRender(processing: VNode, shouldUpdate: boolean): Array | VNode | null { - if ( - !processing.isCreated && - processing.oldProps === processing.props && - !getContextChangeCtx() && - !shouldUpdate - ) { - if (processing.suspenseChildStatus === SuspenseChildStatus.ShowFallback) { - // 当显示fallback时,suspense的子组件要更新 - return updateFallback(processing); - } - return onlyUpdateChildVNodes(processing); - } - - return captureSuspenseComponent(processing); -} - -function updateFallback(processing: VNode): Array | VNode | null { - const childFragment: VNode = processing.child; - - if (childFragment.childShouldUpdate) { - if (processing.promiseResolve) { - // promise已完成,展示promise返回的新节点 - return captureSuspenseComponent(processing); - } else { - // promise未完成,继续显示fallback,不需要继续刷新子节点 - const fallbackFragment: VNode = processing.child.next; - childFragment.childShouldUpdate = false; - fallbackFragment.childShouldUpdate = false; - return null; - } - } else { - const children = onlyUpdateChildVNodes(processing); - - if (children !== null) { - // child不需要更新,跳过child处理fallback - return children[1]; - } else { - return null; - } - } -} - -export function bubbleRender(processing: VNode) { - if (processing.suspenseChildStatus === SuspenseChildStatus.ShowFallback - || (!processing.isCreated && processing.oldSuspenseChildStatus === SuspenseChildStatus.ShowFallback) - ) { - FlagUtils.markUpdate(processing); - } - - return null; -} - -export function captureSuspenseComponent(processing: VNode) { - const nextProps = processing.props; - - // suspense被捕获后需要展示fallback - const showFallback = processing.suspenseDidCapture; - - if (showFallback) { - processing.suspenseDidCapture = false; - const nextFallbackChildren = nextProps.fallback; - return createFallback(processing, nextFallbackChildren); - } else { - const newChildren = nextProps.children; - return createSuspenseChildren(processing, newChildren); - } -} - -// 创建子节点 -function createSuspenseChildren(processing: VNode, newChildren) { - let childFragment: VNode; - if (!processing.isCreated) { - const oldChildFragment: VNode = processing.child; - const oldFallbackFragment: VNode | null = oldChildFragment.next; - - childFragment = updateVNode(oldChildFragment); - childFragment.next = null; - // 将Suspense新的子参数传给子Fragment - childFragment.props = processing.props.children; - childFragment.shouldUpdate = true; - - // 删除fallback - if (oldFallbackFragment !== null) { - FlagUtils.setDeletion(oldFallbackFragment); - processing.dirtyNodes = [oldFallbackFragment]; - } - // SuspenseComponent 中使用 - processing.suspenseChildStatus = SuspenseChildStatus.ShowChild; - } else { - childFragment = createVNode(Fragment, null, newChildren); - } - - childFragment.parent = processing; - childFragment.cIndex = 0; - updateVNodePath(childFragment); - processing.child = childFragment; - processing.promiseResolve = false; - return processing.child; -} - // 创建fallback子节点 function createFallback(processing: VNode, fallbackChildren) { const childFragment: VNode = processing.child; @@ -152,13 +51,118 @@ function createFallback(processing: VNode, fallbackChildren) { return fallbackFragment; } +// 创建子节点 +function createSuspenseChildren(processing: VNode, newChildren) { + let childFragment: VNode; + if (!processing.isCreated) { + const oldChildFragment: VNode = processing.child; + const oldFallbackFragment: VNode | null = oldChildFragment.next; + + childFragment = updateVNode(oldChildFragment); + childFragment.next = null; + // 将Suspense新的子参数传给子Fragment + childFragment.props = processing.props.children; + childFragment.shouldUpdate = true; + + // 删除fallback + if (oldFallbackFragment !== null) { + FlagUtils.setDeletion(oldFallbackFragment); + processing.dirtyNodes = [oldFallbackFragment]; + } + // SuspenseComponent 中使用 + processing.suspenseChildStatus = SuspenseChildStatus.ShowChild; + } else { + childFragment = createVNode(Fragment, null, newChildren); + } + + childFragment.parent = processing; + childFragment.cIndex = 0; + updateVNodePath(childFragment); + processing.child = childFragment; + processing.promiseResolve = false; + return processing.child; +} + +export function captureSuspenseComponent(processing: VNode) { + const nextProps = processing.props; + + // suspense被捕获后需要展示fallback + const showFallback = processing.suspenseDidCapture; + + if (showFallback) { + processing.suspenseDidCapture = false; + const nextFallbackChildren = nextProps.fallback; + return createFallback(processing, nextFallbackChildren); + } else { + const newChildren = nextProps.children; + return createSuspenseChildren(processing, newChildren); + } +} + +function updateFallback(processing: VNode): Array | VNode | null { + const childFragment: VNode | null= processing.child; + + if (childFragment?.childShouldUpdate) { + if (processing.promiseResolve) { + // promise已完成,展示promise返回的新节点 + return captureSuspenseComponent(processing); + } else { + // promise未完成,继续显示fallback,不需要继续刷新子节点 + const fallbackFragment: VNode = processing.child.next; + childFragment.childShouldUpdate = false; + fallbackFragment.childShouldUpdate = false; + return null; + } + } else { + const children = onlyUpdateChildVNodes(processing); + + if (children !== null) { + // child不需要更新,跳过child处理fallback + return children[1]; + } else { + return null; + } + } +} + +export function captureRender(processing: VNode, shouldUpdate: boolean): Array | VNode | null { + if ( + !processing.isCreated && + processing.oldProps === processing.props && + !getContextChangeCtx() && + !shouldUpdate + ) { + if (processing.suspenseChildStatus === SuspenseChildStatus.ShowFallback) { + // 当显示fallback时,suspense的子组件要更新 + return updateFallback(processing); + } + return onlyUpdateChildVNodes(processing); + } + + return captureSuspenseComponent(processing); +} + +export function bubbleRender(processing: VNode) { + if (processing.suspenseChildStatus === SuspenseChildStatus.ShowFallback + || (!processing.isCreated && processing.oldSuspenseChildStatus === SuspenseChildStatus.ShowFallback) + ) { + FlagUtils.markUpdate(processing); + } + + return null; +} + +function canCapturePromise(vNode: VNode | null): boolean { + return vNode?.suspenseChildStatus !== SuspenseChildStatus.ShowFallback && vNode?.props.fallback !== undefined; +} + // 处理Suspense子组件抛出的promise -export function handleSuspenseChildThrowError(parent: VNode, processing: VNode, error: any): boolean { +export function handleSuspenseChildThrowError(parent: VNode | null, processing: VNode, error: any): boolean { let vNode = parent; // 向上找到最近的不在fallback状态的Suspense,并触发重新渲染 do { - if (vNode.tag === SuspenseComponent && canCapturePromise(vNode)) { + if (vNode?.tag === SuspenseComponent && canCapturePromise(vNode)) { if (vNode.suspensePromises === null) { vNode.suspensePromises = new Set(); } @@ -196,36 +200,8 @@ export function handleSuspenseChildThrowError(parent: VNode, processing: VNode, return false; } -function canCapturePromise(vNode: VNode): boolean { - return vNode.suspenseChildStatus !== SuspenseChildStatus.ShowFallback && vNode.props.fallback !== undefined; -} - const PossiblyWeakSet = typeof WeakSet === 'function' ? WeakSet : Set; -// 对于每个promise,添加一个侦听器,以便当它resolve时,重新渲染 -export function listenToPromise(suspenseVNode: VNode) { - const promises: Set> | null = suspenseVNode.suspensePromises; - if (promises !== null) { - suspenseVNode.suspensePromises = null; - - // 记录已经监听的 promise - let promiseCache = suspenseVNode.realNode; - if (promiseCache === null) { - // @ts-ignore - promiseCache = suspenseVNode.realNode = new PossiblyWeakSet(); - } - - promises.forEach(promise => { - const resole = resolvePromise.bind(null, suspenseVNode, promise); - if (!promiseCache.has(promise)) { - promiseCache.add(promise); - // 监听promise - promise.then(resole, resole); - } - }); - } -} - function resolvePromise(suspenseVNode: VNode, promise: PromiseType) { const promiseCache = suspenseVNode.realNode; if (promiseCache !== null) { @@ -238,4 +214,27 @@ function resolvePromise(suspenseVNode: VNode, promise: PromiseType) { } } +// 对于每个promise,添加一个侦听器,以便当它resolve时,重新渲染 +export function listenToPromise(suspenseVNode: VNode) { + const promises: Set> | null = suspenseVNode.suspensePromises; + if (promises !== null) { + suspenseVNode.suspensePromises = null; + // 记录已经监听的 promise + let promiseCache = suspenseVNode.realNode; + if (promiseCache === null) { + // @ts-ignore + promiseCache = new PossiblyWeakSet(); + suspenseVNode.realNode = new PossiblyWeakSet(); + } + + promises.forEach(promise => { + const resole = resolvePromise.bind(null, suspenseVNode, promise); + if (!promiseCache.has(promise)) { + promiseCache.add(promise); + // 监听promise + promise.then(resole, resole); + } + }); + } +} diff --git a/libs/horizon/src/renderer/render/TreeRoot.ts b/libs/horizon/src/renderer/render/TreeRoot.ts index 8b4f2026..c9f54646 100644 --- a/libs/horizon/src/renderer/render/TreeRoot.ts +++ b/libs/horizon/src/renderer/render/TreeRoot.ts @@ -6,10 +6,6 @@ import {resetNamespaceCtx, setNamespaceCtx} from '../ContextSaver'; import {resetOldCtx} from '../components/context/CompatibleContext'; import {onlyUpdateChildVNodes} from '../vnode/VNodeCreator'; -export function captureRender(processing: VNode): VNode | null { - return updateTreeRoot(processing); -} - export function bubbleRender(processing: VNode) { resetNamespaceCtx(processing); resetOldCtx(processing); @@ -40,3 +36,7 @@ function updateTreeRoot(processing) { processing.child = createVNodeChildren(processing, newElement); return processing.child; } + +export function captureRender(processing: VNode): VNode | null { + return updateTreeRoot(processing); +} diff --git a/libs/horizon/src/renderer/render/class/ClassLifeCycleProcessor.ts b/libs/horizon/src/renderer/render/class/ClassLifeCycleProcessor.ts index d6e21c2c..db796eb5 100644 --- a/libs/horizon/src/renderer/render/class/ClassLifeCycleProcessor.ts +++ b/libs/horizon/src/renderer/render/class/ClassLifeCycleProcessor.ts @@ -1,7 +1,7 @@ import type { VNode } from '../../Types'; import type { Callback } from '../../UpdateHandler'; -import {shallowCompare} from '../../utils/compare'; +import { shallowCompare } from '../../utils/compare'; import { pushUpdate, newUpdate, @@ -25,9 +25,11 @@ export function callDerivedStateFromProps( const newState = getDerivedStateFromProps(nextProps, oldState); // 组件未返回state,需要返回旧的preState - processing.state = newState === null || newState === undefined - ? oldState - : { ...oldState, ...newState }; + if (newState) { + processing.state = { ...oldState, ...newState }; + return; + } + processing.state = oldState; } } @@ -40,7 +42,7 @@ function changeStateContent(type: UpdateState, content: object, callback: Callba if (type === UpdateState.Update || type === UpdateState.Override) { update.content = content; } - if (callback !== undefined && callback !== null) { + if (callback) { update.callback = callback; } From a68c3b9897568eb318c3e8e4dfc8a8f7b617c56d Mon Sep 17 00:00:00 2001 From: * <8> Date: Tue, 25 Jan 2022 11:38:32 +0800 Subject: [PATCH 04/14] Match-id-5684643f375c3d5f9612e974f8374ae4a3548172 --- .../src/renderer/diff/nodeDiffComparator.ts | 2 +- .../src/renderer/submit/LifeCycleHandler.ts | 54 +++++-------------- libs/horizon/src/renderer/vnode/VNode.ts | 2 +- 3 files changed, 15 insertions(+), 43 deletions(-) diff --git a/libs/horizon/src/renderer/diff/nodeDiffComparator.ts b/libs/horizon/src/renderer/diff/nodeDiffComparator.ts index b805f71b..dad4f3a3 100644 --- a/libs/horizon/src/renderer/diff/nodeDiffComparator.ts +++ b/libs/horizon/src/renderer/diff/nodeDiffComparator.ts @@ -323,7 +323,7 @@ function diffArrayNodes( if (firstChild && parentNode.tag === DomComponent && newChildren.length === 0) { // if (false) { FlagUtils.markClear(parentNode); - parentNode.ClearChild = firstChild; + parentNode.clearChild = firstChild; } else { deleteVNodes(parentNode, oldNode, rightEndOldNode); } diff --git a/libs/horizon/src/renderer/submit/LifeCycleHandler.ts b/libs/horizon/src/renderer/submit/LifeCycleHandler.ts index 190763cf..0fa7bcaa 100644 --- a/libs/horizon/src/renderer/submit/LifeCycleHandler.ts +++ b/libs/horizon/src/renderer/submit/LifeCycleHandler.ts @@ -313,48 +313,20 @@ function submitClear(vNode: VNode): void { const cloneDom = realNode.cloneNode(false); // 复制节点后horizon的两个属性消失 cloneDom[internalKeys.VNode] = realNode[internalKeys.VNode]; cloneDom[internalKeys.props] = realNode[internalKeys.props]; - // 删除节点 - let currentParentIsValid = false; - // 这两个变量要一起更新 - let currentParent; - - travelVNodeTree(vNode, (node) => { - if (!currentParentIsValid) { - const parentObj = findDomParent(node); - currentParent = parentObj.parentDom; - currentParentIsValid = true; - } - const tag = node.tag; - if (tag === DomComponent || tag === DomText) { - // 卸载子vNode,递归遍历子vNode - unmountNestedVNodes(node.ClearChild); // node.child 为空,需要添加原有的child - - // 在所有子项都卸载后,删除dom树中的节点 - removeChildDom(currentParent, node.realNode); - console.log(currentParent); - currentParent.append(cloneDom); - vNode.realNode = cloneDom; - FlagUtils.removeFlag(vNode, Clear); - vNode.ClearChild = null; - } else if (tag === DomPortal) { - console.log(DomPortal); - if (node.child !== null) { - currentParent = node.outerDom; - } - } else { - unmountVNode(node); - } - }, (node) => { - // 如果是dom不用再遍历child - const tag = node.tag; - return tag === DomComponent || tag === DomText; - }, null, (node) => { - if (node.tag === DomPortal) { - // 当离开portal,需要重新设置parent - currentParentIsValid = false; - } - }); + const parentObj = findDomParent(vNode); + const currentParent = parentObj.parentDom; + const clearChild = vNode.clearChild as VNode; + // 卸载子vNode,递归遍历子vNode + unmountNestedVNodes(clearChild); // node.child 为空,需要添加原有的child + + // 在所有子项都卸载后,删除dom树中的节点 + removeChildDom(currentParent, vNode.realNode); + clearVNode(clearChild); + currentParent.append(cloneDom); + vNode.realNode = cloneDom; + FlagUtils.removeFlag(vNode, Clear); + vNode.clearChild = null; } function submitDeletion(vNode: VNode): void { diff --git a/libs/horizon/src/renderer/vnode/VNode.ts b/libs/horizon/src/renderer/vnode/VNode.ts index 9cac9cc2..82b4ae1e 100644 --- a/libs/horizon/src/renderer/vnode/VNode.ts +++ b/libs/horizon/src/renderer/vnode/VNode.ts @@ -62,7 +62,7 @@ export class VNode { ForceUpdate?: boolean; Clear?: boolean; } = {}; - ClearChild: VNode|null = null; + clearChild: VNode|null = null; // one tree相关属性 isCreated: boolean = true; oldHooks: Array> = []; // 保存上一次执行的hook From b9a2d4067dd96b9e9ac91ad06ec938e88a17846c Mon Sep 17 00:00:00 2001 From: * <8> Date: Tue, 25 Jan 2022 11:49:16 +0800 Subject: [PATCH 05/14] Match-id-30c3c11db166222cef6d0b38a690afdd4a0fbc80 --- libs/horizon/src/renderer/submit/LifeCycleHandler.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/libs/horizon/src/renderer/submit/LifeCycleHandler.ts b/libs/horizon/src/renderer/submit/LifeCycleHandler.ts index 0fa7bcaa..398709df 100644 --- a/libs/horizon/src/renderer/submit/LifeCycleHandler.ts +++ b/libs/horizon/src/renderer/submit/LifeCycleHandler.ts @@ -43,7 +43,6 @@ import { findDomParent, getSiblingDom, } from '../vnode/VNodeUtils'; import { shouldAutoFocus } from '../../dom/utils/Common'; -import { internalKeys } from '../../dom/DOMInternalKeys'; function callComponentWillUnmount(vNode: VNode, instance: any) { try { @@ -311,8 +310,12 @@ function unmountDomComponents(vNode: VNode): void { function submitClear(vNode: VNode): void { const realNode = vNode.realNode; const cloneDom = realNode.cloneNode(false); // 复制节点后horizon的两个属性消失 - cloneDom[internalKeys.VNode] = realNode[internalKeys.VNode]; - cloneDom[internalKeys.props] = realNode[internalKeys.props]; + const customizeKeys = Object.keys; + const keyLength = customizeKeys.length; + for(let i = 0; i < keyLength; i++) { + const key = customizeKeys[i]; + cloneDom[key] = realNode(key); + } const parentObj = findDomParent(vNode); const currentParent = parentObj.parentDom; From b6d3a8f5fe742650429a1ad81c1d9315d61c0aa0 Mon Sep 17 00:00:00 2001 From: * <8> Date: Tue, 25 Jan 2022 14:53:48 +0800 Subject: [PATCH 06/14] Match-id-f0e7424b75b18470293cc3ef4a49bbe02d65caf4 --- libs/horizon/src/dom/DOMExternal.ts | 4 ++-- libs/horizon/src/dom/DOMInternalKeys.ts | 2 +- .../horizon/src/event/customEvents/CustomKeyboardEvent.ts | 2 +- .../event/simulatedEvtHandler/BeforeInputEventHandler.ts | 2 +- libs/horizon/src/renderer/render/LazyComponent.ts | 2 +- libs/horizon/src/renderer/render/SuspenseComponent.ts | 4 ++-- .../src/renderer/render/class/ClassLifeCycleProcessor.ts | 8 +++----- 7 files changed, 11 insertions(+), 13 deletions(-) diff --git a/libs/horizon/src/dom/DOMExternal.ts b/libs/horizon/src/dom/DOMExternal.ts index 02742120..e9404cb7 100644 --- a/libs/horizon/src/dom/DOMExternal.ts +++ b/libs/horizon/src/dom/DOMExternal.ts @@ -66,8 +66,8 @@ function executeRender( return getFirstCustomDom(treeRoot); } -function findDOMNode(domOrEle: Element): null | Element | Text { - if (!domOrEle) { +function findDOMNode(domOrEle?: Element): null | Element | Text { + if (domOrEle == null) { return null; } diff --git a/libs/horizon/src/dom/DOMInternalKeys.ts b/libs/horizon/src/dom/DOMInternalKeys.ts index 55621aae..309fd09b 100644 --- a/libs/horizon/src/dom/DOMInternalKeys.ts +++ b/libs/horizon/src/dom/DOMInternalKeys.ts @@ -94,7 +94,7 @@ export function getEventToListenerMap(target: EventTarget): Map Date: Tue, 25 Jan 2022 15:06:21 +0800 Subject: [PATCH 07/14] Match-id-7554e20c0f59389a4740e62cb8b1be2a00ad3543 --- libs/horizon/src/dom/DOMOperator.ts | 2 +- libs/horizon/src/renderer/ContextSaver.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/horizon/src/dom/DOMOperator.ts b/libs/horizon/src/dom/DOMOperator.ts index 9033a028..68bf83b8 100644 --- a/libs/horizon/src/dom/DOMOperator.ts +++ b/libs/horizon/src/dom/DOMOperator.ts @@ -54,7 +54,7 @@ function getChildNS(parentNS: string | null, tagName: string): string { } // 获取容器 -export function getNSCtx(dom: Container, parentNS: string, type: string): string { +export function getNSCtx(dom?: Container, parentNS: string, type: string): string { return dom ? getChildNS(dom.namespaceURI ?? null, dom.nodeName) : getChildNS(parentNS, type); } diff --git a/libs/horizon/src/renderer/ContextSaver.ts b/libs/horizon/src/renderer/ContextSaver.ts index b5200eaa..d092dd76 100644 --- a/libs/horizon/src/renderer/ContextSaver.ts +++ b/libs/horizon/src/renderer/ContextSaver.ts @@ -31,7 +31,7 @@ let ctxOldPreviousContext: Object = {}; // capture阶段设置 function setNamespaceCtx(vNode: VNode, dom?: Container) { - const nextContext = getNSCtx(dom, ctxNamespace, vNode.type); + const nextContext = getNSCtx(ctxNamespace, vNode.type, dom); vNode.setContext(CTX_NAMESPACE, ctxNamespace); ctxNamespace = nextContext; From 14ac0163a182e3d44e7fcfe77678f32258b3e913 Mon Sep 17 00:00:00 2001 From: * <8> Date: Tue, 25 Jan 2022 18:06:11 +0800 Subject: [PATCH 08/14] Match-id-faffd764b594db29fe1fde20f07ce87444d16c7e --- libs/horizon/src/dom/DOMInternalKeys.ts | 3 +- libs/horizon/src/dom/DOMOperator.ts | 8 +-- .../DOMPropertiesHandler.ts | 8 +-- .../dom/DOMPropertiesHandler/StyleHandler.ts | 34 +++++------ .../DOMPropertiesHandler/UpdateCommonProp.ts | 16 +++-- libs/horizon/src/dom/SelectionRangeHandler.ts | 8 +-- .../src/dom/validators/PropertiesData.ts | 10 ++-- .../src/dom/validators/ValidateProps.ts | 10 ++-- .../dom/valueHandler/SelectValueHandler.ts | 18 +++--- libs/horizon/src/renderer/Types.ts | 1 - .../src/renderer/taskExecutor/BrowserAsync.ts | 3 +- .../src/renderer/taskExecutor/RenderQueue.ts | 48 +++++++-------- .../src/renderer/taskExecutor/TaskExecutor.ts | 59 ++++++++++--------- .../src/renderer/taskExecutor/TaskQueue.ts | 36 +++++------ .../src/renderer/vnode/VNodeCreator.ts | 2 +- .../src/renderer/vnode/VNodeShouldUpdate.ts | 26 ++++---- 16 files changed, 145 insertions(+), 145 deletions(-) diff --git a/libs/horizon/src/dom/DOMInternalKeys.ts b/libs/horizon/src/dom/DOMInternalKeys.ts index 309fd09b..2be5522e 100644 --- a/libs/horizon/src/dom/DOMInternalKeys.ts +++ b/libs/horizon/src/dom/DOMInternalKeys.ts @@ -24,11 +24,12 @@ const internalKeys = { }; // 通过 VNode 实例获取 DOM 节点 -export function getDom(vNode: VNode): Element | Text | void { +export function getDom(vNode: VNode): Element | Text | null { const {tag} = vNode; if (tag === DomComponent || tag === DomText) { return vNode.realNode; } + return null; } // 将 VNode 属性相关信息挂到 DOM 对象的特定属性上 diff --git a/libs/horizon/src/dom/DOMOperator.ts b/libs/horizon/src/dom/DOMOperator.ts index 68bf83b8..71204206 100644 --- a/libs/horizon/src/dom/DOMOperator.ts +++ b/libs/horizon/src/dom/DOMOperator.ts @@ -5,7 +5,7 @@ import { import { createDom, } from './utils/DomCreator'; -import { getSelectionInfo, resetSelectionRange, selectionData } from './SelectionRangeHandler'; +import { getSelectionInfo, resetSelectionRange, SelectionData } from './SelectionRangeHandler'; import { isElement, isComment, isDocument, isDocumentFragment, getDomTag, shouldAutoFocus } from './utils/Common'; import { NSS } from './utils/DomCreator'; import { adjustStyleValue } from './DOMPropertiesHandler/StyleHandler'; @@ -37,7 +37,7 @@ export type Props = { export type Container = (Element & { _treeRoot?: VNode }) | (Document & { _treeRoot?: VNode }); -let selectionInfo: null | selectionData = null; +let selectionInfo: null | SelectionData = null; function getChildNS(parentNS: string | null, tagName: string): string { if (parentNS === NSS.svg && tagName === 'foreignObject') { @@ -54,12 +54,12 @@ function getChildNS(parentNS: string | null, tagName: string): string { } // 获取容器 -export function getNSCtx(dom?: Container, parentNS: string, type: string): string { +export function getNSCtx(parentNS: string, type: string, dom: Container | undefined): string { return dom ? getChildNS(dom.namespaceURI ?? null, dom.nodeName) : getChildNS(parentNS, type); } export function prepareForSubmit(): void { - selectionInfo = getSelectionInfo(); + selectionInfo = getSelectionInfo(); } export function resetAfterSubmit(): void { diff --git a/libs/horizon/src/dom/DOMPropertiesHandler/DOMPropertiesHandler.ts b/libs/horizon/src/dom/DOMPropertiesHandler/DOMPropertiesHandler.ts index 5016d811..5ec79c33 100644 --- a/libs/horizon/src/dom/DOMPropertiesHandler/DOMPropertiesHandler.ts +++ b/libs/horizon/src/dom/DOMPropertiesHandler/DOMPropertiesHandler.ts @@ -8,7 +8,7 @@ import { } from '../../event/EventBinding'; import { isEventProp, isNativeElement } from '../validators/ValidateProps'; -function updateOneProp(dom, propName, propVal, isNativeTag, isInit?: boolean) { +function updateOneProp(dom, propName, isNativeTag, propVal?, isInit?: boolean) { if (propName === 'style') { setStyles(dom, propVal); } else if (propName === 'dangerouslySetInnerHTML') { @@ -42,7 +42,7 @@ export function setDomProps( const propName = keysOfProps[i]; const propVal = props[propName]; - updateOneProp(dom, propName, propVal, isNativeTag, true); + updateOneProp(dom, propName, isNativeTag, propVal, true); } } @@ -55,7 +55,7 @@ export function updateDomProps( for (let i = 0; i < changeList.length; i++) { const { propName, propVal } = changeList[i]; - updateOneProp(dom, propName, propVal, isNativeTag); + updateOneProp(dom, propName, isNativeTag, propVal); } } @@ -111,7 +111,7 @@ export function compareProps( for (let i = 0; i < keysOfNewProps.length; i++) { const propName = keysOfNewProps[i]; const newPropValue = newProps[propName]; - const oldPropValue = oldProps != null ? oldProps[propName] : undefined; + const oldPropValue = oldProps != null ? oldProps[propName] : null; if (newPropValue === oldPropValue || (newPropValue == null && oldPropValue == null)) { // 新旧属性值未发生变化,或者新旧属性皆为空值,不需要进行处理 diff --git a/libs/horizon/src/dom/DOMPropertiesHandler/StyleHandler.ts b/libs/horizon/src/dom/DOMPropertiesHandler/StyleHandler.ts index 23e1b91c..ae576bfe 100644 --- a/libs/horizon/src/dom/DOMPropertiesHandler/StyleHandler.ts +++ b/libs/horizon/src/dom/DOMPropertiesHandler/StyleHandler.ts @@ -1,3 +1,20 @@ +/** + * 对一些没有写单位的样式进行适配,例如:width: 10 => width: 10px + * 对空值或布尔值进行适配,转为空字符串 + * 去掉多余空字符 + */ + export function adjustStyleValue(name, value) { + let validValue = value; + + if (typeof value === 'number' && value !== 0 && isNeedUnitCSS(name)) { + validValue = `${value}px`; + } else if (value === '' || value == null || typeof value === 'boolean') { + validValue = ''; + } + + return validValue; +} + /** * 设置 DOM 节点的 style 属性 */ @@ -32,20 +49,3 @@ function isNeedUnitCSS(propName: string) { || propName.startsWith('box') || propName.endsWith('Opacity')); } - -/** - * 对一些没有写单位的样式进行适配,例如:width: 10 => width: 10px - * 对空值或布尔值进行适配,转为空字符串 - * 去掉多余空字符 - */ -export function adjustStyleValue(name, value) { - let validValue = value; - - if (typeof value === 'number' && value !== 0 && isNeedUnitCSS(name)) { - validValue = `${value}px`; - } else if (value === '' || value == null || typeof value === 'boolean') { - validValue = ''; - } - - return validValue; -} diff --git a/libs/horizon/src/dom/DOMPropertiesHandler/UpdateCommonProp.ts b/libs/horizon/src/dom/DOMPropertiesHandler/UpdateCommonProp.ts index 5ac5e058..a2fe3e9d 100644 --- a/libs/horizon/src/dom/DOMPropertiesHandler/UpdateCommonProp.ts +++ b/libs/horizon/src/dom/DOMPropertiesHandler/UpdateCommonProp.ts @@ -18,6 +18,13 @@ const svgHumpAttr = new Set(['allowReorder', 'autoReverse', 'baseFrequency', 'ba 'stitchTiles', 'surfaceScale','systemLanguage', 'tableValues', 'targetX', 'targetY', 'textLength','viewBox', 'viewTarget', 'xChannelSelector','yChannelSelector', 'zoomAndPan']); +// 驼峰 变 “-” +function convertToLowerCase(str) { + const replacer = (match, char) => `-${char.toLowerCase()}`; + + return str.replace(/([A-Z])/g, replacer); +} + /** * 给 dom 设置属性 * attrName 指代码中属性设置的属性名称(如 class) @@ -70,12 +77,3 @@ export function updateCommonProp(dom: Element, attrName: string, value: any, isN } } } - -// 驼峰 变 “-” -function convertToLowerCase(str) { - const replacer = (match, char) => { - return `-${char.toLowerCase()}`; - } - - return str.replace(/([A-Z])/g, replacer); -}; diff --git a/libs/horizon/src/dom/SelectionRangeHandler.ts b/libs/horizon/src/dom/SelectionRangeHandler.ts index f498174d..62931335 100644 --- a/libs/horizon/src/dom/SelectionRangeHandler.ts +++ b/libs/horizon/src/dom/SelectionRangeHandler.ts @@ -105,16 +105,16 @@ export function getSelectionInfo() { }; } -export interface selectionData { +export interface SelectionData { focusedDom: HTMLInputElement | HTMLTextAreaElement | void; selectionRange: { - start: number; - end: number; + start: number | null; + end: number | null; }; } // 防止选择范围内的信息因为节点删除或其他原因导致的信息丢失 -export function resetSelectionRange(preSelectionRangeData: selectionData) { +export function resetSelectionRange(preSelectionRangeData: SelectionData) { // 当前 focus 的元素 const currentFocusedDom = getIFrameFocusedDom(); diff --git a/libs/horizon/src/dom/validators/PropertiesData.ts b/libs/horizon/src/dom/validators/PropertiesData.ts index 9eb90b7b..70645aae 100644 --- a/libs/horizon/src/dom/validators/PropertiesData.ts +++ b/libs/horizon/src/dom/validators/PropertiesData.ts @@ -6,7 +6,7 @@ export enum PROPERTY_TYPE { STRING, // 普通的字符串类型 SPECIAL, // 需要特殊处理的属性类型 BOOLEAN_STR, // 字符串类型的 true false -}; +} export type PropDetails = { propName: string; @@ -15,10 +15,6 @@ export type PropDetails = { attrNS: string | null; }; -export function getPropDetails(name: string): PropDetails | null { - return propsDetailData[name] || null; -} - // 属性相关数据 // 依次为 propertyName、type、attributeName、attributeNamespace,不填则使用默认值 // type 默认 STRING @@ -123,3 +119,7 @@ propertiesData.forEach(record => { attrNS, }; }); + +export function getPropDetails(name: string): PropDetails | null { + return propsDetailData[name] || null; +} diff --git a/libs/horizon/src/dom/validators/ValidateProps.ts b/libs/horizon/src/dom/validators/ValidateProps.ts index dddd8175..5616f342 100644 --- a/libs/horizon/src/dom/validators/ValidateProps.ts +++ b/libs/horizon/src/dom/validators/ValidateProps.ts @@ -34,6 +34,11 @@ function isInvalidBoolean( return false; } +// 是事件属性 +export function isEventProp(propName) { + return propName.substr(0, 2) === 'on'; +} + function isValidProp(tagName, name, value) { // 校验事件名称 if (isEventProp(name)) { @@ -79,11 +84,6 @@ export function isInvalidValue( return false; } -// 是事件属性 -export function isEventProp(propName) { - return propName.substr(0, 2) === 'on'; -} - // dev模式下校验属性是否合法 export function validateProps(type, props) { if (!props) { diff --git a/libs/horizon/src/dom/valueHandler/SelectValueHandler.ts b/libs/horizon/src/dom/valueHandler/SelectValueHandler.ts index 6602e876..75812620 100644 --- a/libs/horizon/src/dom/valueHandler/SelectValueHandler.ts +++ b/libs/horizon/src/dom/valueHandler/SelectValueHandler.ts @@ -1,14 +1,5 @@ import {HorizonSelect, IProperty} from '../utils/Interface'; -// 更新 ] { + S, initArg: P, init?: (P) => S, isUseState?: boolean): [S, Trigger] | void { const stage = getHookStage(); if (stage === null) { throwNotInFuncError(); diff --git a/libs/horizon/src/renderer/render/Fragment.ts b/libs/horizon/src/renderer/render/Fragment.ts index a64c2532..27c2b5c1 100644 --- a/libs/horizon/src/renderer/render/Fragment.ts +++ b/libs/horizon/src/renderer/render/Fragment.ts @@ -1,10 +1,6 @@ import type {VNode} from '../Types'; import {createVNodeChildren} from './BaseComponent'; -export function captureRender(processing: VNode): VNode | null { - return captureFragment(processing); -} - export function bubbleRender() {} function captureFragment(processing: VNode) { @@ -12,3 +8,7 @@ function captureFragment(processing: VNode) { processing.child = createVNodeChildren(processing, newElement); return processing.child; } + +export function captureRender(processing: VNode): VNode | null { + return captureFragment(processing); +} diff --git a/libs/horizon/src/renderer/render/MemoComponent.ts b/libs/horizon/src/renderer/render/MemoComponent.ts index fd273249..d4a10d22 100644 --- a/libs/horizon/src/renderer/render/MemoComponent.ts +++ b/libs/horizon/src/renderer/render/MemoComponent.ts @@ -10,10 +10,6 @@ import { } from '../../external/JSXElementType'; import {Fragment} from '../vnode/VNodeTags'; -export function captureRender(processing: VNode, shouldUpdate: boolean): VNode | null { - return captureMemoComponent(processing, shouldUpdate); -} - export function bubbleRender() {} export function captureMemoComponent( @@ -59,3 +55,7 @@ export function captureMemoComponent( return newChild; } + +export function captureRender(processing: VNode, shouldUpdate: boolean): VNode | null { + return captureMemoComponent(processing, shouldUpdate); +} diff --git a/libs/horizon/src/renderer/submit/LifeCycleHandler.ts b/libs/horizon/src/renderer/submit/LifeCycleHandler.ts index 68503458..9dafedd5 100644 --- a/libs/horizon/src/renderer/submit/LifeCycleHandler.ts +++ b/libs/horizon/src/renderer/submit/LifeCycleHandler.ts @@ -225,10 +225,10 @@ function unmountVNode(vNode: VNode): void { function unmountNestedVNodes(vNode: VNode): void { travelVNodeTree(vNode, (node) => { unmountVNode(node); - }, (node) => { + }, node => // 如果是DomPortal,不需要遍历child - return node.tag === DomPortal; - }); + node.tag === DomPortal + ); } function submitAddition(vNode: VNode): void { @@ -296,10 +296,9 @@ function unmountDomComponents(vNode: VNode): void { } else { unmountVNode(node); } - }, (node) => { + }, node => // 如果是dom不用再遍历child - return node.tag === DomComponent || node.tag === DomText; - }, null, (node) => { + node.tag === DomComponent || node.tag === DomText, null, (node) => { if (node.tag === DomPortal) { // 当离开portal,需要重新设置parent currentParentIsValid = false; diff --git a/libs/horizon/src/renderer/taskExecutor/RenderQueue.ts b/libs/horizon/src/renderer/taskExecutor/RenderQueue.ts index 48b3fc72..31760489 100644 --- a/libs/horizon/src/renderer/taskExecutor/RenderQueue.ts +++ b/libs/horizon/src/renderer/taskExecutor/RenderQueue.ts @@ -12,6 +12,16 @@ let callingQueueTask: any | null = null; // 防止重入 let isCallingRenderQueue = false; +export function callRenderQueueImmediate() { + if (callingQueueTask !== null) { + // 取消异步调度 + cancelTask(callingQueueTask); + callingQueueTask = null; + } + + callRenderQueue(); +} + // 执行render回调 function callRenderQueue() { if (!isCallingRenderQueue && renderQueue !== null) { @@ -54,13 +64,3 @@ export function pushRenderCallback(callback: RenderCallback) { // 返回一个空对象,用于区别null return {}; } - -export function callRenderQueueImmediate() { - if (callingQueueTask !== null) { - // 取消异步调度 - cancelTask(callingQueueTask); - callingQueueTask = null; - } - - callRenderQueue(); -} diff --git a/libs/horizon/src/renderer/utils/compare.ts b/libs/horizon/src/renderer/utils/compare.ts index 3849ec85..e6fb69a1 100644 --- a/libs/horizon/src/renderer/utils/compare.ts +++ b/libs/horizon/src/renderer/utils/compare.ts @@ -42,9 +42,9 @@ export function shallowCompare(paramX: any, paramY: any): boolean { return false; } - return keysX.every((key, i) => { - return Object.prototype.hasOwnProperty.call(paramY, key) && isSame(paramX[key], paramY[keysX[i]]); - }); + return keysX.every((key, i) => + Object.prototype.hasOwnProperty.call(paramY, key) && isSame(paramX[key], paramY[keysX[i]]) + ); } return false;