From a72fb10ef3fc66d224239810d56d9f120c68f5f9 Mon Sep 17 00:00:00 2001 From: * <*> Date: Fri, 2 Jun 2023 16:44:47 +0800 Subject: [PATCH 1/3] Match-id-c3d530104af803ccd0d7aef09548027b4c8d60f7 --- libs/horizon/src/dom/SelectionRangeHandler.ts | 4 +- libs/horizon/src/dom/utils/Common.ts | 4 + .../src/dom/validators/ValidateProps.ts | 5 +- .../src/dom/valueHandler/InputValueHandler.ts | 9 +- .../dom/valueHandler/SelectValueHandler.ts | 7 +- .../dom/valueHandler/TextareaValueHandler.ts | 5 +- libs/horizon/src/event/EventBinding.ts | 21 ++-- libs/horizon/src/event/FormValueController.ts | 6 +- libs/horizon/src/event/ListenerGetter.ts | 95 ++++++++++--------- .../src/renderer/diff/nodeDiffComparator.ts | 10 +- 10 files changed, 88 insertions(+), 78 deletions(-) diff --git a/libs/horizon/src/dom/SelectionRangeHandler.ts b/libs/horizon/src/dom/SelectionRangeHandler.ts index e09e0dad..a8f643c1 100644 --- a/libs/horizon/src/dom/SelectionRangeHandler.ts +++ b/libs/horizon/src/dom/SelectionRangeHandler.ts @@ -17,7 +17,7 @@ * 处理文本框、输入框中框选范围内的数据 */ -import { getIFrameFocusedDom, isText } from './utils/Common'; +import { getIFrameFocusedDom, isNull, isText } from './utils/Common'; import { isElement } from './utils/Common'; @@ -30,7 +30,7 @@ function setSelectionRange(dom: HTMLInputElement | HTMLTextAreaElement, range) { const { start, end } = range; let realEnd = end; - if (realEnd == null) { + if (isNull(realEnd)) { realEnd = start; } diff --git a/libs/horizon/src/dom/utils/Common.ts b/libs/horizon/src/dom/utils/Common.ts index 30c172cf..001a32f1 100644 --- a/libs/horizon/src/dom/utils/Common.ts +++ b/libs/horizon/src/dom/utils/Common.ts @@ -84,3 +84,7 @@ const types = ['button', 'input', 'select', 'textarea']; export function shouldAutoFocus(tagName: string, props: Props): boolean { return types.includes(tagName) ? Boolean(props.autoFocus) : false; } + +export function isNull(val) { + return val === null || val === undefined; +} diff --git a/libs/horizon/src/dom/validators/ValidateProps.ts b/libs/horizon/src/dom/validators/ValidateProps.ts index f025ce63..9eabcc09 100644 --- a/libs/horizon/src/dom/validators/ValidateProps.ts +++ b/libs/horizon/src/dom/validators/ValidateProps.ts @@ -14,6 +14,7 @@ */ import { getPropDetails, PROPERTY_TYPE, PropDetails } from './PropertiesData'; +import { isNull } from '../utils/Common'; const INVALID_EVENT_NAME_REGEX = /^on[^A-Z]/; @@ -73,7 +74,7 @@ export function isInvalidValue( propDetails: PropDetails | null, isNativeTag: boolean ): boolean { - if (value == null) { + if (isNull(value)) { return true; } @@ -104,7 +105,7 @@ export function validateProps(type, props) { } // style属性必须是对象 - if (props.style != null && typeof props.style !== 'object') { + if (!isNull(props.style) && typeof props.style !== 'object') { throw new Error('style should be a object.'); } diff --git a/libs/horizon/src/dom/valueHandler/InputValueHandler.ts b/libs/horizon/src/dom/valueHandler/InputValueHandler.ts index 824d57dd..749e85b3 100644 --- a/libs/horizon/src/dom/valueHandler/InputValueHandler.ts +++ b/libs/horizon/src/dom/valueHandler/InputValueHandler.ts @@ -15,6 +15,7 @@ import { updateCommonProp } from '../DOMPropertiesHandler/UpdateCommonProp'; import { Props } from '../utils/Interface'; +import { isNull } from '../utils/Common'; function getInitValue(dom: HTMLInputElement, props: Props) { const { value, defaultValue, checked, defaultChecked } = props; @@ -29,7 +30,7 @@ function getInitValue(dom: HTMLInputElement, props: Props) { export function getInputPropsWithoutValue(dom: HTMLInputElement, props: Props) { // checked属于必填属性,无法置 let { checked } = props; - if (checked == null) { + if (isNull(checked)) { checked = getInitValue(dom, props).initChecked; } @@ -45,12 +46,12 @@ export function getInputPropsWithoutValue(dom: HTMLInputElement, props: Props) { export function updateInputValue(dom: HTMLInputElement, props: Props) { const { value, checked } = props; - if (value != null) { + if (!isNull(value)) { // 处理 dom.value 逻辑 if (dom.value !== String(value)) { dom.value = String(value); } - } else if (checked != null) { + } else if (!isNull(checked)) { updateCommonProp(dom, 'checked', checked, true); } } @@ -60,7 +61,7 @@ export function setInitInputValue(dom: HTMLInputElement, props: Props) { const { value, defaultValue } = props; const { initValue, initChecked } = getInitValue(dom, props); - if (value != null || defaultValue != null) { + if (!isNull(value) || !isNull(defaultValue)) { // value 的使用优先级 value 属性 > defaultValue 属性 > 空字符串 const initValueStr = String(initValue); diff --git a/libs/horizon/src/dom/valueHandler/SelectValueHandler.ts b/libs/horizon/src/dom/valueHandler/SelectValueHandler.ts index fb9f859e..86297315 100644 --- a/libs/horizon/src/dom/valueHandler/SelectValueHandler.ts +++ b/libs/horizon/src/dom/valueHandler/SelectValueHandler.ts @@ -14,6 +14,7 @@ */ import { HorizonSelect, Props } from '../utils/Interface'; +import { isNull } from '../utils/Common'; function updateMultipleValue(options, newValues) { const newValueSet = new Set(); @@ -69,18 +70,18 @@ export function updateSelectValue(dom: HorizonSelect, props: Props, isInit = fal dom._multiple = newMultiple; // 设置了 value 属性 - if (value != null) { + if (!isNull(value)) { updateValue(dom.options, value, newMultiple); } else if (oldMultiple !== newMultiple) { // 修改了 multiple 属性 // 切换 multiple 之后,如果设置了 defaultValue 需要重新应用 - if (defaultValue != null) { + if (!isNull(defaultValue)) { updateValue(dom.options, defaultValue, newMultiple); } else { // 恢复到未选定状态 updateValue(dom.options, newMultiple ? [] : '', newMultiple); } - } else if (isInit && defaultValue != null) { + } else if (isInit && !isNull(defaultValue)) { // 设置了 defaultValue 属性 updateValue(dom.options, defaultValue, newMultiple); } diff --git a/libs/horizon/src/dom/valueHandler/TextareaValueHandler.ts b/libs/horizon/src/dom/valueHandler/TextareaValueHandler.ts index 82b2203e..e852cd1f 100644 --- a/libs/horizon/src/dom/valueHandler/TextareaValueHandler.ts +++ b/libs/horizon/src/dom/valueHandler/TextareaValueHandler.ts @@ -14,12 +14,13 @@ */ import { Props } from '../utils/Interface'; +import { isNull } from '../utils/Common'; // 值的优先级 value > children > defaultValue function getInitValue(props: Props) { const { value } = props; - if (value == null) { + if (isNull(value)) { const { defaultValue, children } = props; let initValue = defaultValue; @@ -30,7 +31,7 @@ function getInitValue(props: Props) { } // defaultValue 属性未配置,置为空字符串 - initValue = initValue != null ? initValue : ''; + initValue = initValue ?? ''; return initValue; } else { return value; diff --git a/libs/horizon/src/event/EventBinding.ts b/libs/horizon/src/event/EventBinding.ts index e4e0f59d..56e1567c 100644 --- a/libs/horizon/src/event/EventBinding.ts +++ b/libs/horizon/src/event/EventBinding.ts @@ -62,13 +62,6 @@ function isCaptureEvent(horizonEventName) { return horizonEventName.slice(-7) === 'Capture'; } -// 利用冒泡事件模拟不冒泡事件,需要直接在根节点绑定 -export function listenSimulatedDelegatedEvents(root: VNode) { - for (let i = 0; i < simulatedDelegatedEvents.length; i++) { - lazyDelegateOnRoot(root, simulatedDelegatedEvents[i]); - } -} - // 事件懒委托,当用户定义事件后,再进行委托到根节点 export function lazyDelegateOnRoot(currentRoot: VNode, eventName: string) { currentRoot.delegatedEvents.add(eventName); @@ -80,11 +73,8 @@ export function lazyDelegateOnRoot(currentRoot: VNode, eventName: string) { const nativeFullName = isCapture ? nativeEvent + 'capture' : nativeEvent; // 事件存储在DOM节点属性,避免多个VNode(root和portal)对应同一个DOM, 造成事件重复监听 - let events = currentRoot.realNode.$EV; - - if (!events) { - events = (currentRoot.realNode as any).$EV = {}; - } + currentRoot.realNode.$EV = currentRoot.realNode.$EV || {}; + const events = currentRoot.realNode.$EV; if (!events[nativeFullName]) { events[nativeFullName] = listenToNativeEvent(nativeEvent, currentRoot.realNode, isCapture); @@ -92,6 +82,13 @@ export function lazyDelegateOnRoot(currentRoot: VNode, eventName: string) { }); } +// 利用冒泡事件模拟不冒泡事件,需要直接在根节点绑定 +export function listenSimulatedDelegatedEvents(root: VNode) { + for (let i = 0; i < simulatedDelegatedEvents.length; i++) { + lazyDelegateOnRoot(root, simulatedDelegatedEvents[i]); + } +} + // 通过horizon事件名获取到native事件名 function getNativeEvtName(horizonEventName, capture) { let nativeName; diff --git a/libs/horizon/src/event/FormValueController.ts b/libs/horizon/src/event/FormValueController.ts index 3c63a299..2e296e6d 100644 --- a/libs/horizon/src/event/FormValueController.ts +++ b/libs/horizon/src/event/FormValueController.ts @@ -14,7 +14,7 @@ */ import { getVNodeProps } from '../dom/DOMInternalKeys'; -import { getDomTag } from '../dom/utils/Common'; +import { getDomTag, isNull } from '../dom/utils/Common'; import { Props } from '../dom/utils/Interface'; import { updateTextareaValue } from '../dom/valueHandler/TextareaValueHandler'; import { updateInputHandlerIfChanged } from '../dom/valueHandler/ValueChangeHandler'; @@ -41,14 +41,14 @@ function controlInputValue(inputDom: HTMLInputElement, props: Props) { const { name, type } = props; // 如果是 radio,找出同一form内,name相同的Radio,更新它们Handler的Value - if (type === 'radio' && name != null) { + if (type === 'radio' && !isNull(name)) { const radioList = document.querySelectorAll(`input[type="radio"][name="${name}"]`); for (let i = 0; i < radioList.length; i++) { const radio = radioList[i]; if (radio === inputDom) { continue; } - if (radio.form != null && inputDom.form != null && radio.form !== inputDom.form) { + if (!isNull(radio.form) && !isNull(inputDom.form) && radio.form !== inputDom.form) { continue; } diff --git a/libs/horizon/src/event/ListenerGetter.ts b/libs/horizon/src/event/ListenerGetter.ts index 3b19cf2a..02feee6f 100644 --- a/libs/horizon/src/event/ListenerGetter.ts +++ b/libs/horizon/src/event/ListenerGetter.ts @@ -87,34 +87,30 @@ export function getListenersFromTree( return listeners; } -// 获取enter和leave事件队列 -export function collectMouseListeners( - leaveEvent: null | WrappedEvent, - enterEvent: null | WrappedEvent, - from: VNode | null, - to: VNode | null, -): ListenerUnitList { - // 确定公共父节点,作为在树上遍历的终点 - const commonParent = from && to ? getCommonAncestor(from, to) : null; - let leaveEventList: ListenerUnitList = []; - if (from && leaveEvent) { - // 遍历树,获取绑定的leave事件 - leaveEventList = getMouseListenersFromTree( - leaveEvent, - from, - commonParent, - ); + +// 获取父节点 +function getParent(inst: VNode | null): VNode | null { + if (inst === null) { + return null; } - let enterEventList: ListenerUnitList = []; - if (to && enterEvent) { - // 先触发父节点enter事件,所以需要逆序 - enterEventList = getMouseListenersFromTree( - enterEvent, - to, - commonParent, - ).reverse(); + do { + inst = inst.parent; + } while (inst && inst.tag !== DomComponent); + return inst || null; +} + +// 寻找两个节点的共同最近祖先,如果没有则返回null +function getCommonAncestor(instA: VNode, instB: VNode): VNode | null { + const parentsSet = new Set(); + for (let tempA: VNode | null = instA; tempA; tempA = getParent(tempA)) { + parentsSet.add(tempA); } - return [...leaveEventList, ...enterEventList]; + for (let tempB: VNode | null = instB; tempB; tempB = getParent(tempB)) { + if (parentsSet.has(tempB)) { + return tempB; + } + } + return null; } function getMouseListenersFromTree( @@ -149,27 +145,32 @@ function getMouseListenersFromTree( return listeners; } -// 寻找两个节点的共同最近祖先,如果没有则返回null -function getCommonAncestor(instA: VNode, instB: VNode): VNode | null { - const parentsSet = new Set(); - for (let tempA: VNode | null = instA; tempA; tempA = getParent(tempA)) { - parentsSet.add(tempA); +// 获取enter和leave事件队列 +export function collectMouseListeners( + leaveEvent: null | WrappedEvent, + enterEvent: null | WrappedEvent, + from: VNode | null, + to: VNode | null, +): ListenerUnitList { + // 确定公共父节点,作为在树上遍历的终点 + const commonParent = from && to ? getCommonAncestor(from, to) : null; + let leaveEventList: ListenerUnitList = []; + if (from && leaveEvent) { + // 遍历树,获取绑定的leave事件 + leaveEventList = getMouseListenersFromTree( + leaveEvent, + from, + commonParent, + ); } - for (let tempB: VNode | null = instB; tempB; tempB = getParent(tempB)) { - if (parentsSet.has(tempB)) { - return tempB; - } + let enterEventList: ListenerUnitList = []; + if (to && enterEvent) { + // 先触发父节点enter事件,所以需要逆序 + enterEventList = getMouseListenersFromTree( + enterEvent, + to, + commonParent, + ).reverse(); } - return null; -} - -// 获取父节点 -function getParent(inst: VNode | null): VNode | null { - if (inst === null) { - return null; - } - do { - inst = inst.parent; - } while (inst && inst.tag !== DomComponent); - return inst || null; + return [...leaveEventList, ...enterEventList]; } diff --git a/libs/horizon/src/renderer/diff/nodeDiffComparator.ts b/libs/horizon/src/renderer/diff/nodeDiffComparator.ts index 42f30b53..83798c68 100644 --- a/libs/horizon/src/renderer/diff/nodeDiffComparator.ts +++ b/libs/horizon/src/renderer/diff/nodeDiffComparator.ts @@ -182,6 +182,10 @@ function getNewNode(parentNode: VNode, newChild: any, oldNode: VNode | null) { } break; } + break; + } + default: { + break; } } @@ -374,7 +378,7 @@ function diffArrayNodesHandler(parentNode: VNode, firstChild: VNode | null, newC // 4. 新节点还有一部分,但是老节点已经没有了 if (oldNode === null) { let isDirectAdd = false; - // TODO: 是否可以扩大至非dom类型节点 + // 是否可以扩大至非dom类型节点待确认 // 如果dom节点在上次添加前没有节点,说明本次添加时,可以直接添加到最后,不需要通过 getSiblingDom 函数找到 before 节点 if ( parentNode.tag === DomComponent && @@ -513,7 +517,7 @@ function diffIteratorNodesHandler( } // 新节点是字符串类型 -function diffStringNodeHandler(parentNode: VNode, newChild: any, firstChildVNode: VNode, isComparing: boolean) { +function diffStringNodeHandler(parentNode: VNode, newChild: any, firstChildVNode: VNode | null, isComparing: boolean) { let newTextNode: VNode | null = null; // 第一个vNode是Text,则复用 @@ -560,7 +564,7 @@ function diffObjectNodeHandler( } let resultNode: VNode | null = null; - let startDelVNode = firstChildVNode; + let startDelVNode: VNode | null = firstChildVNode; if (newChild.vtype === TYPE_COMMON_ELEMENT) { if (canReuseNode) { // 可以复用 From 6175b4e30889d9092a1c9c52fa3b741f4b3c1052 Mon Sep 17 00:00:00 2001 From: * <*> Date: Wed, 7 Jun 2023 10:24:02 +0800 Subject: [PATCH 2/3] Match-id-e0350c199ac3e8354d02617e9f692d807c56383a --- libs/horizon/src/renderer/TreeBuilder.ts | 7 +- libs/horizon/src/renderer/hooks/BaseHook.ts | 2 +- .../src/renderer/hooks/UseImperativeHook.ts | 3 +- .../src/renderer/render/ContextProvider.ts | 2 +- .../src/renderer/render/DomComponent.ts | 3 +- libs/horizon/src/renderer/render/DomText.ts | 3 +- .../src/renderer/render/SuspenseComponent.ts | 2 +- .../render/class/ClassLifeCycleProcessor.ts | 2 +- .../src/renderer/taskExecutor/RenderQueue.ts | 2 +- .../src/renderer/taskExecutor/TaskQueue.ts | 4 +- .../src/renderer/vnode/VNodeCreator.ts | 5 +- libs/horizon/src/renderer/vnode/VNodeFlags.ts | 76 +++++++++---------- 12 files changed, 58 insertions(+), 53 deletions(-) diff --git a/libs/horizon/src/renderer/TreeBuilder.ts b/libs/horizon/src/renderer/TreeBuilder.ts index 9798bb8a..cc99e968 100644 --- a/libs/horizon/src/renderer/TreeBuilder.ts +++ b/libs/horizon/src/renderer/TreeBuilder.ts @@ -54,6 +54,7 @@ import { import { getPathArr } from './utils/vNodePath'; import { injectUpdater } from '../external/devtools'; import { popCurrentRoot, pushCurrentRoot } from './RootStack'; +import { isNull } from '../dom/utils/Common'; // 不可恢复错误 let unrecoverableErrorDuringBuild: any = null; @@ -139,7 +140,7 @@ function bubbleVNode(vNode: VNode): void { node = parent; // 更新processing,抛出异常时可以使用 processing = node; - } while (node !== null); + } while (!isNull(node)); // 修改结果 if (getBuildResult() === BuildInComplete) { @@ -180,7 +181,7 @@ function getChildByIndex(vNode: VNode, idx: number) { let node = vNode.child; for (let i = 0; i < idx; i++) { // 场景:当组件被销毁,业务若异步(定时器)调用setState修改状态,可能出现路径错误,此处进行保护。 - if (node == null) { + if (isNull(node)) { return null; } @@ -225,7 +226,7 @@ export function calcStartUpdateVNode(treeRoot: VNode) { const pathIndex = Number(startNodePath[i]); node = getChildByIndex(node, pathIndex)!; // 路径错误时,回退到从根更新 - if (node == null) { + if (isNull(node)) { return treeRoot; } } diff --git a/libs/horizon/src/renderer/hooks/BaseHook.ts b/libs/horizon/src/renderer/hooks/BaseHook.ts index 3ffcaf62..0109bf62 100644 --- a/libs/horizon/src/renderer/hooks/BaseHook.ts +++ b/libs/horizon/src/renderer/hooks/BaseHook.ts @@ -52,7 +52,7 @@ export function createHook(state: any = null): Hook { return currentHook; } -export function getNextHook(hook: Hook, hooks: Array>) { +export function getNextHook(hook: Hook, hooks: Array>): Hook | null { return hooks[hook.hIndex + 1] || null; } diff --git a/libs/horizon/src/renderer/hooks/UseImperativeHook.ts b/libs/horizon/src/renderer/hooks/UseImperativeHook.ts index 3ae2feef..94d69743 100644 --- a/libs/horizon/src/renderer/hooks/UseImperativeHook.ts +++ b/libs/horizon/src/renderer/hooks/UseImperativeHook.ts @@ -22,7 +22,7 @@ function isNotNull(object: any): boolean { return object !== null && object !== undefined; } -function effectFunc(func: () => R, ref: Ref | ((any) => any) | null): (() => void) | void { +function effectFunc(func: () => R, ref: Ref | ((any) => any) | null): (() => void) | null { if (typeof ref === 'function') { const value = func(); ref(value); @@ -37,6 +37,7 @@ function effectFunc(func: () => R, ref: Ref | ((any) => any) | null): (() ref.current = null; }; } + return null; } export function useImperativeHandleImpl( diff --git a/libs/horizon/src/renderer/render/ContextProvider.ts b/libs/horizon/src/renderer/render/ContextProvider.ts index 1eadd6cc..6a8f7810 100644 --- a/libs/horizon/src/renderer/render/ContextProvider.ts +++ b/libs/horizon/src/renderer/render/ContextProvider.ts @@ -64,7 +64,7 @@ function handleContextChange(processing: VNode, context: ContextType): void node => { const depContexts = node.depContexts; if (depContexts && depContexts.length) { - isMatch = matchDependencies(depContexts, context, node) ?? isMatch; + isMatch = matchDependencies(depContexts, context, node) || isMatch; } }, node => diff --git a/libs/horizon/src/renderer/render/DomComponent.ts b/libs/horizon/src/renderer/render/DomComponent.ts index 2eaef98b..247986ad 100644 --- a/libs/horizon/src/renderer/render/DomComponent.ts +++ b/libs/horizon/src/renderer/render/DomComponent.ts @@ -23,6 +23,7 @@ import { markRef } from './BaseComponent'; import { DomComponent, DomPortal, DomText } from '../vnode/VNodeTags'; import { travelVNodeTree } from '../vnode/VNodeUtils'; import { createChildrenByDiff } from '../diff/nodeDiffComparator'; +import { isNull } from '../../dom/utils/Common'; function updateDom(processing: VNode, type: any, newProps: Props) { // 如果oldProps !== newProps,意味着存在更新,并且需要处理其相关的副作用 @@ -54,7 +55,7 @@ export function bubbleRender(processing: VNode) { const type = processing.type; const newProps = processing.props; - if (!processing.isCreated && processing.realNode != null) { + if (!processing.isCreated && !isNull(processing.realNode)) { // 更新dom属性 updateDom(processing, type, newProps); diff --git a/libs/horizon/src/renderer/render/DomText.ts b/libs/horizon/src/renderer/render/DomText.ts index 6f46901c..2f8cec01 100644 --- a/libs/horizon/src/renderer/render/DomText.ts +++ b/libs/horizon/src/renderer/render/DomText.ts @@ -18,6 +18,7 @@ import type { VNode } from '../Types'; import { throwIfTrue } from '../utils/throwIfTrue'; import { newTextDom } from '../../dom/DOMOperator'; import { FlagUtils } from '../vnode/VNodeFlags'; +import { isNull } from '../../dom/utils/Common'; export function captureRender(): VNode | null { return null; @@ -26,7 +27,7 @@ export function captureRender(): VNode | null { export function bubbleRender(processing: VNode) { const newText = processing.props; - if (!processing.isCreated && processing.realNode != null) { + if (!processing.isCreated && !isNull(processing.realNode)) { // 更新 const oldText = processing.oldProps; // 如果文本不同,将其标记为更新 diff --git a/libs/horizon/src/renderer/render/SuspenseComponent.ts b/libs/horizon/src/renderer/render/SuspenseComponent.ts index 82c04dee..4bc1a3b6 100644 --- a/libs/horizon/src/renderer/render/SuspenseComponent.ts +++ b/libs/horizon/src/renderer/render/SuspenseComponent.ts @@ -165,7 +165,7 @@ function canCapturePromise(vNode: VNode | null): boolean { // 处理Suspense子组件抛出的promise export function handleSuspenseChildThrowError(parent: VNode, processing: VNode, promise: PromiseType): boolean { - let vNode = parent; + let vNode: VNode | null = parent; // 向上找到最近的不在fallback状态的Suspense,并触发重新渲染 do { diff --git a/libs/horizon/src/renderer/render/class/ClassLifeCycleProcessor.ts b/libs/horizon/src/renderer/render/class/ClassLifeCycleProcessor.ts index f1fe2d1d..bf3c9c14 100644 --- a/libs/horizon/src/renderer/render/class/ClassLifeCycleProcessor.ts +++ b/libs/horizon/src/renderer/render/class/ClassLifeCycleProcessor.ts @@ -35,7 +35,7 @@ export function callDerivedStateFromProps( const newState = getDerivedStateFromProps(nextProps, oldState); // 组件未返回state,需要返回旧的preState - processing.state = newState === null || newState === undefined ? oldState : { ...oldState, ...newState }; + processing.state = newState ? { ...oldState, ...newState } : oldState; } } diff --git a/libs/horizon/src/renderer/taskExecutor/RenderQueue.ts b/libs/horizon/src/renderer/taskExecutor/RenderQueue.ts index 729dfbc8..bec90454 100644 --- a/libs/horizon/src/renderer/taskExecutor/RenderQueue.ts +++ b/libs/horizon/src/renderer/taskExecutor/RenderQueue.ts @@ -35,7 +35,7 @@ function callRenderQueue() { try { let callback; - while ((callback = renderQueue.shift())) { + while (callback = renderQueue.shift()) { callback(); } diff --git a/libs/horizon/src/renderer/taskExecutor/TaskQueue.ts b/libs/horizon/src/renderer/taskExecutor/TaskQueue.ts index 3c0bfcfe..84fa08bd 100644 --- a/libs/horizon/src/renderer/taskExecutor/TaskQueue.ts +++ b/libs/horizon/src/renderer/taskExecutor/TaskQueue.ts @@ -63,12 +63,12 @@ export function add(node: Node): void { export function first(): Node | null { const val: Node | null | undefined = taskQueue[0]; - return val !== undefined ? val : null; + return val ?? null; } export function shift(): Node | null { const val = taskQueue.shift(); - return val !== undefined ? val : null; + return val ?? null; } export function remove(node: Node) { diff --git a/libs/horizon/src/renderer/vnode/VNodeCreator.ts b/libs/horizon/src/renderer/vnode/VNodeCreator.ts index 73944854..27e08e99 100644 --- a/libs/horizon/src/renderer/vnode/VNodeCreator.ts +++ b/libs/horizon/src/renderer/vnode/VNodeCreator.ts @@ -44,6 +44,7 @@ import { import { VNode } from './VNode'; import { JSXElement, Source } from '../Types'; import { markVNodePath } from '../utils/vNodePath'; +import { isNull } from '../../dom/utils/Common'; const typeLazyMap = { [TYPE_FORWARD_REF]: ForwardRef, @@ -136,7 +137,7 @@ export function createUndeterminedVNode(type, key, props, source: Source | null) vNodeTag = typeMap[type.vtype]; isLazy = type.vtype === TYPE_LAZY; } else { - throw Error(`Component type is invalid, got: ${type == null ? type : componentType}`); + throw Error(`Component type is invalid, got: ${isNull(type) ? type : componentType}`); } const vNode = newVirtualNode(vNodeTag, key, props); @@ -183,7 +184,7 @@ export function createTreeRootVNode(container) { return vNode; } -// TODO: 暂时保留给测试用例使用,后续修改测试用例 +// 暂时保留给测试用例使用,后续修改测试用例 export function createVNode(tag: VNodeTag | string, ...secondArg) { let vNode = null; switch (tag) { diff --git a/libs/horizon/src/renderer/vnode/VNodeFlags.ts b/libs/horizon/src/renderer/vnode/VNodeFlags.ts index 31e40aa7..7ca52c55 100644 --- a/libs/horizon/src/renderer/vnode/VNodeFlags.ts +++ b/libs/horizon/src/renderer/vnode/VNodeFlags.ts @@ -36,71 +36,71 @@ export const ForceUpdate = /** */ 1 << 12; // For suspense export const Clear = /** */ 1 << 13; const LifecycleEffectArr = Update | Callback | Ref | Snapshot; -export class FlagUtils { - static removeFlag(node: VNode, flag: number) { +export const FlagUtils = { + removeFlag(node: VNode, flag: number) { node.flags &= ~flag; - } + }, - static removeLifecycleEffectFlags(node) { + removeLifecycleEffectFlags(node) { node.flags &= ~LifecycleEffectArr; - } + }, - static hasAnyFlag(node: VNode) { + hasAnyFlag(node: VNode) { // 有标志位 return node.flags !== InitFlag; - } + }, - static hasFlag(node: VNode, flag) { + hasFlag(node: VNode, flag) { return (node.flags & flag) !== 0; - } + }, - static setNoFlags(node: VNode) { + setNoFlags(node: VNode) { node.flags = InitFlag; - } + }, - static markAddition(node: VNode) { + markAddition(node: VNode) { node.flags |= Addition; - } + }, - static setAddition(node: VNode) { + setAddition(node: VNode) { node.flags = Addition; - } + }, - static markDirectAddition(node: VNode) { + markDirectAddition(node: VNode) { node.flags |= DirectAddition; - } - static markUpdate(node: VNode) { + }, + markUpdate(node: VNode) { node.flags |= Update; - } - static setDeletion(node: VNode) { + }, + setDeletion(node: VNode) { node.flags = Deletion; - } - static markContentReset(node: VNode) { + }, + markContentReset(node: VNode) { node.flags |= ResetText; - } - static markCallback(node: VNode) { + }, + markCallback(node: VNode) { node.flags |= Callback; - } - static markDidCapture(node: VNode) { + }, + markDidCapture(node: VNode) { node.flags |= DidCapture; - } - static markShouldCapture(node: VNode) { + }, + markShouldCapture(node: VNode) { node.flags |= ShouldCapture; - } - static markRef(node: VNode) { + }, + markRef(node: VNode) { node.flags |= Ref; - } - static markSnapshot(node: VNode) { + }, + markSnapshot(node: VNode) { node.flags |= Snapshot; - } - static markInterrupted(node: VNode) { + }, + markInterrupted(node: VNode) { node.flags |= Interrupted; - } - static markForceUpdate(node: VNode) { + }, + markForceUpdate(node: VNode) { node.flags |= ForceUpdate; - } + }, - static markClear(node: VNode) { + markClear(node: VNode) { node.flags |= Clear; } } From 04181677ea2a74f91799dd8a6090f32ec5c73298 Mon Sep 17 00:00:00 2001 From: * <*> Date: Wed, 7 Jun 2023 15:12:51 +0800 Subject: [PATCH 3/3] Match-id-376100c85662cffb23b28ed1f31fb28473ed51a5 --- libs/horizon/src/dom/SelectionRangeHandler.ts | 4 ++-- libs/horizon/src/dom/utils/Common.ts | 4 ++-- libs/horizon/src/dom/validators/ValidateProps.ts | 5 ++--- libs/horizon/src/dom/valueHandler/InputValueHandler.ts | 9 ++++----- libs/horizon/src/dom/valueHandler/SelectValueHandler.ts | 7 +++---- .../horizon/src/dom/valueHandler/TextareaValueHandler.ts | 3 +-- libs/horizon/src/event/EventBinding.ts | 2 +- libs/horizon/src/event/FormValueController.ts | 6 +++--- libs/horizon/src/renderer/TreeBuilder.ts | 7 +++---- libs/horizon/src/renderer/hooks/UseImperativeHook.ts | 5 +---- libs/horizon/src/renderer/render/DomComponent.ts | 3 +-- libs/horizon/src/renderer/render/DomText.ts | 2 +- libs/horizon/src/renderer/vnode/VNodeCreator.ts | 3 +-- 13 files changed, 25 insertions(+), 35 deletions(-) diff --git a/libs/horizon/src/dom/SelectionRangeHandler.ts b/libs/horizon/src/dom/SelectionRangeHandler.ts index a8f643c1..f9488f9b 100644 --- a/libs/horizon/src/dom/SelectionRangeHandler.ts +++ b/libs/horizon/src/dom/SelectionRangeHandler.ts @@ -17,7 +17,7 @@ * 处理文本框、输入框中框选范围内的数据 */ -import { getIFrameFocusedDom, isNull, isText } from './utils/Common'; +import { getIFrameFocusedDom, isText } from './utils/Common'; import { isElement } from './utils/Common'; @@ -30,7 +30,7 @@ function setSelectionRange(dom: HTMLInputElement | HTMLTextAreaElement, range) { const { start, end } = range; let realEnd = end; - if (isNull(realEnd)) { + if (realEnd === null || realEnd === undefined) { realEnd = start; } diff --git a/libs/horizon/src/dom/utils/Common.ts b/libs/horizon/src/dom/utils/Common.ts index 001a32f1..dde99489 100644 --- a/libs/horizon/src/dom/utils/Common.ts +++ b/libs/horizon/src/dom/utils/Common.ts @@ -85,6 +85,6 @@ export function shouldAutoFocus(tagName: string, props: Props): boolean { return types.includes(tagName) ? Boolean(props.autoFocus) : false; } -export function isNull(val) { - return val === null || val === undefined; +export function isNotNull(object: any): boolean { + return object !== null && object !== undefined; } diff --git a/libs/horizon/src/dom/validators/ValidateProps.ts b/libs/horizon/src/dom/validators/ValidateProps.ts index 9eabcc09..ed1f0132 100644 --- a/libs/horizon/src/dom/validators/ValidateProps.ts +++ b/libs/horizon/src/dom/validators/ValidateProps.ts @@ -14,7 +14,6 @@ */ import { getPropDetails, PROPERTY_TYPE, PropDetails } from './PropertiesData'; -import { isNull } from '../utils/Common'; const INVALID_EVENT_NAME_REGEX = /^on[^A-Z]/; @@ -74,7 +73,7 @@ export function isInvalidValue( propDetails: PropDetails | null, isNativeTag: boolean ): boolean { - if (isNull(value)) { + if (value === null || value === undefined) { return true; } @@ -105,7 +104,7 @@ export function validateProps(type, props) { } // style属性必须是对象 - if (!isNull(props.style) && typeof props.style !== 'object') { + if (props.style !== null && props.style !== undefined && typeof props.style !== 'object') { throw new Error('style should be a object.'); } diff --git a/libs/horizon/src/dom/valueHandler/InputValueHandler.ts b/libs/horizon/src/dom/valueHandler/InputValueHandler.ts index 749e85b3..16908947 100644 --- a/libs/horizon/src/dom/valueHandler/InputValueHandler.ts +++ b/libs/horizon/src/dom/valueHandler/InputValueHandler.ts @@ -15,7 +15,6 @@ import { updateCommonProp } from '../DOMPropertiesHandler/UpdateCommonProp'; import { Props } from '../utils/Interface'; -import { isNull } from '../utils/Common'; function getInitValue(dom: HTMLInputElement, props: Props) { const { value, defaultValue, checked, defaultChecked } = props; @@ -30,7 +29,7 @@ function getInitValue(dom: HTMLInputElement, props: Props) { export function getInputPropsWithoutValue(dom: HTMLInputElement, props: Props) { // checked属于必填属性,无法置 let { checked } = props; - if (isNull(checked)) { + if (checked === undefined) { checked = getInitValue(dom, props).initChecked; } @@ -46,12 +45,12 @@ export function getInputPropsWithoutValue(dom: HTMLInputElement, props: Props) { export function updateInputValue(dom: HTMLInputElement, props: Props) { const { value, checked } = props; - if (!isNull(value)) { + if (value !== undefined) { // 处理 dom.value 逻辑 if (dom.value !== String(value)) { dom.value = String(value); } - } else if (!isNull(checked)) { + } else if (checked !== undefined) { updateCommonProp(dom, 'checked', checked, true); } } @@ -61,7 +60,7 @@ export function setInitInputValue(dom: HTMLInputElement, props: Props) { const { value, defaultValue } = props; const { initValue, initChecked } = getInitValue(dom, props); - if (!isNull(value) || !isNull(defaultValue)) { + if (value !== undefined || defaultValue !== undefined) { // value 的使用优先级 value 属性 > defaultValue 属性 > 空字符串 const initValueStr = String(initValue); diff --git a/libs/horizon/src/dom/valueHandler/SelectValueHandler.ts b/libs/horizon/src/dom/valueHandler/SelectValueHandler.ts index 86297315..9ba9273f 100644 --- a/libs/horizon/src/dom/valueHandler/SelectValueHandler.ts +++ b/libs/horizon/src/dom/valueHandler/SelectValueHandler.ts @@ -14,7 +14,6 @@ */ import { HorizonSelect, Props } from '../utils/Interface'; -import { isNull } from '../utils/Common'; function updateMultipleValue(options, newValues) { const newValueSet = new Set(); @@ -70,18 +69,18 @@ export function updateSelectValue(dom: HorizonSelect, props: Props, isInit = fal dom._multiple = newMultiple; // 设置了 value 属性 - if (!isNull(value)) { + if (value !== null && value !== undefined) { updateValue(dom.options, value, newMultiple); } else if (oldMultiple !== newMultiple) { // 修改了 multiple 属性 // 切换 multiple 之后,如果设置了 defaultValue 需要重新应用 - if (!isNull(defaultValue)) { + if (defaultValue !== null && defaultValue !== undefined) { updateValue(dom.options, defaultValue, newMultiple); } else { // 恢复到未选定状态 updateValue(dom.options, newMultiple ? [] : '', newMultiple); } - } else if (isInit && !isNull(defaultValue)) { + } else if (isInit && defaultValue !== null && defaultValue !== undefined) { // 设置了 defaultValue 属性 updateValue(dom.options, defaultValue, newMultiple); } diff --git a/libs/horizon/src/dom/valueHandler/TextareaValueHandler.ts b/libs/horizon/src/dom/valueHandler/TextareaValueHandler.ts index e852cd1f..cf91b9c0 100644 --- a/libs/horizon/src/dom/valueHandler/TextareaValueHandler.ts +++ b/libs/horizon/src/dom/valueHandler/TextareaValueHandler.ts @@ -14,13 +14,12 @@ */ import { Props } from '../utils/Interface'; -import { isNull } from '../utils/Common'; // 值的优先级 value > children > defaultValue function getInitValue(props: Props) { const { value } = props; - if (isNull(value)) { + if (value === undefined) { const { defaultValue, children } = props; let initValue = defaultValue; diff --git a/libs/horizon/src/event/EventBinding.ts b/libs/horizon/src/event/EventBinding.ts index 56e1567c..baf965dd 100644 --- a/libs/horizon/src/event/EventBinding.ts +++ b/libs/horizon/src/event/EventBinding.ts @@ -73,7 +73,7 @@ export function lazyDelegateOnRoot(currentRoot: VNode, eventName: string) { const nativeFullName = isCapture ? nativeEvent + 'capture' : nativeEvent; // 事件存储在DOM节点属性,避免多个VNode(root和portal)对应同一个DOM, 造成事件重复监听 - currentRoot.realNode.$EV = currentRoot.realNode.$EV || {}; + currentRoot.realNode.$EV = currentRoot.realNode.$EV ?? {}; const events = currentRoot.realNode.$EV; if (!events[nativeFullName]) { diff --git a/libs/horizon/src/event/FormValueController.ts b/libs/horizon/src/event/FormValueController.ts index 2e296e6d..1b1649d0 100644 --- a/libs/horizon/src/event/FormValueController.ts +++ b/libs/horizon/src/event/FormValueController.ts @@ -14,7 +14,7 @@ */ import { getVNodeProps } from '../dom/DOMInternalKeys'; -import { getDomTag, isNull } from '../dom/utils/Common'; +import { getDomTag, isNotNull } from '../dom/utils/Common'; import { Props } from '../dom/utils/Interface'; import { updateTextareaValue } from '../dom/valueHandler/TextareaValueHandler'; import { updateInputHandlerIfChanged } from '../dom/valueHandler/ValueChangeHandler'; @@ -41,14 +41,14 @@ function controlInputValue(inputDom: HTMLInputElement, props: Props) { const { name, type } = props; // 如果是 radio,找出同一form内,name相同的Radio,更新它们Handler的Value - if (type === 'radio' && !isNull(name)) { + if (type === 'radio' && isNotNull(name)) { const radioList = document.querySelectorAll(`input[type="radio"][name="${name}"]`); for (let i = 0; i < radioList.length; i++) { const radio = radioList[i]; if (radio === inputDom) { continue; } - if (!isNull(radio.form) && !isNull(inputDom.form) && radio.form !== inputDom.form) { + if (isNotNull(radio.form) && isNotNull(inputDom.form) && radio.form !== inputDom.form) { continue; } diff --git a/libs/horizon/src/renderer/TreeBuilder.ts b/libs/horizon/src/renderer/TreeBuilder.ts index cc99e968..ad27609c 100644 --- a/libs/horizon/src/renderer/TreeBuilder.ts +++ b/libs/horizon/src/renderer/TreeBuilder.ts @@ -54,7 +54,6 @@ import { import { getPathArr } from './utils/vNodePath'; import { injectUpdater } from '../external/devtools'; import { popCurrentRoot, pushCurrentRoot } from './RootStack'; -import { isNull } from '../dom/utils/Common'; // 不可恢复错误 let unrecoverableErrorDuringBuild: any = null; @@ -140,7 +139,7 @@ function bubbleVNode(vNode: VNode): void { node = parent; // 更新processing,抛出异常时可以使用 processing = node; - } while (!isNull(node)); + } while (node); // 修改结果 if (getBuildResult() === BuildInComplete) { @@ -181,7 +180,7 @@ function getChildByIndex(vNode: VNode, idx: number) { let node = vNode.child; for (let i = 0; i < idx; i++) { // 场景:当组件被销毁,业务若异步(定时器)调用setState修改状态,可能出现路径错误,此处进行保护。 - if (isNull(node)) { + if (node === null || node === undefined) { return null; } @@ -226,7 +225,7 @@ export function calcStartUpdateVNode(treeRoot: VNode) { const pathIndex = Number(startNodePath[i]); node = getChildByIndex(node, pathIndex)!; // 路径错误时,回退到从根更新 - if (isNull(node)) { + if (node === null) { return treeRoot; } } diff --git a/libs/horizon/src/renderer/hooks/UseImperativeHook.ts b/libs/horizon/src/renderer/hooks/UseImperativeHook.ts index 94d69743..9a39a69b 100644 --- a/libs/horizon/src/renderer/hooks/UseImperativeHook.ts +++ b/libs/horizon/src/renderer/hooks/UseImperativeHook.ts @@ -17,10 +17,7 @@ import { useLayoutEffectImpl } from './UseEffectHook'; import { getHookStage } from './HookStage'; import { throwNotInFuncError } from './BaseHook'; import type { Ref } from './HookType'; - -function isNotNull(object: any): boolean { - return object !== null && object !== undefined; -} +import { isNotNull } from '../../dom/utils/Common'; function effectFunc(func: () => R, ref: Ref | ((any) => any) | null): (() => void) | null { if (typeof ref === 'function') { diff --git a/libs/horizon/src/renderer/render/DomComponent.ts b/libs/horizon/src/renderer/render/DomComponent.ts index 247986ad..77273d96 100644 --- a/libs/horizon/src/renderer/render/DomComponent.ts +++ b/libs/horizon/src/renderer/render/DomComponent.ts @@ -23,7 +23,6 @@ import { markRef } from './BaseComponent'; import { DomComponent, DomPortal, DomText } from '../vnode/VNodeTags'; import { travelVNodeTree } from '../vnode/VNodeUtils'; import { createChildrenByDiff } from '../diff/nodeDiffComparator'; -import { isNull } from '../../dom/utils/Common'; function updateDom(processing: VNode, type: any, newProps: Props) { // 如果oldProps !== newProps,意味着存在更新,并且需要处理其相关的副作用 @@ -55,7 +54,7 @@ export function bubbleRender(processing: VNode) { const type = processing.type; const newProps = processing.props; - if (!processing.isCreated && !isNull(processing.realNode)) { + if (!processing.isCreated && processing.realNode !== null) { // 更新dom属性 updateDom(processing, type, newProps); diff --git a/libs/horizon/src/renderer/render/DomText.ts b/libs/horizon/src/renderer/render/DomText.ts index 2f8cec01..663f15f6 100644 --- a/libs/horizon/src/renderer/render/DomText.ts +++ b/libs/horizon/src/renderer/render/DomText.ts @@ -27,7 +27,7 @@ export function captureRender(): VNode | null { export function bubbleRender(processing: VNode) { const newText = processing.props; - if (!processing.isCreated && !isNull(processing.realNode)) { + if (!processing.isCreated && processing.realNode !== null) { // 更新 const oldText = processing.oldProps; // 如果文本不同,将其标记为更新 diff --git a/libs/horizon/src/renderer/vnode/VNodeCreator.ts b/libs/horizon/src/renderer/vnode/VNodeCreator.ts index 27e08e99..65b777c8 100644 --- a/libs/horizon/src/renderer/vnode/VNodeCreator.ts +++ b/libs/horizon/src/renderer/vnode/VNodeCreator.ts @@ -44,7 +44,6 @@ import { import { VNode } from './VNode'; import { JSXElement, Source } from '../Types'; import { markVNodePath } from '../utils/vNodePath'; -import { isNull } from '../../dom/utils/Common'; const typeLazyMap = { [TYPE_FORWARD_REF]: ForwardRef, @@ -137,7 +136,7 @@ export function createUndeterminedVNode(type, key, props, source: Source | null) vNodeTag = typeMap[type.vtype]; isLazy = type.vtype === TYPE_LAZY; } else { - throw Error(`Component type is invalid, got: ${isNull(type) ? type : componentType}`); + throw Error(`Component type is invalid, got: ${type === null || type === undefined ? type : componentType}`); } const vNode = newVirtualNode(vNodeTag, key, props);