diff --git a/libs/horizon/src/dom/DOMInternalKeys.ts b/libs/horizon/src/dom/DOMInternalKeys.ts index 6ddbdb5b..2f4ccb23 100644 --- a/libs/horizon/src/dom/DOMInternalKeys.ts +++ b/libs/horizon/src/dom/DOMInternalKeys.ts @@ -14,13 +14,9 @@ import { TreeRoot, } from '../renderer/vnode/VNodeTags'; -const prefix = '_horizon'; - -const internalKeys = { - VNode: `${prefix}VNode`, - props: `${prefix}Props`, - nonDelegatedEvents: `${prefix}NonDelegatedEvents`, -}; +const INTERNAL_VNODE = '_horizon_VNode'; +const INTERNAL_PROPS = '_horizon_Props'; +const INTERNAL_NONDELEGATEEVENTS = '_horizon_NonDelegatedEvents'; // 通过 VNode 实例获取 DOM 节点 export function getDom(vNode: VNode): Element | Text | null { @@ -36,12 +32,12 @@ export function saveVNode( vNode: VNode, dom: Element | Text | Container, ): void { - dom[internalKeys.VNode] = vNode; + dom[INTERNAL_VNODE] = vNode; } // 用 DOM 节点,来找其对应的 VNode 实例 export function getVNode(dom: Node|Container): VNode | null { - const vNode = dom[internalKeys.VNode] || (dom as Container)._treeRoot; + const vNode = dom[INTERNAL_VNODE] || (dom as Container)._treeRoot; if (vNode) { const {tag} = vNode; if (tag === DomComponent || tag === DomText || tag === TreeRoot) { @@ -53,7 +49,7 @@ export function getVNode(dom: Node|Container): VNode | null { // 用 DOM 对象,来寻找其对应或者说是最近父级的 vNode export function getNearestVNode(dom: Node): null | VNode { - let vNode = dom[internalKeys.VNode]; + let vNode = dom[INTERNAL_VNODE]; if (vNode) { // 如果是已经被框架标记过的 DOM 节点,那么直接返回其 VNode 实例 return vNode; } @@ -62,7 +58,7 @@ export function getNearestVNode(dom: Node): null | VNode { let parentDom = dom.parentNode; let nearVNode = null; while (parentDom) { - vNode = parentDom[internalKeys.VNode]; + vNode = parentDom[INTERNAL_VNODE]; if (vNode) { nearVNode = vNode; break; @@ -74,19 +70,19 @@ export function getNearestVNode(dom: Node): null | VNode { // 获取 vNode 上的属性相关信息 export function getVNodeProps(dom: Element | Text): Props | null{ - return dom[internalKeys.props] || null; + return dom[INTERNAL_PROPS] || null; } // 将 DOM 属性相关信息挂到 DOM 对象的特定属性上 export function updateVNodeProps(dom: Element | Text, props: Props): void { - dom[internalKeys.props] = props; + dom[INTERNAL_PROPS] = props; } export function getNonDelegatedListenerMap(dom: Element | Text): Map { - let eventsMap = dom[internalKeys.nonDelegatedEvents]; + let eventsMap = dom[INTERNAL_NONDELEGATEEVENTS]; if (!eventsMap) { eventsMap = new Map(); - dom[internalKeys.nonDelegatedEvents] = eventsMap; + dom[INTERNAL_NONDELEGATEEVENTS] = eventsMap; } return eventsMap; } diff --git a/libs/horizon/src/dom/DOMOperator.ts b/libs/horizon/src/dom/DOMOperator.ts index 969219f0..169e85b2 100644 --- a/libs/horizon/src/dom/DOMOperator.ts +++ b/libs/horizon/src/dom/DOMOperator.ts @@ -6,7 +6,7 @@ import { createDom, } from './utils/DomCreator'; import { getSelectionInfo, resetSelectionRange, SelectionData } from './SelectionRangeHandler'; -import { isElement, isComment, isDocument, isDocumentFragment, getDomTag, shouldAutoFocus } from './utils/Common'; +import { shouldAutoFocus } from './utils/Common'; import { NSS } from './utils/DomCreator'; import { adjustStyleValue } from './DOMPropertiesHandler/StyleHandler'; @@ -165,7 +165,7 @@ export function submitDomUpdate(tag: string, vNode: VNode) { // 应用diff更新Properties. // 当一个选中的radio改变名称,浏览器使另一个radio的复选框为false. if (type === 'input' && newProps.type === 'radio' && newProps.name != null && newProps.checked != null) { - updateCommonProp(element, 'checked', newProps.checked); + updateCommonProp(element, 'checked', newProps.checked, true); } const isNativeTag = isNativeElement(type, newProps); updateDomProps(element, changeList, isNativeTag); diff --git a/libs/horizon/src/dom/DOMPropertiesHandler/DOMPropertiesHandler.ts b/libs/horizon/src/dom/DOMPropertiesHandler/DOMPropertiesHandler.ts index 482a15a9..d0755511 100644 --- a/libs/horizon/src/dom/DOMPropertiesHandler/DOMPropertiesHandler.ts +++ b/libs/horizon/src/dom/DOMPropertiesHandler/DOMPropertiesHandler.ts @@ -11,20 +11,18 @@ import { isEventProp, isNativeElement } from '../validators/ValidateProps'; function updateOneProp(dom, propName, isNativeTag, propVal?, isInit?: boolean) { if (propName === 'style') { setStyles(dom, propVal); - } else if (propName === 'dangerouslySetInnerHTML') { - dom.innerHTML = propVal.__html; - } else if (propName === 'children') { // 只处理纯文本子节点,其他children在VNode树中处理 - const type = typeof propVal; - if (type === 'string') { - dom.textContent = propVal; - } else if (type === 'number') { - dom.textContent = propVal + ''; // 这种数字转字符串的方式效率最高 - } } else if (isEventProp(propName)) { // 事件监听属性处理 if (!allDelegatedHorizonEvents.has(propName)) { listenNonDelegatedEvent(propName, dom, propVal); } + } else if (propName === 'children') { // 只处理纯文本子节点,其他children在VNode树中处理 + const type = typeof propVal; + if (type === 'string' || type === 'number') { + dom.textContent = propVal; + } + } else if (propName === 'dangerouslySetInnerHTML') { + dom.innerHTML = propVal.__html; } else { if (!isInit || (isInit && propVal != null)) { updateCommonProp(dom, propName, propVal, isNativeTag); @@ -40,10 +38,12 @@ export function setDomProps( ): 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]; + let propName; + let propVal; + const keyLength = keysOfProps.length; + for (let i = 0; i < keyLength; i++) { + propName = keysOfProps[i]; + propVal = props[propName]; updateOneProp(dom, propName, isNativeTag, propVal, true); } @@ -55,9 +55,12 @@ export function updateDomProps( changeList: Array, isNativeTag: boolean, ): void { - for (let i = 0; i < changeList.length; i++) { - const { propName, propVal } = changeList[i]; - + const listLength = changeList.length; + let propName; + let propVal; + for (let i = 0; i < listLength; i++) { + propName = changeList[i].propName; + propVal = changeList[i].propVal; updateOneProp(dom, propName, isNativeTag, propVal); } } @@ -73,19 +76,24 @@ export function compareProps( const keysOfOldProps = Object.keys(oldProps); const keysOfNewProps = Object.keys(newProps); + const oldPropsLength = keysOfOldProps.length; + let propName; + let oldStyle; + let styleProps; + let styleProp; // 找到旧属性中需要删除的属性 - for (let i = 0; i < keysOfOldProps.length; i++) { - const propName = keysOfOldProps[i]; + for (let i = 0; i < oldPropsLength; i++) { + propName = keysOfOldProps[i]; // 新属性中包含该属性或者该属性为空值的属性不需要处理 if (keysOfNewProps.includes(propName) || oldProps[propName] == null) { continue; } if (propName === 'style') { - const oldStyle = oldProps[propName]; - const styleProps = Object.keys(oldStyle); + oldStyle = oldProps[propName]; + styleProps = Object.keys(oldStyle); for (let j = 0; j < styleProps.length; j++) { - const styleProp = styleProps[j]; + styleProp = styleProps[j]; updatesForStyle[styleProp] = ''; } } else if ( @@ -110,11 +118,17 @@ export function compareProps( } } + let newPropValue; + let oldPropValue; + let oldStyleProps; + let newStyleProps; + let newHTML; + let oldHTML; // 遍历新属性,获取新增和变更属性 for (let i = 0; i < keysOfNewProps.length; i++) { - const propName = keysOfNewProps[i]; - const newPropValue = newProps[propName]; - const oldPropValue = oldProps != null ? oldProps[propName] : null; + propName = keysOfNewProps[i]; + newPropValue = newProps[propName]; + oldPropValue = oldProps != null ? oldProps[propName] : null; if (newPropValue === oldPropValue || (newPropValue == null && oldPropValue == null)) { // 新旧属性值未发生变化,或者新旧属性皆为空值,不需要进行处理 @@ -124,18 +138,18 @@ export function compareProps( if (propName === 'style') { if (oldPropValue) { // 之前 style 属性有设置非空值 // 原来有这个 style,但现在没这个 style 了 - const oldStyleProps = Object.keys(oldPropValue); + oldStyleProps = Object.keys(oldPropValue); for (let j = 0; j < oldStyleProps.length; j++) { - const styleProp = oldStyleProps[j]; + styleProp = oldStyleProps[j]; if (!newPropValue || !Object.prototype.hasOwnProperty.call(newPropValue, styleProp)) { updatesForStyle[styleProp] = ''; } } // 现在有这个 style,但是和原来不相等 - const newStyleProps = newPropValue ? Object.keys(newPropValue) : []; + newStyleProps = newPropValue ? Object.keys(newPropValue) : []; for (let j = 0; j < newStyleProps.length; j++) { - const styleProp = newStyleProps[j]; + styleProp = newStyleProps[j]; if (oldPropValue[styleProp] !== newPropValue[styleProp]) { updatesForStyle[styleProp] = newPropValue[styleProp]; } @@ -150,8 +164,8 @@ export function compareProps( updatesForStyle = newPropValue; } } else if (propName === 'dangerouslySetInnerHTML') { - const newHTML = newPropValue ? newPropValue.__html : undefined; - const oldHTML = oldPropValue ? oldPropValue.__html : undefined; + newHTML = newPropValue ? newPropValue.__html : undefined; + oldHTML = oldPropValue ? oldPropValue.__html : undefined; if (newHTML != null) { if (oldHTML !== newHTML) { toBeUpdatedProps.push({ diff --git a/libs/horizon/src/dom/DOMPropertiesHandler/UpdateCommonProp.ts b/libs/horizon/src/dom/DOMPropertiesHandler/UpdateCommonProp.ts index 5af59913..468408ac 100644 --- a/libs/horizon/src/dom/DOMPropertiesHandler/UpdateCommonProp.ts +++ b/libs/horizon/src/dom/DOMPropertiesHandler/UpdateCommonProp.ts @@ -29,7 +29,7 @@ function convertToLowerCase(str) { * attrName 指代码中属性设置的属性名称(如 class) * 多数情况 attrName 仅用作初始 DOM 节点对象使用,而 property 更多用于页面交互 */ -export function updateCommonProp(dom: Element, attrName: string, value: any, isNativeTag: boolean = true) { +export function updateCommonProp(dom: Element, attrName: string, value: any, isNativeTag: boolean) { const propDetails = getPropDetails(attrName); if (isInvalidValue(attrName, value, propDetails, isNativeTag)) { diff --git a/libs/horizon/src/dom/utils/DomCreator.ts b/libs/horizon/src/dom/utils/DomCreator.ts index 9d89ef5f..360aea81 100644 --- a/libs/horizon/src/dom/utils/DomCreator.ts +++ b/libs/horizon/src/dom/utils/DomCreator.ts @@ -5,13 +5,6 @@ export const NSS = { svg: 'http://www.w3.org/2000/svg', }; -const div = document.createElement('div'); -const span = document.createElement('span'); -const tr = document.createElement('tr'); -const td = document.createElement('td'); -const a = document.createElement('a'); -const p = document.createElement('p'); - // 创建DOM元素 export function createDom( tagName: string, @@ -23,18 +16,6 @@ export function createDom( if (ns !== NSS.html) { dom = document.createElementNS(ns, tagName); - } else if (tagName === 'div') { - dom = div.cloneNode(false); - } else if (tagName === 'span') { - dom = span.cloneNode(false); - } else if (tagName === 'tr') { - dom = tr.cloneNode(false); - } else if (tagName === 'td') { - dom = td.cloneNode(false); - } else if (tagName === 'a') { - dom = a.cloneNode(false); - } else if (tagName === 'p') { - dom = p.cloneNode(false); } else { dom = document.createElement(tagName); } diff --git a/libs/horizon/src/dom/valueHandler/InputValueHandler.ts b/libs/horizon/src/dom/valueHandler/InputValueHandler.ts index e081a5ea..368bf288 100644 --- a/libs/horizon/src/dom/valueHandler/InputValueHandler.ts +++ b/libs/horizon/src/dom/valueHandler/InputValueHandler.ts @@ -37,7 +37,7 @@ export function updateInputValue(dom: HTMLInputElement, properties: IProperty) { dom.value = String(value); } } else if (checked != null) { - updateCommonProp(dom, 'checked', checked); + updateCommonProp(dom, 'checked', checked, true); } } diff --git a/libs/horizon/src/renderer/ContextSaver.ts b/libs/horizon/src/renderer/ContextSaver.ts index d092dd76..62586895 100644 --- a/libs/horizon/src/renderer/ContextSaver.ts +++ b/libs/horizon/src/renderer/ContextSaver.ts @@ -29,17 +29,32 @@ let ctxOldContext: Object = {}; let ctxOldChange: Boolean = false; let ctxOldPreviousContext: Object = {}; +function setContext(vNode: VNode, contextName, value) { + if (vNode.contexts === null) { + vNode.contexts = { + [contextName]: value, + }; + } else { + vNode.contexts[contextName] = value; + } +} +function getContext(vNode: VNode, contextName) { + if (vNode.contexts !== null) { + return vNode.contexts[contextName]; + } +} + // capture阶段设置 function setNamespaceCtx(vNode: VNode, dom?: Container) { const nextContext = getNSCtx(ctxNamespace, vNode.type, dom); - vNode.setContext(CTX_NAMESPACE, ctxNamespace); + setContext(vNode, CTX_NAMESPACE, ctxNamespace); ctxNamespace = nextContext; } // bubble阶段恢复 function resetNamespaceCtx(vNode: VNode) { - ctxNamespace = vNode.getContext(CTX_NAMESPACE); + ctxNamespace = getContext(vNode, CTX_NAMESPACE); } function getNamespaceCtx(): string { @@ -49,14 +64,14 @@ function getNamespaceCtx(): string { function setContextCtx(providerVNode: VNode, nextValue: T) { const context: ContextType = providerVNode.type._context; - providerVNode.setContext(CTX_CONTEXT, context.value); + setContext(providerVNode, CTX_CONTEXT, context.value); context.value = nextValue; } function resetContextCtx(providerVNode: VNode) { const context: ContextType = providerVNode.type._context; - context.value = providerVNode.getContext(CTX_CONTEXT); + context.value = getContext(providerVNode, CTX_CONTEXT); } // 在局部更新时,恢复父节点的context @@ -74,11 +89,11 @@ function recoverParentsContextCtx(vNode: VNode) { // ctxOldContext是 旧context提供者的context function setVNodeOldContext(providerVNode: VNode, context: Object) { - providerVNode.setContext(CTX_OLD_CONTEXT, context); + setContext(providerVNode, CTX_OLD_CONTEXT, context); } function getVNodeOldContext(vNode: VNode) { - return vNode.getContext(CTX_OLD_CONTEXT); + return getContext(vNode, CTX_OLD_CONTEXT); } function setOldContextCtx(providerVNode: VNode, context: Object) { @@ -95,11 +110,11 @@ function resetOldContextCtx(vNode: VNode) { } function setVNodeOldPreviousContext(providerVNode: VNode, context: Object) { - providerVNode.setContext(CTX_OLD_PREVIOUS_CONTEXT, context); + setContext(providerVNode, CTX_OLD_PREVIOUS_CONTEXT, context); } function getVNodeOldPreviousContext(vNode: VNode) { - return vNode.getContext(CTX_OLD_PREVIOUS_CONTEXT); + return getContext(vNode, CTX_OLD_PREVIOUS_CONTEXT); } function setOldPreviousContextCtx(context: Object) { @@ -111,7 +126,7 @@ function getOldPreviousContextCtx() { } function setContextChangeCtx(providerVNode: VNode, didChange: boolean) { - providerVNode.setContext(CTX_OLD_CHANGE, didChange); + setContext(providerVNode, CTX_OLD_CHANGE, didChange); ctxOldChange = didChange; } @@ -120,7 +135,7 @@ function getContextChangeCtx() { } function resetContextChangeCtx(vNode: VNode) { - ctxOldChange = vNode.getContext(CTX_OLD_CHANGE); + ctxOldChange = getContext(vNode, CTX_OLD_CHANGE); } export { diff --git a/libs/horizon/src/renderer/ErrorHandler.ts b/libs/horizon/src/renderer/ErrorHandler.ts index 991ef861..837f25c5 100644 --- a/libs/horizon/src/renderer/ErrorHandler.ts +++ b/libs/horizon/src/renderer/ErrorHandler.ts @@ -6,7 +6,7 @@ import type {VNode} from './Types'; import type {Update} from './UpdateHandler'; import {ClassComponent, TreeRoot} from './vnode/VNodeTags'; -import {FlagUtils, Interrupted} from './vnode/VNodeFlags'; +import {FlagUtils, Interrupted, DidCapture, InitFlag} from './vnode/VNodeFlags'; import {newUpdate, UpdateState, pushUpdate} from './UpdateHandler'; import {launchUpdateFromVNode, tryRenderFromRoot} from './TreeBuilder'; import {setRootThrowError} from './submit/Submit'; @@ -99,7 +99,7 @@ export function handleRenderThrowError( const ctor = vNode.type; const instance = vNode.realNode; if ( - !vNode.flags.DidCapture && + (vNode.flags & DidCapture) === InitFlag && ( typeof ctor.getDerivedStateFromError === 'function' || (instance !== null && typeof instance.componentDidCatch === 'function') diff --git a/libs/horizon/src/renderer/TreeBuilder.ts b/libs/horizon/src/renderer/TreeBuilder.ts index 020b58b2..0f13627d 100644 --- a/libs/horizon/src/renderer/TreeBuilder.ts +++ b/libs/horizon/src/renderer/TreeBuilder.ts @@ -3,7 +3,7 @@ import type { VNode } from './Types'; import { callRenderQueueImmediate, pushRenderCallback } from './taskExecutor/RenderQueue'; import { updateVNode } from './vnode/VNodeCreator'; import { TreeRoot } from './vnode/VNodeTags'; -import { FlagUtils } from './vnode/VNodeFlags'; +import { FlagUtils, InitFlag, Interrupted } from './vnode/VNodeFlags'; import { captureVNode } from './render/BaseComponent'; import { checkLoopingUpdateLimit, submitToRender } from './submit/Submit'; import { runAsyncEffects } from './submit/HookEffectHandler'; @@ -18,7 +18,7 @@ import { setProcessingClassVNode, setStartVNode } from './GlobalVar'; -import { findDomParent, getSiblingVNode } from './vnode/VNodeUtils'; +import { findDomParent } from './vnode/VNodeUtils'; import { ByAsync, BySync, @@ -86,13 +86,13 @@ function bubbleVNode(vNode: VNode): void { do { const parent = node.parent; - if (!node.flags.Interrupted) { // vNode没有抛出异常 + if ((node.flags & Interrupted) === InitFlag) { // vNode没有抛出异常 componentRenders[node.tag].bubbleRender(node); // 设置node的childShouldUpdate属性 updateChildShouldUpdate(node); - if (parent !== null && node !== getStartVNode() && !parent.flags.Interrupted) { + if (parent !== null && node !== getStartVNode() && (parent.flags & Interrupted) === InitFlag) { collectDirtyNodes(node, parent); } } @@ -108,7 +108,7 @@ function bubbleVNode(vNode: VNode): void { break; } - const siblingVNode = getSiblingVNode(node); + const siblingVNode = node.next; if (siblingVNode !== null) { // 有兄弟vNode processing = siblingVNode; return; diff --git a/libs/horizon/src/renderer/diff/DiffTools.ts b/libs/horizon/src/renderer/diff/DiffTools.ts index 9b152d92..42fd2864 100644 --- a/libs/horizon/src/renderer/diff/DiffTools.ts +++ b/libs/horizon/src/renderer/diff/DiffTools.ts @@ -9,10 +9,6 @@ export function isTextType(newChild: any) { return typeof newChild === 'string' || typeof newChild === 'number'; } -export function isArrayType(newChild: any) { - return Array.isArray(newChild); -} - export function isIteratorType(newChild: any) { return (typeof Symbol === 'function' && newChild[Symbol.iterator]) || newChild['@@iterator']; } diff --git a/libs/horizon/src/renderer/diff/nodeDiffComparator.ts b/libs/horizon/src/renderer/diff/nodeDiffComparator.ts index 0d65c816..530e9f87 100644 --- a/libs/horizon/src/renderer/diff/nodeDiffComparator.ts +++ b/libs/horizon/src/renderer/diff/nodeDiffComparator.ts @@ -7,11 +7,10 @@ import { isSameType, getIteratorFn, isTextType, - isArrayType, isIteratorType, isObjectType, } from './DiffTools'; -import {getSiblingVNode, travelChildren} from '../vnode/VNodeUtils'; +import { travelChildren } from '../vnode/VNodeUtils'; enum DiffCategory { TEXT_NODE = 'TEXT_NODE', @@ -60,7 +59,7 @@ function checkCanReuseNode(oldNode: VNode | null, newChild: any): boolean { } if (isObjectType(newChild)) { - if (isArrayType(newChild) || isIteratorType(newChild)) { + if (Array.isArray(newChild) || isIteratorType(newChild)) { return oldKey === null; } if (newChild.vtype === TYPE_COMMON_ELEMENT || newChild.vtype === TYPE_PORTAL) { @@ -79,7 +78,7 @@ function getNodeType(newChild: any): string | null { return DiffCategory.TEXT_NODE; } if (isObjectType(newChild)) { - if (isArrayType(newChild) || isIteratorType(newChild)) { + if (Array.isArray(newChild) || isIteratorType(newChild)) { return DiffCategory.ARR_NODE; } if (newChild.vtype === TYPE_COMMON_ELEMENT || newChild.vtype === TYPE_PORTAL) { @@ -200,7 +199,7 @@ function getOldNodeFromMap(nodeMap: Map, newIdx: number, return nodeMap.get(newIdx) || null; } if (isObjectType(newChild)) { - if (isArrayType(newChild) || isIteratorType(newChild)) { + if (Array.isArray(newChild) || isIteratorType(newChild)) { return nodeMap.get(newIdx) || null; } if (newChild.vtype === TYPE_COMMON_ELEMENT || newChild.vtype === TYPE_PORTAL) { @@ -216,11 +215,11 @@ function setCIndex(vNode: VNode, idx: number) { } // diff数组类型的节点,核心算法 -function diffArrayNodes( +function diffArrayNodesHandler( parentNode: VNode, firstChild: VNode | null, newChildren: Array, - isComparing: boolean = true + isComparing: boolean ): VNode | null { let resultingFirstChild: VNode | null = null; @@ -242,6 +241,8 @@ function diffArrayNodes( prevNewNode = newNode; } + let canBeReuse; + let newNode; // 1. 从左侧开始比对currentVNode和newChildren,若不能复用则跳出循环 for (; oldNode !== null && leftIdx < newChildren.length; leftIdx++) { if (oldNode.eIndex > leftIdx) { @@ -249,17 +250,17 @@ function diffArrayNodes( nextOldNode = oldNode; oldNode = null; } else { - nextOldNode = getSiblingVNode(oldNode); + nextOldNode = oldNode.next; } - const canBeReuse = checkCanReuseNode(oldNode, newChildren[leftIdx]); + canBeReuse = checkCanReuseNode(oldNode, newChildren[leftIdx]); // 不能复用,break if (!canBeReuse) { oldNode = oldNode ?? nextOldNode; break; } - const newNode = getNewNode(parentNode, newChildren[leftIdx], oldNode); + newNode = getNewNode(parentNode, newChildren[leftIdx], oldNode); // 没有生成新节点,break if (!newNode) { oldNode = oldNode ?? nextOldNode; @@ -287,19 +288,20 @@ function diffArrayNodes( let rightOldIndex: number | null = rightRemainingOldChildren.length - 1; // 2. 从右侧开始比对currentVNode和newChildren,若不能复用则跳出循环 + let rightOldNode; for (; rightIdx > leftIdx; rightIdx--) { - const rightOldNode = rightRemainingOldChildren[rightOldIndex]; + rightOldNode = rightRemainingOldChildren[rightOldIndex]; if (rightOldIndex < 0 || rightOldNode === null) { break; } - const canBeReuse = checkCanReuseNode(rightOldNode, newChildren[rightIdx - 1]); + canBeReuse = checkCanReuseNode(rightOldNode, newChildren[rightIdx - 1]); // 不能复用,break if (!canBeReuse) { break; } - const newNode = getNewNode(parentNode, newChildren[rightIdx - 1], rightOldNode); + newNode = getNewNode(parentNode, newChildren[rightIdx - 1], rightOldNode); // 没有生成新节点,break if (newNode === null) { break; @@ -346,7 +348,7 @@ function diffArrayNodes( // 4. 新节点还有一部分,但是老节点已经没有了 if (oldNode === null) { for (; leftIdx < rightIdx; leftIdx++) { - const newNode = getNewNode(parentNode, newChildren[leftIdx], null); + newNode = getNewNode(parentNode, newChildren[leftIdx], null); if (newNode !== null) { theLastPosition = setVNodeAdditionFlag(newNode, theLastPosition, isComparing); @@ -372,9 +374,11 @@ function diffArrayNodes( const preIndex: Array = []; // 贪心算法在替换的过程中会使得数组不正确,通过记录preIndex找到正确值 const reuseNodes = []; // 记录复用的 VNode let i = 0; + let oldNodeFromMap; + let last; for (; leftIdx < rightIdx; leftIdx++) { - const oldNodeFromMap = getOldNodeFromMap(leftChildrenMap, leftIdx, newChildren[leftIdx]); - const newNode = getNewNode(parentNode, newChildren[leftIdx], oldNodeFromMap); + oldNodeFromMap = getOldNodeFromMap(leftChildrenMap, leftIdx, newChildren[leftIdx]); + newNode = getNewNode(parentNode, newChildren[leftIdx], oldNodeFromMap); if (newNode !== null) { if (isComparing && !newNode.isCreated) { // 从Map删除,后面不会deleteVNode @@ -384,7 +388,7 @@ function diffArrayNodes( if (oldNodeFromMap !== null) { let eIndex = newNode.eIndex; eIndexes.push(eIndex); - const last: number | undefined = eIndexes[result[result.length - 1]]; + last = eIndexes[result[result.length - 1]]; if (eIndex > last || last === undefined) { // 大的 eIndex直接放在最后 preIndex[i] = result[result.length - 1]; result.push(i); @@ -462,16 +466,6 @@ function setVNodesCIndex(startChild: VNode, startIdx: number) { } } -// 新节点是数组类型 -function diffArrayNodesHandler( - parentNode: VNode, - firstChild: VNode | null, - newChildren: Array, - isComparing: boolean = true -): VNode | null { - return diffArrayNodes(parentNode, firstChild, newChildren, isComparing); -} - // 新节点是迭代器类型 function diffIteratorNodesHandler( parentNode: VNode, @@ -490,7 +484,7 @@ function diffIteratorNodesHandler( result = iteratorObj.next(); } - return diffArrayNodes(parentNode, firstChild, childrenArray, isComparing); + return diffArrayNodesHandler(parentNode, firstChild, childrenArray, isComparing); } // 新节点是字符串类型 @@ -552,13 +546,13 @@ function diffObjectNodeHandler( // 可以复用 if (canReuseNode.tag === Fragment && newChild.type === TYPE_FRAGMENT) { resultNode = updateVNode(canReuseNode, newChild.props.children); - startDelVNode = getSiblingVNode(canReuseNode); + startDelVNode = canReuseNode.next; resultNode.next = null; } else if (isSameType(canReuseNode, newChild)) { resultNode = updateVNode(canReuseNode, newChild.props); resultNode.ref = newChild.ref; resultNode.belongClassVNode = newChild.belongClassVNode; - startDelVNode = getSiblingVNode(resultNode); + startDelVNode = resultNode.next; resultNode.next = null; } } @@ -578,7 +572,7 @@ function diffObjectNodeHandler( // 可以复用 if (canReuseNode.tag === DomPortal && canReuseNode.outerDom === newChild.outerDom) { resultNode = updateVNode(canReuseNode, newChild.children || []); - startDelVNode = getSiblingVNode(canReuseNode); + startDelVNode = canReuseNode.next; resultNode.next = null; } } @@ -629,7 +623,7 @@ export function createChildrenByDiff( } // 3. newChild是数组类型 - if (isArrayType(newChild)) { + if (Array.isArray(newChild)) { return diffArrayNodesHandler(parentNode, firstChild, newChild, isComparing); } diff --git a/libs/horizon/src/renderer/render/BaseComponent.ts b/libs/horizon/src/renderer/render/BaseComponent.ts index 8e5e56d4..1b2edf2d 100644 --- a/libs/horizon/src/renderer/render/BaseComponent.ts +++ b/libs/horizon/src/renderer/render/BaseComponent.ts @@ -66,9 +66,7 @@ export function captureVNode(processing: VNode): VNode | null { // 创建孩子节点 export function createVNodeChildren(processing: VNode, nextChildren: any) { - const isComparing = !processing.isCreated; - - return createChildrenByDiff(processing, processing.child, nextChildren, isComparing); + return createChildrenByDiff(processing, processing.child, nextChildren, !processing.isCreated); } export function markRef(processing: VNode) { diff --git a/libs/horizon/src/renderer/render/ClassComponent.ts b/libs/horizon/src/renderer/render/ClassComponent.ts index b459215c..9c4c1f20 100644 --- a/libs/horizon/src/renderer/render/ClassComponent.ts +++ b/libs/horizon/src/renderer/render/ClassComponent.ts @@ -20,7 +20,7 @@ import { markComponentDidUpdate, markGetSnapshotBeforeUpdate, } from './class/ClassLifeCycleProcessor'; -import { FlagUtils } from '../vnode/VNodeFlags'; +import { FlagUtils, DidCapture } from '../vnode/VNodeFlags'; import { createVNodeChildren, markRef } from './BaseComponent'; import { createUpdateArray, @@ -73,7 +73,7 @@ function createChildren(clazz: any, processing: VNode) { processing.state = processing.realNode.state; const inst = processing.realNode; - const isCatchError = processing.flags.DidCapture; + const isCatchError = (processing.flags & DidCapture) === DidCapture; // 按照已有规格,如果捕获了错误却没有定义getDerivedStateFromError函数,返回的child为null const newElements = isCatchError && typeof clazz.getDerivedStateFromError !== 'function' @@ -122,7 +122,7 @@ export function captureClassComponent(processing: VNode, clazz: any, nextProps: const newContext = getCurrentContext(clazz, processing); // 子节点抛出异常时,如果本class是个捕获异常的处理节点,这时候oldProps是null,所以需要使用props - let oldProps = processing.flags.DidCapture ? processing.props : processing.oldProps; + let oldProps = (processing.flags & DidCapture) === DidCapture ? processing.props : processing.oldProps; if (processing.isLazyComponent) { oldProps = mergeDefaultProps(processing.type, oldProps); } @@ -161,7 +161,7 @@ export function captureClassComponent(processing: VNode, clazz: any, nextProps: inst.props = nextProps; } // 如果捕获了 error,必须更新 - const isCatchError = processing.flags.DidCapture; + const isCatchError = (processing.flags & DidCapture) === DidCapture; shouldUpdate = isCatchError || shouldUpdate; // 更新ref diff --git a/libs/horizon/src/renderer/render/DomComponent.ts b/libs/horizon/src/renderer/render/DomComponent.ts index bac845b1..7aef0d8d 100644 --- a/libs/horizon/src/renderer/render/DomComponent.ts +++ b/libs/horizon/src/renderer/render/DomComponent.ts @@ -1,5 +1,5 @@ -import type {VNode} from '../Types'; -import type {Props} from '../../dom/DOMOperator'; +import type { VNode } from '../Types'; +import type { Props } from '../../dom/DOMOperator'; import { getNamespaceCtx, @@ -12,10 +12,10 @@ import { initDomProps, getPropChangeList, isTextChild, } from '../../dom/DOMOperator'; -import {FlagUtils} from '../vnode/VNodeFlags'; -import {createVNodeChildren, markRef} from './BaseComponent'; -import {DomComponent, DomPortal, DomText} from '../vnode/VNodeTags'; -import {getFirstChild, travelVNodeTree} from '../vnode/VNodeUtils'; +import { FlagUtils } from '../vnode/VNodeFlags'; +import { createVNodeChildren, markRef } from './BaseComponent'; +import { DomComponent, DomPortal, DomText } from '../vnode/VNodeTags'; +import { travelVNodeTree } from '../vnode/VNodeUtils'; function updateDom( processing: VNode, @@ -50,24 +50,6 @@ function updateDom( } } -// 把dom类型的子节点append到parent dom中 -function appendAllChildren(parent: Element, processing: VNode) { - const vNode = processing.child; - if (vNode === null) { - return; - } - - // 向下递归它的子节点,查找所有终端节点。 - travelVNodeTree(vNode, node => { - if (node.tag === DomComponent || node.tag === DomText) { - appendChildElement(parent, node.realNode); - } - }, node => - // 已经append到父节点,或者是DomPortal都不需要处理child了 - node.tag === DomComponent || node.tag === DomText || node.tag === DomPortal - , processing); -} - export function bubbleRender(processing: VNode) { resetNamespaceCtx(processing); @@ -95,7 +77,19 @@ export function bubbleRender(processing: VNode) { processing, ); - appendAllChildren(dom, processing); + // 把dom类型的子节点append到parent dom中 + const vNode = processing.child; + if (vNode !== null) { + // 向下递归它的子节点,查找所有终端节点。 + travelVNodeTree(vNode, node => { + if (node.tag === DomComponent || node.tag === DomText) { + appendChildElement(dom, node.realNode); + } + }, node => + // 已经append到父节点,或者是DomPortal都不需要处理child了 + node.tag === DomComponent || node.tag === DomText || node.tag === DomPortal + , processing); + } processing.realNode = dom; diff --git a/libs/horizon/src/renderer/submit/LifeCycleHandler.ts b/libs/horizon/src/renderer/submit/LifeCycleHandler.ts index 354a179e..55edf2c7 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, Clear } from '../vnode/VNodeFlags'; +import { FlagUtils, ResetText, Clear, Update } from '../vnode/VNodeFlags'; import { mergeDefaultProps } from '../render/LazyComponent'; import { submitDomUpdate, @@ -97,7 +97,7 @@ function callAfterSubmitLifeCycles( } case ClassComponent: { const instance = vNode.realNode; - if (vNode.flags.Update) { + if ((vNode.flags & Update) === Update) { if (vNode.isCreated) { instance.componentDidMount(); } else { @@ -124,7 +124,7 @@ function callAfterSubmitLifeCycles( return; } case DomComponent: { - if (vNode.isCreated && vNode.flags.Update) { + if (vNode.isCreated && (vNode.flags & Update) === Update) { // button、input、select、textarea、如果有 autoFocus 属性需要focus if (shouldAutoFocus(vNode.type, vNode.props)) { vNode.realNode.focus(); @@ -222,7 +222,7 @@ function unmountNestedVNodes(vNode: VNode): void { function submitAddition(vNode: VNode): void { const { parent, parentDom } = findDomParent(vNode); - if (parent.flags.ResetText) { + if ((vNode.flags & ResetText) === ResetText) { // 在insert之前先reset clearText(parentDom); FlagUtils.removeFlag(parent, ResetText); diff --git a/libs/horizon/src/renderer/submit/Submit.ts b/libs/horizon/src/renderer/submit/Submit.ts index 30fee9d8..ae4027f3 100644 --- a/libs/horizon/src/renderer/submit/Submit.ts +++ b/libs/horizon/src/renderer/submit/Submit.ts @@ -1,8 +1,6 @@ import type {VNode} from '../Types'; -import {callRenderQueueImmediate} from '../taskExecutor/RenderQueue'; -import {throwIfTrue} from '../utils/throwIfTrue'; -import {FlagUtils, Addition as AdditionFlag} from '../vnode/VNodeFlags'; +import {FlagUtils, Addition, Snapshot, ResetText, Ref, Update, Deletion, Clear, Callback} from '../vnode/VNodeFlags'; import {prepareForSubmit, resetAfterSubmit} from '../../dom/DOMOperator'; import {handleSubmitError} from '../ErrorHandler'; import { @@ -91,7 +89,7 @@ export function submitToRender(treeRoot) { function beforeSubmit(dirtyNodes: Array) { dirtyNodes.forEach(node => { try { - if (node.flags.Snapshot) { + if ((node.flags & Snapshot) === Snapshot) { callBeforeSubmitLifeCycles(node); } } catch (error) { @@ -103,35 +101,38 @@ function beforeSubmit(dirtyNodes: Array) { function submit(dirtyNodes: Array) { dirtyNodes.forEach(node => { try { - if (node.flags.ResetText) { + if ((node.flags & ResetText) === ResetText) { submitResetTextContent(node); } - if (node.flags.Ref) { + if ((node.flags & Ref) === Ref) { if (!node.isCreated) { // 需要执行 detachRef(node, true); } } - const {Addition, Update, Deletion, Clear} = node.flags; - if (Addition && Update) { + const isAdd = (node.flags & Addition) === Addition; + const isUpdate = (node.flags & Update) === Update; + if (isAdd && isUpdate) { // Addition submitAddition(node); - FlagUtils.removeFlag(node, AdditionFlag); + FlagUtils.removeFlag(node, Addition); // Update submitUpdate(node); } else { - if (Addition) { + const isDeletion = (node.flags & Deletion) === Deletion; + const isClear = (node.flags & Clear) === Clear; + if (isAdd) { submitAddition(node); - FlagUtils.removeFlag(node, AdditionFlag); - } else if (Update) { + FlagUtils.removeFlag(node, Addition); + } else if (isUpdate) { submitUpdate(node); - } else if (Deletion) { + } else if (isDeletion) { submitDeletion(node); } - if (Clear) { + if (isClear) { submitClear(node); } } @@ -144,11 +145,11 @@ function submit(dirtyNodes: Array) { function afterSubmit(dirtyNodes: Array) { dirtyNodes.forEach(node => { try { - if (node.flags.Update || node.flags.Callback) { + if ((node.flags & Update) === Update || (node.flags & Callback) === Callback) { callAfterSubmitLifeCycles(node); } - if (node.flags.Ref) { + if ((node.flags & Ref) === Ref) { attachRef(node); } } catch (error) { diff --git a/libs/horizon/src/renderer/vnode/VNode.ts b/libs/horizon/src/renderer/vnode/VNode.ts index 8ce6d94b..5799e34c 100644 --- a/libs/horizon/src/renderer/vnode/VNode.ts +++ b/libs/horizon/src/renderer/vnode/VNode.ts @@ -1,16 +1,18 @@ /** * 虚拟DOM结构体 */ -import {TreeRoot} from './VNodeTags'; -import type {VNodeTag} from './VNodeTags'; -import type {RefType, ContextType} from '../Types'; -import type {Hook} from '../hooks/HookType'; +import { TreeRoot, FunctionComponent, ClassComponent, DomPortal, DomText, ContextConsumer, ForwardRef, SuspenseComponent, LazyComponent, ClsOrFunComponent, DomComponent, Fragment, ContextProvider, Profiler, MemoComponent, IncompleteClassComponent } from './VNodeTags'; +import type { VNodeTag } from './VNodeTags'; +import type { RefType, ContextType } from '../Types'; +import type { Hook } from '../hooks/HookType'; +import { InitFlag } from './VNodeFlags'; export class VNode { tag: VNodeTag; key: string | null; // 唯一标识符 + props: any; // 传给组件的props的值,类组件包含defaultProps,Lazy组件不包含 type: any = null; - realNode: any = null; // 如果是类,则存放实例;如果是div这种,则存放真实DOM; + realNode: any; // 如果是类,则存放实例;如果是div这种,则存放真实DOM; // 关系结构 parent: VNode | null = null; // 父节点 @@ -20,21 +22,20 @@ export class VNode { eIndex: number = 0; // HorizonElement在jsx中的位置,例如:jsx中的null不会生成vNode,所以eIndex和cIndex不一致 ref: RefType | ((handle: any) => void) | null = null; // 包裹一个函数,submit阶段使用,比如将外部useRef生成的对象赋值到ref上 - props: any; // 传给组件的props的值,类组件包含defaultProps,Lazy组件不包含 oldProps: any = null; - suspensePromises: any = null; // suspense组件的promise列表 - changeList: any = null; // DOM的变更列表 - effectList: any[] | null = null; // useEffect 的更新数组 - updates: any[] | null = null; // TreeRoot和ClassComponent使用的更新数组 - stateCallbacks: any[] | null = null; // 存放存在setState的第二个参数和HorizonDOM.render的第三个参数所在的node数组 - isForceUpdate: boolean = false; // 是否使用强制更新 + suspensePromises: any; // suspense组件的promise列表 + changeList: any; // DOM的变更列表 + effectList: any[] | null; // useEffect 的更新数组 + updates: any[] | null; // TreeRoot和ClassComponent使用的更新数组 + stateCallbacks: any[] | null; // 存放存在setState的第二个参数和HorizonDOM.render的第三个参数所在的node数组 + isForceUpdate: boolean; // 是否使用强制更新 - state: any = null; // ClassComponent和TreeRoot的状态 - hooks: Array> | null = null; // 保存hook + state: any; // ClassComponent和TreeRoot的状态 + hooks: Array> | null; // 保存hook suspenseChildStatus: string = ''; // Suspense的Children是否显示 - depContexts: Array> | null = null; // FunctionComponent和ClassComponent对context的依赖列表 - isDepContextChange: boolean = false; // context是否变更 + depContexts: Array> | null; // FunctionComponent和ClassComponent对context的依赖列表 + isDepContextChange: boolean; // context是否变更 dirtyNodes: Array | null = null; // 需要改动的节点数组 shouldUpdate: boolean = false; childShouldUpdate: boolean = false; @@ -42,40 +43,27 @@ export class VNode { task: any; // 使用这个变量来记录修改前的值,用于恢复。 - contexts = {}; + contexts: any; // 因为LazyComponent会修改tag和type属性,为了能识别,增加一个属性 - isLazyComponent: boolean = false; + isLazyComponent: boolean; // 因为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; - Clear?: boolean; - } = {}; - clearChild: VNode | null = null; + lazyType: any; + flags = InitFlag; + clearChild: VNode | null; // one tree相关属性 isCreated: boolean = true; - oldHooks: Array> | null = []; // 保存上一次执行的hook - oldState: any = null; + oldHooks: Array> | null; // 保存上一次执行的hook + oldState: any; oldRef: RefType | ((handle: any) => void) | null = null; - suspenseChildThrow = false; - oldSuspenseChildStatus: string = ''; // 上一次Suspense的Children是否显示 + suspenseChildThrow: boolean; + oldSuspenseChildStatus: string; // 上一次Suspense的Children是否显示 oldChild: VNode | null = null; - suspenseDidCapture: boolean = false; // suspense是否捕获了异常 - promiseResolve: boolean = false; // suspense的promise是否resolve + suspenseDidCapture: boolean; // suspense是否捕获了异常 + promiseResolve: boolean; // suspense的promise是否resolve path: string = ''; // 保存从根到本节点的路径 - toUpdateNodes: Set | null = null; // 保存要更新的节点 + toUpdateNodes: Set | null; // 保存要更新的节点 belongClassVNode: VNode | null = null; // 记录JSXElement所属class vNode,处理ref的时候使用 @@ -85,18 +73,80 @@ export class VNode { this.props = props; - // 根节点 - if (tag === TreeRoot) { - this.outerDom = outerDom; - this.task = null; - this.toUpdateNodes = new Set(); + switch (tag) { + case TreeRoot: + this.outerDom = outerDom; + this.task = null; + this.toUpdateNodes = new Set(); + this.realNode = null; + this.updates = null; + this.stateCallbacks = null; + this.state = null; + this.oldState = null; + this.contexts = null; + break; + case FunctionComponent: + this.effectList = null; + this.hooks = null; + this.depContexts = null; + this.isDepContextChange = false; + this.oldHooks = null; + break; + case ClassComponent: + this.realNode = null; + this.updates = null; + this.stateCallbacks = null; + this.isForceUpdate = false; + this.state = null; + this.depContexts = null; + this.isDepContextChange = false; + this.oldState = null; + this.contexts = null; + break; + case ClsOrFunComponent: + this.realNode = null; + this.contexts = null; + break; + case DomPortal: + this.realNode = null; + this.contexts = null; + break; + case DomComponent: + this.realNode = null; + this.changeList = null; + this.contexts = null; + break; + case DomText: + this.realNode = null; + break; + case SuspenseComponent: + this.realNode = null; + this.suspensePromises = null; + this.suspenseChildThrow = false; + this.suspenseDidCapture = false; + this.promiseResolve = false; + this.oldSuspenseChildStatus = ''; + break; + case ContextProvider: + this.contexts = null; + break; + case MemoComponent: + this.effectList = null; + break; + case LazyComponent: + this.realNode = null; + this.stateCallbacks = null; + break; + case Fragment: + break; + case ContextConsumer: + break; + case ForwardRef: + break; + case Profiler: + break; + case IncompleteClassComponent: + break; } } - - setContext(contextName, value) { - this.contexts[contextName] = value; - } - getContext(contextName) { - return this.contexts[contextName]; - } } diff --git a/libs/horizon/src/renderer/vnode/VNodeCreator.ts b/libs/horizon/src/renderer/vnode/VNodeCreator.ts index 2d6d3bb2..ca585e5f 100644 --- a/libs/horizon/src/renderer/vnode/VNodeCreator.ts +++ b/libs/horizon/src/renderer/vnode/VNodeCreator.ts @@ -133,9 +133,8 @@ export function createUndeterminedVNode(type, key, props) { vNode.type = type; vNode.shouldUpdate = true; - // lazy类型的特殊处理 - vNode.isLazyComponent = isLazy; if (isLazy) { + vNode.isLazyComponent = isLazy; vNode.lazyType = type; } return vNode; diff --git a/libs/horizon/src/renderer/vnode/VNodeFlags.ts b/libs/horizon/src/renderer/vnode/VNodeFlags.ts index 48d0331d..62f98803 100644 --- a/libs/horizon/src/renderer/vnode/VNodeFlags.ts +++ b/libs/horizon/src/renderer/vnode/VNodeFlags.ts @@ -4,97 +4,79 @@ import type { VNode } from '../Types'; + +export const InitFlag = /** */ 0b000000000000; // vNode节点的flags -export const Addition = 'Addition'; -export const Update = 'Update'; -export const Deletion = 'Deletion'; -export const ResetText = 'ResetText'; -export const Callback = 'Callback'; -export const DidCapture = 'DidCapture'; -export const Ref = 'Ref'; -export const Snapshot = 'Snapshot'; -// 被中断了,抛出错误的vNode以及它的父vNode -export const Interrupted = 'Interrupted'; -export const ShouldCapture = 'ShouldCapture'; -// For suspense -export const ForceUpdate = 'ForceUpdate'; -export const Clear = 'Clear'; - -const FlagArr = [Addition, Update, Deletion, Clear, ResetText, Callback, - DidCapture, Ref, Snapshot, Interrupted, ShouldCapture, ForceUpdate]; - -const LifecycleEffectArr = [Update, Callback, Ref, Snapshot]; - -function resetFlag(node) { - node.flags = {}; -} +export const Addition = /** */ 0b100000000000; +export const Update = /** */ 0b010000000000; +export const Deletion = /** */ 0b001000000000; +export const ResetText =/** */ 0b000100000000; +export const Callback = /** */ 0b000010000000; +export const DidCapture =/** */ 0b000001000000; +export const Ref = /** */ 0b000000100000; +export const Snapshot = /** */ 0b000000010000; +export const Interrupted = /** */ 0b000000001000; // 被中断了,抛出错误的vNode以及它的父vNode +export const ShouldCapture =/** */ 0b000000000100; +export const ForceUpdate = /** */ 0b000000000010; // For suspense +export const Clear = /** */ 0b000000000001; +const LifecycleEffectArr = Update | Callback | Ref | Snapshot; export class FlagUtils { - static removeFlag(node: VNode, flag: string) { - node.flags[flag] = false; + static removeFlag(node: VNode, flag: number) { + const flags = node.flags; + node.flags = flags & (~flag); } static removeLifecycleEffectFlags(node) { - LifecycleEffectArr.forEach(key => { - node.flags[key] = false; - }); + const flags = node.flags; + node.flags = flags & (~LifecycleEffectArr); } static hasAnyFlag(node: VNode) { // 有标志位 - const flags = node.flags; - const arrLength = FlagArr.length; - let key; - for (let i = 0; i < arrLength; i++) { - key = FlagArr[i]; - if (flags[key]) { - return true; - } - } - return false; + return node.flags !== InitFlag; } static setNoFlags(node: VNode) { - resetFlag(node); + node.flags = InitFlag; } static markAddition(node: VNode) { - node.flags.Addition = true; + node.flags |= Addition; } static setAddition(node: VNode) { - resetFlag(node); - node.flags.Addition = true; + node.flags = Addition; } static markUpdate(node: VNode) { - node.flags.Update = true; + node.flags |= Update; } static setDeletion(node: VNode) { - resetFlag(node); - node.flags.Deletion = true; + node.flags = Deletion; } static markContentReset(node: VNode) { - node.flags.ResetText = true; + node.flags |= ResetText; } static markCallback(node: VNode) { - node.flags.Callback = true; + node.flags |= Callback; } static markDidCapture(node: VNode) { - node.flags.DidCapture = true; + node.flags |= DidCapture; } static markShouldCapture(node: VNode) { - node.flags.ShouldCapture = true; + node.flags |= ShouldCapture; } static markRef(node: VNode) { - node.flags.Ref = true; + node.flags |= Ref; } static markSnapshot(node: VNode) { - node.flags.Snapshot = true; + node.flags |= Snapshot; } static markInterrupted(node: VNode) { - node.flags.Interrupted = true; + node.flags |= Interrupted; } static markForceUpdate(node: VNode) { - node.flags.ForceUpdate = true; + node.flags |= ForceUpdate; } + static markClear(node: VNode) { - node.flags.Clear = true; + node.flags |= Clear; } } diff --git a/libs/horizon/src/renderer/vnode/VNodeUtils.ts b/libs/horizon/src/renderer/vnode/VNodeUtils.ts index 82ad3ec8..c9d4b572 100644 --- a/libs/horizon/src/renderer/vnode/VNodeUtils.ts +++ b/libs/horizon/src/renderer/vnode/VNodeUtils.ts @@ -7,10 +7,7 @@ import type {VNode} from '../Types'; import {DomComponent, DomPortal, DomText, TreeRoot} from './VNodeTags'; import {isComment} from '../../dom/utils/Common'; import {getNearestVNode} from '../../dom/DOMInternalKeys'; - -export function getSiblingVNode(node) { - return node.next; -} +import { Addition, InitFlag } from './VNodeFlags'; export function travelChildren(beginVNode: VNode, handleVNode: Function, isFinish?: Function) { let node: VNode | null = beginVNode; @@ -58,7 +55,7 @@ export function travelVNodeTree( } // 找兄弟,没有就往上再找兄弟 - while (getSiblingVNode(node) === null) { + while (node.next === null) { if (node.parent === null || node.parent === overVNode) { return null; } @@ -69,7 +66,7 @@ export function travelVNodeTree( } } // 找到兄弟 - const siblingVNode = getSiblingVNode(node); + const siblingVNode = node.next; siblingVNode.parent = node.parent; node = siblingVNode; } @@ -96,6 +93,7 @@ export function clearVNode(vNode: VNode) { vNode.oldState = null; vNode.oldRef = null; vNode.oldChild = null; + vNode.flags = InitFlag; vNode.toUpdateNodes = null; @@ -175,7 +173,7 @@ export function getSiblingDom(vNode: VNode): Element | null { findSibling: while (true) { // 没有兄弟节点,找父节点 - while (getSiblingVNode(node) === null) { + while (node.next === null) { // 没父节点,或父节点已经是根节点,则返回 if (node.parent === null || isDomContainer(node.parent)) { return null; @@ -183,14 +181,14 @@ export function getSiblingDom(vNode: VNode): Element | null { node = node.parent; } - const siblingVNode = getSiblingVNode(node); + const siblingVNode = node.next; siblingVNode.parent = node.parent; node = siblingVNode; // 如果不是dom节点,往下找 while (!isDomVNode(node)) { // 如果节点也是Addition - if (node.flags.Addition) { + if ((node.flags & Addition) ===Addition) { continue findSibling; } @@ -204,7 +202,7 @@ export function getSiblingDom(vNode: VNode): Element | null { } } - if (!node.flags.Addition) { + if ((node.flags & Addition) ===InitFlag) { // 找到 return node.realNode; }