From 571fabd4596770522374ee9b167225ea8a1ae0f7 Mon Sep 17 00:00:00 2001 From: * <8> Date: Fri, 21 Jan 2022 10:25:49 +0800 Subject: [PATCH 01/23] Match-id-f4bad54d19ae92cb4575ca64e04a176d3def5a37 --- libs/horizon/src/renderer/submit/LifeCycleHandler.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libs/horizon/src/renderer/submit/LifeCycleHandler.ts b/libs/horizon/src/renderer/submit/LifeCycleHandler.ts index 46260ac0..d2c19ee2 100644 --- a/libs/horizon/src/renderer/submit/LifeCycleHandler.ts +++ b/libs/horizon/src/renderer/submit/LifeCycleHandler.ts @@ -71,7 +71,7 @@ function callBeforeSubmitLifeCycles( const root = vNode.realNode; clearContainer(root.outerDom); } - + // No Default } } @@ -160,7 +160,7 @@ function hideOrUnhideAllChildren(vNode, isHidden) { function attachRef(vNode: VNode) { const ref = vNode.ref; - if (ref !== null) { + if (ref !== null && ref !== undefined) { const instance = vNode.realNode; let refType = typeof ref; @@ -179,7 +179,7 @@ function attachRef(vNode: VNode) { function detachRef(vNode: VNode, isOldRef?: boolean) { let ref = (isOldRef ? vNode.oldRef : vNode.ref); - if (ref !== null) { + if (ref !== null && ref !== undefined) { let refType = typeof ref; if (refType === 'function') { From 9c23f1be7ed6fc867d3accd2384f1706a166c354 Mon Sep 17 00:00:00 2001 From: * <8> Date: Sat, 22 Jan 2022 11:22:33 +0800 Subject: [PATCH 02/23] Match-id-7c9f92f0e54002e1e320dd2556d80d5da3b140f1 --- libs/horizon/src/renderer/TreeBuilder.ts | 4 +- .../src/renderer/submit/LifeCycleHandler.ts | 31 +++----- libs/horizon/src/renderer/submit/Submit.ts | 5 +- .../src/renderer/taskExecutor/BrowserAsync.ts | 36 +++------- .../src/renderer/taskExecutor/RenderQueue.ts | 4 +- .../src/renderer/taskExecutor/TaskExecutor.ts | 72 +++++-------------- .../src/renderer/taskExecutor/TaskQueue.ts | 17 +++-- 7 files changed, 51 insertions(+), 118 deletions(-) diff --git a/libs/horizon/src/renderer/TreeBuilder.ts b/libs/horizon/src/renderer/TreeBuilder.ts index 4f89f7a4..ad709044 100644 --- a/libs/horizon/src/renderer/TreeBuilder.ts +++ b/libs/horizon/src/renderer/TreeBuilder.ts @@ -172,6 +172,8 @@ function buildVNodeTree(treeRoot: VNode) { } } while (true); + processing = null; + setExecuteMode(preMode); } @@ -217,7 +219,7 @@ export function launchUpdateFromVNode(vNode: VNode) { } // 保存待刷新的节点 - treeRoot.toUpdateNodes.add(vNode); + treeRoot.toUpdateNodes?.add(vNode); if (checkMode(BySync) && // 非批量 !checkMode(InRender)) { // 不是渲染阶段触发 diff --git a/libs/horizon/src/renderer/submit/LifeCycleHandler.ts b/libs/horizon/src/renderer/submit/LifeCycleHandler.ts index d2c19ee2..5e2fd84b 100644 --- a/libs/horizon/src/renderer/submit/LifeCycleHandler.ts +++ b/libs/horizon/src/renderer/submit/LifeCycleHandler.ts @@ -160,39 +160,26 @@ function hideOrUnhideAllChildren(vNode, isHidden) { function attachRef(vNode: VNode) { const ref = vNode.ref; - if (ref !== null && ref !== undefined) { - const instance = vNode.realNode; - - let refType = typeof ref; - if (refType === 'function') { - ref(instance); - } else if (refType === 'object') { - (ref).current = instance; - } else { - if (vNode.belongClassVNode && vNode.belongClassVNode.realNode) { - vNode.belongClassVNode.realNode.refs[String(ref)] = instance; - } - } - } + handleRef(vNode, ref, vNode.realNode); } function detachRef(vNode: VNode, isOldRef?: boolean) { let ref = (isOldRef ? vNode.oldRef : vNode.ref); + handleRef(vNode, ref, null); +} + +function handleRef(vNode: VNode, ref, val) { if (ref !== null && ref !== undefined) { - let refType = typeof ref; + const refType = typeof ref; if (refType === 'function') { - try { - ref(null); - } catch (error) { - handleSubmitError(vNode, error); - } + ref(val); } else if (refType === 'object') { - (ref).current = null; + (ref).current = val; } else { if (vNode.belongClassVNode && vNode.belongClassVNode.realNode) { - vNode.belongClassVNode.realNode.refs[String(ref)] = null; + vNode.belongClassVNode.realNode.refs[String(ref)] = val; } } } diff --git a/libs/horizon/src/renderer/submit/Submit.ts b/libs/horizon/src/renderer/submit/Submit.ts index 774c7386..e76f0b97 100644 --- a/libs/horizon/src/renderer/submit/Submit.ts +++ b/libs/horizon/src/renderer/submit/Submit.ts @@ -35,13 +35,10 @@ let lastRoot: VNode | null = null; export function submitToRender(treeRoot) { treeRoot.shouldUpdate = treeRoot.childShouldUpdate; - - const startVNode = getStartVNode(); - // 置空task,让才能加入新的render任务 treeRoot.task = null; - setProcessing(null); + const startVNode = getStartVNode(); if (FlagUtils.hasAnyFlag(startVNode)) { // 把自己加上 diff --git a/libs/horizon/src/renderer/taskExecutor/BrowserAsync.ts b/libs/horizon/src/renderer/taskExecutor/BrowserAsync.ts index 185ec51d..4633625f 100644 --- a/libs/horizon/src/renderer/taskExecutor/BrowserAsync.ts +++ b/libs/horizon/src/renderer/taskExecutor/BrowserAsync.ts @@ -2,65 +2,45 @@ * 浏览器相关实现 */ -export let now; - -if (typeof performance === 'object' && typeof performance.now === 'function') { - const localPerformance = performance; - now = () => localPerformance.now(); -} else { - const localDate = Date; - const initialTime = localDate.now(); - now = () => localDate.now() - initialTime; -} - let isMessageLoopRunning = false; let browserCallback = null; -// 默认每次只运行5ms -const runTime = 5; -let deadline = 0; export function isOverTime() { - return now() >= deadline; + return false; } // 1、设置deadline;2、回调TaskExecutor传过来的browserCallback const callRenderTasks = () => { - if (browserCallback == null) { + if (browserCallback === null) { return; } - const currentTime = now(); - // 计算deadline - deadline = currentTime + runTime; try { // 执行callback - const hasMoreTask = browserCallback( - currentTime, - ); + const hasMoreTask = browserCallback(); if (!hasMoreTask) { // 没有更多task isMessageLoopRunning = false; browserCallback = null; } else { // 还有task,继续调用 - port.postMessage(null); + port2.postMessage(null); } } catch (error) { - port.postMessage(null); + port2.postMessage(null); throw error; } }; -const channel = new MessageChannel(); -const port = channel.port2; -channel.port1.onmessage = callRenderTasks; +const { port1, port2 } = new MessageChannel(); +port1.onmessage = callRenderTasks; export function requestBrowserCallback(callback) { browserCallback = callback; if (!isMessageLoopRunning) { isMessageLoopRunning = true; - port.postMessage(null); + port2.postMessage(null); } } diff --git a/libs/horizon/src/renderer/taskExecutor/RenderQueue.ts b/libs/horizon/src/renderer/taskExecutor/RenderQueue.ts index 125f0a17..0db72fcb 100644 --- a/libs/horizon/src/renderer/taskExecutor/RenderQueue.ts +++ b/libs/horizon/src/renderer/taskExecutor/RenderQueue.ts @@ -46,9 +46,7 @@ function callRenderQueue() { try { for (; i < renderQueue.length; i++) { let callback = renderQueue[i]; - do { - callback = callback(); - } while (callback !== null); + callback(); } renderQueue = null; } catch (error) { diff --git a/libs/horizon/src/renderer/taskExecutor/TaskExecutor.ts b/libs/horizon/src/renderer/taskExecutor/TaskExecutor.ts index f5a8b986..fa0a3294 100644 --- a/libs/horizon/src/renderer/taskExecutor/TaskExecutor.ts +++ b/libs/horizon/src/renderer/taskExecutor/TaskExecutor.ts @@ -5,10 +5,9 @@ import { requestBrowserCallback, isOverTime, - now, } from './BrowserAsync'; -import {add, shift, first} from './TaskQueue'; +import { add, shift, first, remove } from './TaskQueue'; const ImmediatePriority = 1; const NormalPriority = 10; @@ -16,89 +15,65 @@ const NormalPriority = 10; // 用于控制插入任务的顺序 let idCounter = 1; -let currentPriorityLevel = NormalPriority; - // 正在执行task let isProcessing = false; // 调度中,等待浏览器回调 -let isScheduling = false; - -function runSync(callback, priorityLevel = NormalPriority) { - const previousPriorityLevel = currentPriorityLevel; - currentPriorityLevel = priorityLevel; - - try { - return callback(); - } finally { - currentPriorityLevel = previousPriorityLevel; - } -} +let isWaiting = false; function runAsync(callback, priorityLevel= NormalPriority ) { - let timeout; + let increment; switch (priorityLevel) { case ImmediatePriority: - timeout = -1; + increment = -1; break; case NormalPriority: default: - timeout = 5000; + increment = 10000; break; } const task = { id: idCounter++, callback, - priorityLevel, - expirationTime: now() + timeout, + order: idCounter + increment, }; add(task); - if (!isScheduling && !isProcessing) { - isScheduling = true; + if (!isWaiting && !isProcessing) { + isWaiting = true; requestBrowserCallback(callTasks); } return task; } -function callTasks(initialTime) { - isScheduling = false; +function callTasks() { + isWaiting = false; isProcessing = true; let task = null; - const previousPriorityLevel = currentPriorityLevel; try { - let currentTime = initialTime; task = first(); // 循环执行task while (task !== null) { - if ( - task.expirationTime > currentTime && - isOverTime() - ) { - // 任务的过期时间大于当前时间(没达到此任务的过期时间)且超过了deadline + if (isOverTime()) { + // 超过了deadline break; } const callback = task.callback; - if (typeof callback === 'function') { + if (callback !== null) { task.callback = null; - currentPriorityLevel = task.priorityLevel; - const didUserCallbackTimeout = task.expirationTime <= currentTime; - const continuationCallback = callback(didUserCallbackTimeout); - currentTime = now(); - // 执行callback返回函数,重置callback - if (typeof continuationCallback === 'function') { - task.callback = continuationCallback; - } else { - if (task === first()) { - shift(); - } + callback(); + + if (task === first()) { + shift(); + } else { // 执行任务中可能插入了新任务 + remove(task); } } else { shift(); @@ -110,8 +85,6 @@ function callTasks(initialTime) { // 返回是否还有任务,如果有,说明是被中断了 return task !== null; } finally { - task = null; - currentPriorityLevel = previousPriorityLevel; isProcessing = false; } } @@ -120,16 +93,9 @@ function cancelTask(task) { task.callback = null; } -function getCurrentPriorityLevel() { - return currentPriorityLevel; -} - export { ImmediatePriority, NormalPriority, - runSync, runAsync, cancelTask, - getCurrentPriorityLevel, - now, }; diff --git a/libs/horizon/src/renderer/taskExecutor/TaskQueue.ts b/libs/horizon/src/renderer/taskExecutor/TaskQueue.ts index 708e9bdf..bfb98f79 100644 --- a/libs/horizon/src/renderer/taskExecutor/TaskQueue.ts +++ b/libs/horizon/src/renderer/taskExecutor/TaskQueue.ts @@ -5,7 +5,7 @@ type Queue = Array; type Node = { id: number; - expirationTime: number; + order: number; }; // 任务队列 @@ -15,9 +15,7 @@ export function add(node: Node): void { // 查找第一个大于等于 value 的下标,都比 value 小则返回 -1 const idx = getBiggerIdx(node); - if (idx === 0) { - taskQueue.unshift(node); - } else if (idx === -1) { + if (idx === -1) { taskQueue.push(node); } else { taskQueue.splice(idx, 0, node); @@ -32,10 +30,11 @@ function getBiggerIdx(node: Node) { while (left <= right) { const middle = left + ((right - left) >> 1); - if (compare(taskQueue[middle], node) > 0) + if (compare(taskQueue[middle], node) > 0) { right = middle - 1; - else + } else { left = middle + 1; + } } return (left < taskQueue.length) ? left : -1; @@ -51,8 +50,12 @@ export function shift(): Node | null { return val !== undefined ? val : null; } +export function remove(node: Node) { + taskQueue.splice(taskQueue.indexOf(node), 1); +} + function compare(a: Node, b: Node) { // 优先先用index排序,其次用id - const diff = a.expirationTime - b.expirationTime; + const diff = a.order - b.order; return diff !== 0 ? diff : a.id - b.id; } From fa55d306c2b239e1edbf773d43e033331757357a Mon Sep 17 00:00:00 2001 From: * <8> Date: Mon, 24 Jan 2022 10:39:44 +0800 Subject: [PATCH 03/23] 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 04/23] 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 05/23] 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 06/23] 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 07/23] 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 08/23] 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 09/23] 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 10/23] 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; From 21b1aa3a565d5f4b5dfb698a18c230be775a660a Mon Sep 17 00:00:00 2001 From: * <8> Date: Wed, 26 Jan 2022 17:43:53 +0800 Subject: [PATCH 22/23] Match-id-c9a7495e252e4e9c279e1a95f9d466f6985902e5 --- libs/horizon/src/renderer/ErrorHandler.ts | 7 ++++--- libs/horizon/src/renderer/TreeBuilder.ts | 10 ++-------- .../src/renderer/render/SuspenseComponent.ts | 4 ++-- libs/horizon/src/renderer/submit/Submit.ts | 4 ++-- .../src/renderer/taskExecutor/RenderQueue.ts | 14 +++----------- 5 files changed, 13 insertions(+), 26 deletions(-) diff --git a/libs/horizon/src/renderer/ErrorHandler.ts b/libs/horizon/src/renderer/ErrorHandler.ts index f4e66484..0e472f16 100644 --- a/libs/horizon/src/renderer/ErrorHandler.ts +++ b/libs/horizon/src/renderer/ErrorHandler.ts @@ -8,10 +8,11 @@ import type {Update} from './UpdateHandler'; import {ClassComponent, TreeRoot} from './vnode/VNodeTags'; import {FlagUtils, Interrupted} from './vnode/VNodeFlags'; import {newUpdate, UpdateState, pushUpdate} from './UpdateHandler'; -import {launchUpdateFromVNode, setBuildResultError, tryRenderRoot} from './TreeBuilder'; +import {launchUpdateFromVNode, tryRenderFromRoot} from './TreeBuilder'; import {setRootThrowError} from './submit/Submit'; import {handleSuspenseChildThrowError} from './render/SuspenseComponent'; import {updateShouldUpdateOfTree} from './vnode/VNodeShouldUpdate'; +import {BuildErrored, setBuildResult} from './GlobalVar'; // 处理capture和bubble阶段抛出的错误 export function handleRenderThrowError( @@ -33,7 +34,7 @@ export function handleRenderThrowError( } // 抛出错误无法作为suspense内容处理(或无suspense来处理),这次当成真的错误来处理 - setBuildResultError(); + setBuildResult(BuildErrored); // 向上遍历寻找ClassComponent组件(同时也是Error Boundaries组件) 或者 TreeRoot let vNode = sourceVNode.parent; @@ -167,7 +168,7 @@ function triggerUpdate(vNode, state) { const root = updateShouldUpdateOfTree(vNode); if (root !== null) { - tryRenderRoot(root); + tryRenderFromRoot(root); } } diff --git a/libs/horizon/src/renderer/TreeBuilder.ts b/libs/horizon/src/renderer/TreeBuilder.ts index 7d6201a2..0c7aacd8 100644 --- a/libs/horizon/src/renderer/TreeBuilder.ts +++ b/libs/horizon/src/renderer/TreeBuilder.ts @@ -251,7 +251,7 @@ function renderFromRoot(treeRoot) { } // 尝试去渲染,已有任务就跳出 -export function tryRenderRoot(treeRoot: VNode) { +export function tryRenderFromRoot(treeRoot: VNode) { if (treeRoot.shouldUpdate && treeRoot.task === null) { // 任务放进queue,但是调度开始还是异步的 treeRoot.task = pushRenderCallback( @@ -283,7 +283,7 @@ export function launchUpdateFromVNode(vNode: VNode) { // 不能改成下面的异步,否则会有时序问题,因为业务可能会依赖这个渲染的完成。 renderFromRoot(treeRoot); } else { - tryRenderRoot(treeRoot); + tryRenderFromRoot(treeRoot); if (!isExecuting()) { // 同步执行 @@ -313,12 +313,6 @@ function getChildByIndex(vNode: VNode, idx: number) { return node; } -export function setBuildResultError() { - if (getBuildResult() !== BuildCompleted) { - setBuildResult(BuildErrored); - } -} - // ============================== HorizonDOM使用 ============================== export function runDiscreteUpdates() { if (checkMode(ByAsync) || checkMode(InRender)) { diff --git a/libs/horizon/src/renderer/render/SuspenseComponent.ts b/libs/horizon/src/renderer/render/SuspenseComponent.ts index d0cb6f9e..47d117a8 100644 --- a/libs/horizon/src/renderer/render/SuspenseComponent.ts +++ b/libs/horizon/src/renderer/render/SuspenseComponent.ts @@ -9,7 +9,7 @@ import { Fragment, } from '../vnode/VNodeTags'; import {pushForceUpdate} from '../UpdateHandler'; -import {launchUpdateFromVNode, tryRenderRoot} from '../TreeBuilder'; +import {launchUpdateFromVNode, tryRenderFromRoot} from '../TreeBuilder'; import {updateShouldUpdateOfTree} from '../vnode/VNodeShouldUpdate'; import {getContextChangeCtx} from '../ContextSaver'; @@ -234,7 +234,7 @@ function resolvePromise(suspenseVNode: VNode, promise: PromiseType) { suspenseVNode.promiseResolve = true; const root = updateShouldUpdateOfTree(suspenseVNode); if (root !== null) { - tryRenderRoot(root); + tryRenderFromRoot(root); } } diff --git a/libs/horizon/src/renderer/submit/Submit.ts b/libs/horizon/src/renderer/submit/Submit.ts index e76f0b97..8bdf1266 100644 --- a/libs/horizon/src/renderer/submit/Submit.ts +++ b/libs/horizon/src/renderer/submit/Submit.ts @@ -11,7 +11,7 @@ import { callBeforeSubmitLifeCycles, submitDeletion, submitAddition, submitResetTextContent, submitUpdate, detachRef, } from './LifeCycleHandler'; -import {tryRenderRoot, setProcessing} from '../TreeBuilder'; +import {tryRenderFromRoot, setProcessing} from '../TreeBuilder'; import { BySync, InRender, @@ -78,7 +78,7 @@ export function submitToRender(treeRoot) { countLoopingUpdate(treeRoot); // 在退出`submit` 之前始终调用此函数,以确保任何已计划在此根上执行的update被执行。 - tryRenderRoot(treeRoot); + tryRenderFromRoot(treeRoot); if (rootThrowError) { const error = rootThrowError; diff --git a/libs/horizon/src/renderer/taskExecutor/RenderQueue.ts b/libs/horizon/src/renderer/taskExecutor/RenderQueue.ts index 0db72fcb..9950b369 100644 --- a/libs/horizon/src/renderer/taskExecutor/RenderQueue.ts +++ b/libs/horizon/src/renderer/taskExecutor/RenderQueue.ts @@ -42,22 +42,14 @@ function callRenderQueue() { // 防止重入 isCallingRenderQueue = true; - let i = 0; try { - for (; i < renderQueue.length; i++) { - let callback = renderQueue[i]; + let callback; + while (callback = renderQueue.shift()) { callback(); } + renderQueue = null; } catch (error) { - // 如果有异常抛出,请将剩余的回调留在队列中 - if (renderQueue !== null) { - renderQueue = renderQueue.slice(i + 1); - } - - // 在下一个异步中再调用 - runAsync(callRenderQueueImmediate, ImmediatePriority); - throw error; } finally { isCallingRenderQueue = false; From 1ea19e1f65db9edb308d0823180599932ab3b050 Mon Sep 17 00:00:00 2001 From: * <8> Date: Thu, 27 Jan 2022 10:21:33 +0800 Subject: [PATCH 23/23] Match-id-b24a47ba6afc9d27a78eb451bef46b40cc18f848 --- libs/horizon/src/renderer/ErrorHandler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/horizon/src/renderer/ErrorHandler.ts b/libs/horizon/src/renderer/ErrorHandler.ts index db190048..3c3305c1 100644 --- a/libs/horizon/src/renderer/ErrorHandler.ts +++ b/libs/horizon/src/renderer/ErrorHandler.ts @@ -134,7 +134,7 @@ function triggerUpdate(vNode, state) { const root = updateShouldUpdateOfTree(vNode); if (root !== null) { - tryRenderRoot(root); + tryRenderFromRoot(root); } }