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) { // 可以复用