diff --git a/libs/horizon/src/dom/DOMExternal.ts b/libs/horizon/src/dom/DOMExternal.ts index 237e8de2..e9404cb7 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,7 +42,31 @@ function createRoot(children: any, container: Container, callback?: Callback) { return treeRoot; } -function findDOMNode(domOrEle: Element): null | Element | Text { +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) { return null; } diff --git a/libs/horizon/src/dom/DOMInternalKeys.ts b/libs/horizon/src/dom/DOMInternalKeys.ts index dec6fed1..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 对象的特定属性上 @@ -72,7 +73,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 +94,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] = eventsMap; } return eventsMap; } diff --git a/libs/horizon/src/dom/DOMOperator.ts b/libs/horizon/src/dom/DOMOperator.ts index b475375a..cc023da9 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; @@ -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,18 +54,12 @@ 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; +export function getNSCtx(parentNS: string, type: string, dom?: Container): string { + return dom ? getChildNS(dom.namespaceURI ?? null, dom.nodeName) : getChildNS(parentNS, type); } export function prepareForSubmit(): void { - selectionInfo = getSelectionInfo(); + selectionInfo = getSelectionInfo(); } export function resetAfterSubmit(): 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..5ec79c33 100644 --- a/libs/horizon/src/dom/DOMPropertiesHandler/DOMPropertiesHandler.ts +++ b/libs/horizon/src/dom/DOMPropertiesHandler/DOMPropertiesHandler.ts @@ -8,37 +8,7 @@ import { } from '../../event/EventBinding'; import { isEventProp, isNativeElement } from '../validators/ValidateProps'; -// 初始化DOM属性 -export function setDomProps( - tagName: string, - dom: Element, - props: Object, -): void { - const isNativeTag = isNativeElement(tagName, props); - const keysOfProps = Object.keys(props); - - for (let i = 0; i < keysOfProps.length; i++) { - const propName = keysOfProps[i]; - const propVal = props[propName]; - - updateOneProp(dom, propName, propVal, isNativeTag, true); - } -} - -// 更新 DOM 属性 -export function updateDomProps( - dom: Element, - changeList: Array, - isNativeTag: boolean, -): void { - for (let i = 0; i < changeList.length; i++) { - const { propName, propVal } = changeList[i]; - - updateOneProp(dom, propName, propVal, isNativeTag); - } -} - -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') { @@ -59,6 +29,36 @@ function updateOneProp(dom, propName, propVal, isNativeTag, isInit?: boolean) { } } +// 初始化DOM属性 +export function setDomProps( + tagName: string, + dom: Element, + props: Object, +): void { + const isNativeTag = isNativeElement(tagName, props); + const keysOfProps = Object.keys(props); + + for (let i = 0; i < keysOfProps.length; i++) { + const propName = keysOfProps[i]; + const propVal = props[propName]; + + updateOneProp(dom, propName, isNativeTag, propVal, true); + } +} + +// 更新 DOM 属性 +export function updateDomProps( + dom: Element, + changeList: Array, + isNativeTag: boolean, +): void { + for (let i = 0; i < changeList.length; i++) { + const { propName, propVal } = changeList[i]; + + updateOneProp(dom, propName, isNativeTag, propVal); + } +} + // 找出两个 DOM 属性的差别,生成需要更新的属性集合 export function compareProps( oldProps: Object, @@ -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..49429fbe 100644 --- a/libs/horizon/src/dom/DOMPropertiesHandler/StyleHandler.ts +++ b/libs/horizon/src/dom/DOMPropertiesHandler/StyleHandler.ts @@ -1,3 +1,31 @@ +function isNeedUnitCSS(propName: string) { + return !(noUnitCSS.includes(propName) + || propName.startsWith('borderImage') + || propName.startsWith('flex') + || propName.startsWith('gridRow') + || propName.startsWith('gridColumn') + || propName.startsWith('stroke') + || 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; +} + /** * 设置 DOM 节点的 style 属性 */ @@ -21,31 +49,3 @@ export function setStyles(dom, styles) { */ const noUnitCSS = ['animationIterationCount', 'columnCount', 'columns', 'gridArea', 'fontWeight', 'lineClamp', 'lineHeight', 'opacity', 'order', 'orphans', 'tabSize', 'widows', 'zIndex', 'zoom']; - -function isNeedUnitCSS(propName: string) { - return !(noUnitCSS.includes(propName) - || propName.startsWith('borderImage') - || propName.startsWith('flex') - || propName.startsWith('gridRow') - || propName.startsWith('gridColumn') - || propName.startsWith('stroke') - || 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 e9a6fce1..5af59913 100644 --- a/libs/horizon/src/dom/DOMPropertiesHandler/UpdateCommonProp.ts +++ b/libs/horizon/src/dom/DOMPropertiesHandler/UpdateCommonProp.ts @@ -14,9 +14,15 @@ 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']); + +// 驼峰 变 “-” +function convertToLowerCase(str) { + const replacer = (match, char) => `-${char.toLowerCase()}`; + return str.replace(/([A-Z])/g, replacer); +} /** * 给 dom 设置属性 @@ -70,12 +76,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 f5bde5bf..284faa41 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: { + selectionRange?: { start: number; end: number; - } -}; + }; +} // 防止选择范围内的信息因为节点删除或其他原因导致的信息丢失 -export function resetSelectionRange(preSelectionRangeData: selectionData) { +export function resetSelectionRange(preSelectionRangeData: SelectionData) { // 当前 focus 的元素 const currentFocusedDom = getIFrameFocusedDom(); diff --git a/libs/horizon/src/dom/utils/Interface.ts b/libs/horizon/src/dom/utils/Interface.ts index 62887f72..2306d4b3 100644 --- a/libs/horizon/src/dom/utils/Interface.ts +++ b/libs/horizon/src/dom/utils/Interface.ts @@ -1,9 +1,9 @@ export interface IProperty { - [propName: string]: any + [propName: string]: any; } export interface HorizonSelect extends HTMLSelectElement { - _multiple: boolean; + _multiple?: boolean; } export type HorizonDom = Element | HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement; diff --git a/libs/horizon/src/dom/validators/PropertiesData.ts b/libs/horizon/src/dom/validators/PropertiesData.ts index c11e5fdf..70645aae 100644 --- a/libs/horizon/src/dom/validators/PropertiesData.ts +++ b/libs/horizon/src/dom/validators/PropertiesData.ts @@ -6,19 +6,15 @@ export enum PROPERTY_TYPE { STRING, // 普通的字符串类型 SPECIAL, // 需要特殊处理的属性类型 BOOLEAN_STR, // 字符串类型的 true false -}; +} 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 { - 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'; -// 更新