diff --git a/libs/horizon/src/dom/DOMOperator.ts b/libs/horizon/src/dom/DOMOperator.ts index 58ee6f32..b475375a 100644 --- a/libs/horizon/src/dom/DOMOperator.ts +++ b/libs/horizon/src/dom/DOMOperator.ts @@ -164,6 +164,7 @@ export function submitDomUpdate(tag: string, vNode: VNode) { const type = vNode.type; const changeList = vNode.changeList; vNode.changeList = null; + if (changeList !== null) { saveVNode(vNode, element); updateVNodeProps(element, newProps); diff --git a/libs/horizon/src/renderer/Renderer.ts b/libs/horizon/src/renderer/Renderer.ts index 85fc3d4b..60ac72da 100644 --- a/libs/horizon/src/renderer/Renderer.ts +++ b/libs/horizon/src/renderer/Renderer.ts @@ -38,11 +38,10 @@ export function startUpdate( } export function getFirstCustomDom(treeRoot: VNode): Element | Text | null { - const firstChild = getFirstChild(treeRoot); - if (!firstChild) { + if (!treeRoot.child) { return null; } - return firstChild.realNode; + return treeRoot.child.realNode; } diff --git a/libs/horizon/src/renderer/TreeBuilder.ts b/libs/horizon/src/renderer/TreeBuilder.ts index daed59c5..cc0e5ba0 100644 --- a/libs/horizon/src/renderer/TreeBuilder.ts +++ b/libs/horizon/src/renderer/TreeBuilder.ts @@ -132,12 +132,20 @@ export function calcStartUpdateVNode(treeRoot: VNode) { let node = treeRoot; for (let i = 1; i < startNodePath.length; i++) { let pathIndex = startNodePath[i]; - node = node.children[pathIndex]; + node = getChildByIndex(node, pathIndex); } return node; } +function getChildByIndex(vNode: VNode, idx: number) { + let node = vNode.child; + for (let i = 0; i < idx; i++) { + node = node.next; + } + return node; +} + // 判断数组中节点的path的idx元素是否都相等 function isEqualByIndex(idx: number, nodes: Array) { let val = nodes[0].path[idx]; @@ -225,12 +233,11 @@ function buildVNodeTree(treeRoot: VNode) { // 捕获创建 vNodes const next = captureVNode(processing); - // @ts-ignore - if (!next || !next.length) { + if (next === null) { // 如果没有产生新的,那么就完成当前节点,向上遍历 bubbleVNode(processing); } else { - processing = next[0]; + processing = next; } ProcessingVNode.val = null; diff --git a/libs/horizon/src/renderer/diff/nodeDiffComparator.ts b/libs/horizon/src/renderer/diff/nodeDiffComparator.ts index cbf08e20..ceea92c0 100644 --- a/libs/horizon/src/renderer/diff/nodeDiffComparator.ts +++ b/libs/horizon/src/renderer/diff/nodeDiffComparator.ts @@ -12,7 +12,7 @@ import { isIteratorType, isObjectType, } from './DiffTools'; -import {getSiblingVNode} from '../vnode/VNodeUtils'; +import {getSiblingVNode, travelChildren} from '../vnode/VNodeUtils'; enum DiffCategory { TEXT_NODE = 'TEXT_NODE', @@ -32,15 +32,17 @@ function deleteVNode(parentNode: VNode, delVNode: VNode): void { } // 清除多个节点 -function deleteVNodes(parentVNode: VNode, currentChildren: Array, startIdx: number, endVNode?: VNode): void { - if (currentChildren) { - for (let i = startIdx; i < currentChildren.length; i++) { - const delVNode = currentChildren[i]; - if (delVNode === endVNode) { - return; - } - deleteVNode(parentVNode, delVNode); +function deleteVNodes(parentVNode: VNode, startDelVNode: VNode, endVNode?: VNode): void { + let node = startDelVNode; + + while (node !== null) { + if (node === endVNode) { + return; } + + deleteVNode(parentVNode, node); + + node = node.next; } } @@ -66,7 +68,7 @@ function checkCanReuseNode(oldNode: VNode | null, newChild: any): boolean { return false; } -function getNodeType(parentNode: VNode, newChild: any): string { +function getNodeType(newChild: any): string { if (newChild === null) { return null; } @@ -103,7 +105,7 @@ function setVNodeAdditionFlag(newNode: VNode, lastPosition: number, isComparing: // 获取新节点 function getNewNode(parentNode: VNode, newChild: any, oldNode: VNode | null) { - const newNodeType = getNodeType(parentNode, newChild); + const newNodeType = getNodeType(newChild); if (newNodeType === null) { return null; } @@ -159,29 +161,38 @@ function getNewNode(parentNode: VNode, newChild: any, oldNode: VNode | null) { if (resultNode) { resultNode.parent = parentNode; + resultNode.next = null; } return resultNode; } +function transRightChildrenToArray(child) { + const rightChildrenArray = []; + + travelChildren(child, (node) => { + rightChildrenArray.push(node); + }); + + return rightChildrenArray; +} + function transLeftChildrenToMap( - parentVNode: VNode, - currentChildren: Array, - startIdx: number, + startChild: VNode, rightEndVNode: VNode | null ): Map { const leftChildrenMap: Map = new Map(); - for (let i = startIdx; i < currentChildren.length; i++) { - const currentChild = currentChildren[i]; - if (currentChild === rightEndVNode) { - return leftChildrenMap; - } - leftChildrenMap.set(currentChild.key !== null ? currentChild.key : currentChild.eIndex, currentChild); - } + + travelChildren(startChild, (node) => { + leftChildrenMap.set(node.key !== null ? node.key : node.eIndex, node); + }, (node) => { + return node === rightEndVNode; + }); + return leftChildrenMap; } -function getOldNodeFromMap(parentNode: VNode, nodeMap: Map, newIdx: number, newChild: any) { +function getOldNodeFromMap(nodeMap: Map, newIdx: number, newChild: any) { if (isTextType(newChild)) { return nodeMap.get(newIdx) || null; } @@ -199,16 +210,29 @@ function getOldNodeFromMap(parentNode: VNode, nodeMap: Map | null, + firstChild: VNode | null, newChildren: Array, isComparing: boolean = true -): Array | null { - const resultChildren: Array = []; - let oldNode = (currentChildren.length > 0) ? currentChildren[0] : null; +): VNode | null { + let resultingFirstChild: VNode | null = null; + + let prevNewNode: VNode | null = null; + + let oldNode = firstChild; + let nextOldNode = null; + let theLastPosition = 0; // 从左边开始的位置 let leftIdx = 0; - let nextOldNode = null; + + function appendNode(newNode) { + if (prevNewNode === null) { + resultingFirstChild = newNode; + } else { + prevNewNode.next = newNode; + } + prevNewNode = newNode; + } // 1. 从左侧开始比对currentVNode和newChildren,若不能复用则跳出循环 for (; oldNode !== null && leftIdx < newChildren.length; leftIdx++) { @@ -241,16 +265,17 @@ function diffArrayNodes( theLastPosition = setVNodeAdditionFlag(newNode, theLastPosition, isComparing); newNode.eIndex = leftIdx; - resultChildren.push(newNode); + setCIndex(newNode, leftIdx); + appendNode(newNode); oldNode = nextOldNode; } let rightIdx = newChildren.length; - let rightEndOldNode; // 老节点中最右边不匹配的节点引用 abcde --> abfde 则rightEndOldNode = f; - const rightNewNodes: Array = []; // 最右边匹配的节点引用 abcde --> abfde rightNewNode = [d, e]; + let rightEndOldNode; // 老节点中最右边匹配的节点引用 abcde --> abfde 则rightEndOldNode = c; + let rightNewNode = null; // 最右边匹配的节点引用 abcde --> abfde 则rightNewNode = d; // 从后往前,新资源的位置还没有到最末端,旧的vNode也还没遍历完,则可以考虑从后往前开始 if (rightIdx > leftIdx && oldNode !== null) { - const rightRemainingOldChildren = currentChildren.slice(leftIdx); + const rightRemainingOldChildren = transRightChildrenToArray(oldNode); let rightOldIndex = rightRemainingOldChildren.length - 1; // 2. 从右侧开始比对currentVNode和newChildren,若不能复用则跳出循环 @@ -272,7 +297,13 @@ function diffArrayNodes( break; } - rightNewNodes.unshift(newNode); + // 链接起来 + if (rightNewNode === null) { + rightNewNode = newNode; + } else { + newNode.next = rightNewNode; + rightNewNode = newNode; + } if (isComparing && rightOldNode && newNode.isCreated) { deleteVNode(parentNode, rightOldNode); @@ -288,10 +319,15 @@ function diffArrayNodes( // 3. 新节点已经处理完成 if (leftIdx === rightIdx) { if (isComparing) { - deleteVNodes(parentNode, currentChildren, leftIdx, rightEndOldNode); + deleteVNodes(parentNode, oldNode, rightEndOldNode); } - return mergeResultChildren(resultChildren, rightNewNodes); + if (rightNewNode) { + appendNode(rightNewNode); + setVNodesCIndex(rightNewNode, prevNewNode.cIndex + 1); + } + + return resultingFirstChild; } // 4. 新节点还有一部分,但是老节点已经没有了 @@ -302,18 +338,23 @@ function diffArrayNodes( if (newNode !== null) { theLastPosition = setVNodeAdditionFlag(newNode, theLastPosition, isComparing); newNode.eIndex = leftIdx; - resultChildren.push(newNode); + appendNode(newNode); } } - return mergeResultChildren(resultChildren, rightNewNodes); + if (rightNewNode) { + appendNode(rightNewNode); + setVNodesCIndex(rightNewNode, prevNewNode.cIndex + 1); + } + + return resultingFirstChild; } // 5. 新节点还有一部分,但是老节点也还有一部分 // 把剩下的currentVNode转成Map - const leftChildrenMap = transLeftChildrenToMap(parentNode, currentChildren, currentChildren.indexOf(oldNode), rightEndOldNode); + const leftChildrenMap = transLeftChildrenToMap(oldNode, rightEndOldNode); for (; leftIdx < rightIdx; leftIdx++) { - const oldNodeFromMap = getOldNodeFromMap(parentNode, leftChildrenMap, leftIdx, newChildren[leftIdx]); + const oldNodeFromMap = getOldNodeFromMap(leftChildrenMap, leftIdx, newChildren[leftIdx]); const newNode = getNewNode(parentNode, newChildren[leftIdx], oldNodeFromMap); if (newNode !== null) { if (isComparing && !newNode.isCreated) { @@ -323,53 +364,59 @@ function diffArrayNodes( theLastPosition = setVNodeAdditionFlag(newNode, theLastPosition, isComparing); newNode.eIndex = leftIdx; - resultChildren.push(newNode); + appendNode(newNode); } } if (isComparing) { - leftChildrenMap.forEach(child => deleteVNode(parentNode, child)); + leftChildrenMap.forEach(child => { + deleteVNode(parentNode, child); + }); } - return mergeResultChildren(resultChildren, rightNewNodes); + if (rightNewNode) { + appendNode(rightNewNode); + setVNodesCIndex(rightNewNode, prevNewNode.cIndex + 1); + } + + return resultingFirstChild; } // 设置vNode中的cIndex属性,cIndex是节点在children中的位置 -function setVNodeCIndex(resultChildren) { - resultChildren.forEach((node, idx) => { +function setVNodesCIndex(startChild: VNode, startIdx: number) { + let node = startChild; + let idx = startIdx; + + while (node !== null) { node.cIndex = idx; updateVNodePath(node); - }); + node = node.next; + idx++; + } } -function mergeResultChildren(resultChildren: Array, rightNewNodes: Array): Array { - if (rightNewNodes) { - resultChildren.push(...rightNewNodes); - } - - // 设置vNode中的cIndex属性,cIndex是节点在children中的位置 - setVNodeCIndex(resultChildren); - - return resultChildren; +function setCIndex(vNode: VNode, idx: number) { + vNode.cIndex = idx; + updateVNodePath(vNode); } // 新节点是数组类型 function diffArrayNodesHandler( parentNode: VNode, - currentVNode: Array | null, + firstChild: VNode | null, newChildren: Array, isComparing: boolean = true -): Array | null { - return diffArrayNodes(parentNode, currentVNode, newChildren, isComparing); +): VNode | null { + return diffArrayNodes(parentNode, firstChild, newChildren, isComparing); } // 新节点是迭代器类型 function diffIteratorNodesHandler( parentNode: VNode, - currentVNode: Array | null, + firstChild: VNode | null, newChildrenIterable: Iterable, isComparing: boolean = true -): Array | null { +): VNode | null { const iteratorFn = getIteratorFn(newChildrenIterable); const iteratorObj = iteratorFn.call(newChildrenIterable); @@ -381,13 +428,12 @@ function diffIteratorNodesHandler( result = iteratorObj.next(); } - return diffArrayNodes(parentNode, currentVNode, childrenArray, isComparing); + return diffArrayNodes(parentNode, firstChild, childrenArray, isComparing); } // 新节点是字符串类型 function diffStringNodeHandler( parentNode: VNode, - currentChildren: Array | null, newChild: any, firstChildVNode: VNode, isComparing: boolean @@ -397,10 +443,11 @@ function diffStringNodeHandler( // 第一个vNode是Text,则复用 if (firstChildVNode !== null && firstChildVNode.tag === DomText) { newTextNode = updateVNode(firstChildVNode, String(newChild)); - deleteVNodes(parentNode, currentChildren, 1); + deleteVNodes(parentNode, firstChildVNode.next); + newTextNode.next = null; } else { newTextNode = createVNode(DomText, String(newChild)); - deleteVNodes(parentNode, currentChildren, 0); + deleteVNodes(parentNode, firstChildVNode); } if (isComparing && newTextNode.isCreated) { @@ -410,13 +457,13 @@ function diffStringNodeHandler( newTextNode.cIndex = 0; updateVNodePath(newTextNode); - return [newTextNode]; + return newTextNode; } // 新节点是对象类型 function diffObjectNodeHandler( parentNode: VNode, - currentChildren: Array | null, + firstChild: VNode | null, newChild: any, firstChildVNode: VNode, isComparing: boolean @@ -425,13 +472,14 @@ function diffObjectNodeHandler( // 通过key比对是否有可以reuse const newKey = newChild.key; - for (let i = 0; i < currentChildren.length; i++) { - const oldNode = currentChildren[i]; - if (oldNode.key === newKey) { - canReuseNode = oldNode; + let node = firstChild; + while (node !== null) { + if (node.key === newKey) { + canReuseNode = node; break; } else { - deleteVNode(parentNode, oldNode); + deleteVNode(parentNode, node); + node = node.next; } } @@ -443,10 +491,12 @@ function diffObjectNodeHandler( if (canReuseNode.tag === Fragment && newChild.type === TYPE_FRAGMENT) { resultNode = updateVNode(canReuseNode, newChild.props.children); startDelVNode = getSiblingVNode(canReuseNode); + resultNode.next = null; } else if (isSameType(canReuseNode, newChild)) { resultNode = updateVNode(canReuseNode, newChild.props); resultNode.ref = createRef(newChild); - startDelVNode = getSiblingVNode(canReuseNode); + startDelVNode = getSiblingVNode(resultNode); + resultNode.next = null; } } @@ -465,6 +515,7 @@ function diffObjectNodeHandler( if (canReuseNode.tag === DomPortal && canReuseNode.outerDom === newChild.outerDom) { resultNode = updateVNode(canReuseNode, newChild.children || []); startDelVNode = getSiblingVNode(canReuseNode); + resultNode.next = null; } } if (resultNode === null) { @@ -482,9 +533,9 @@ function diffObjectNodeHandler( resultNode.cIndex = 0; updateVNodePath(resultNode); if (startDelVNode) { - deleteVNodes(parentNode, currentChildren, startDelVNode.cIndex); + deleteVNodes(parentNode, startDelVNode); } - return [resultNode]; + return resultNode; } return null; @@ -493,48 +544,47 @@ function diffObjectNodeHandler( // Diff算法的对外接口 export function createChildrenByDiff( parentNode: VNode, - currentChildren: Array | null, + firstChild: VNode | null, newChild: any, isComparing: boolean = true -): Array | null { +): VNode | null { const isFragment = isNoKeyFragment(newChild); newChild = isFragment ? newChild.props.children : newChild; // 1. 没有新节点,直接把vNode标记为删除 if (newChild == null) { if (isComparing) { - deleteVNodes(parentNode, currentChildren, 0); + deleteVNodes(parentNode, firstChild); } return null; } - const firstChildVNode = currentChildren.length ? currentChildren[0] : null; // 2. newChild是字串类型 if (isTextType(newChild)) { - return diffStringNodeHandler(parentNode, currentChildren, newChild, firstChildVNode, isComparing); + return diffStringNodeHandler(parentNode, newChild, firstChild, isComparing); } // 3. newChild是数组类型 if (isArrayType(newChild)) { - return diffArrayNodesHandler(parentNode, currentChildren, newChild, isComparing); + return diffArrayNodesHandler(parentNode, firstChild, newChild, isComparing); } // 4. newChild是迭代器类型 if (isIteratorType(newChild)) { - return diffIteratorNodesHandler(parentNode, currentChildren, newChild, isComparing); + return diffIteratorNodesHandler(parentNode, firstChild, newChild, isComparing); } // 5. newChild是对象类型 if (isObjectType(newChild)) { - const newVNodes = diffObjectNodeHandler(parentNode, currentChildren, newChild, firstChildVNode, isComparing); + const newVNodes = diffObjectNodeHandler(parentNode, firstChild, newChild, firstChild, isComparing); if (newVNodes) { return newVNodes; } } // 6. 其它情况删除所有节点 - if (firstChildVNode) { - deleteVNodes(parentNode, currentChildren, firstChildVNode.cIndex); + if (firstChild) { + deleteVNodes(parentNode, firstChild); } return null; diff --git a/libs/horizon/src/renderer/render/BaseComponent.ts b/libs/horizon/src/renderer/render/BaseComponent.ts index 8a8d9f62..b045b215 100644 --- a/libs/horizon/src/renderer/render/BaseComponent.ts +++ b/libs/horizon/src/renderer/render/BaseComponent.ts @@ -15,7 +15,7 @@ import { createChildrenByDiff } from '../diff/nodeDiffComparator'; import {onlyUpdateChildVNodes} from '../vnode/VNodeCreator'; import componentRenders from './index'; -export function captureVNode(processing: VNode): Array | VNode | null { +export function captureVNode(processing: VNode): VNode | null { const component = componentRenders[processing.tag]; if (processing.tag !== SuspenseComponent) { @@ -65,10 +65,9 @@ function handlerContext(processing: VNode) { // 创建孩子节点 export function createVNodeChildren(processing: VNode, nextChildren: any) { - const currentChildren = !processing.isCreated ? (processing.children ?? []) : []; const isComparing = !processing.isCreated; - return createChildrenByDiff(processing, currentChildren, nextChildren, isComparing); + return createChildrenByDiff(processing, processing.child, nextChildren, isComparing); } export function markRef(processing: VNode) { diff --git a/libs/horizon/src/renderer/render/ClassComponent.ts b/libs/horizon/src/renderer/render/ClassComponent.ts index 6236bffe..72ff9224 100644 --- a/libs/horizon/src/renderer/render/ClassComponent.ts +++ b/libs/horizon/src/renderer/render/ClassComponent.ts @@ -30,7 +30,7 @@ import { getContextChangeCtx, setContextChangeCtx } from '../ContextSaver'; import ProcessingVNode from '../vnode/ProcessingVNode'; import {onlyUpdateChildVNodes} from '../vnode/VNodeCreator'; -export function captureRender(processing: VNode): Array | null { +export function captureRender(processing: VNode): VNode | null { const clazz = processing.type; const props = processing.props; const nextProps = processing.isLazyComponent ? mergeDefaultProps(clazz, props) : props; @@ -44,13 +44,13 @@ export function bubbleRender(processing: VNode) { } // 用于未完成的类组件 -export function getIncompleteClassComponent(clazz, processing: VNode, nextProps: object): Array | null { +export function getIncompleteClassComponent(clazz, processing: VNode, nextProps: object):VNode | null { mountInstance(clazz, processing, nextProps); return createChildren(clazz, processing); } // 用于类组件 -export function captureClassComponent(processing: VNode, clazz: any, nextProps: object): Array | null { +export function captureClassComponent(processing: VNode, clazz: any, nextProps: object): VNode | null { const isOldCxtExist = isOldProvider(clazz); cacheOldCtx(processing, isOldCxtExist); @@ -169,8 +169,8 @@ function createChildren(clazz: any, processing: VNode) { ? null : inst.render(); - processing.children = createVNodeChildren(processing, newElements); - return processing.children; + processing.child = createVNodeChildren(processing, newElements); + return processing.child; } // 获取当前节点的context diff --git a/libs/horizon/src/renderer/render/ClsOrFunComponent.ts b/libs/horizon/src/renderer/render/ClsOrFunComponent.ts index 7ded07fc..4a2204a1 100644 --- a/libs/horizon/src/renderer/render/ClsOrFunComponent.ts +++ b/libs/horizon/src/renderer/render/ClsOrFunComponent.ts @@ -7,7 +7,7 @@ import {FlagUtils} from '../vnode/VNodeFlags'; import {exeFunctionHook} from '../hooks/HookMain'; import {createVNodeChildren} from './BaseComponent'; -export function captureRender(processing: VNode): Array | null { +export function captureRender(processing: VNode): VNode | null { return captureIndeterminateComponent(processing); } @@ -15,7 +15,7 @@ export function bubbleRender() {} function captureIndeterminateComponent( processing: VNode | null, -): Array | null { +): VNode | null { const funcComp = processing.type; if (!processing.isCreated) { @@ -31,6 +31,6 @@ function captureIndeterminateComponent( const newElements = exeFunctionHook(funcComp, props, context, processing); processing.tag = FunctionComponent; - processing.children = createVNodeChildren(processing, newElements); - return processing.children; + processing.child = createVNodeChildren(processing, newElements); + return processing.child; } diff --git a/libs/horizon/src/renderer/render/ContextConsumer.ts b/libs/horizon/src/renderer/render/ContextConsumer.ts index fb99f8ab..7fcbd59c 100644 --- a/libs/horizon/src/renderer/render/ContextConsumer.ts +++ b/libs/horizon/src/renderer/render/ContextConsumer.ts @@ -3,7 +3,7 @@ import type {VNode, ContextType} from '../Types'; import {resetDepContexts, getNewContext} from '../components/context/Context'; import {createVNodeChildren} from './BaseComponent'; -export function captureRender(processing: VNode): Array | null { +export function captureRender(processing: VNode): VNode | null { return captureContextConsumer(processing); } @@ -18,6 +18,6 @@ function captureContextConsumer(processing: VNode) { const contextVal = getNewContext(processing, context); const newChildren = renderFunc(contextVal); - processing.children = createVNodeChildren(processing, newChildren); - return processing.children; + processing.child = createVNodeChildren(processing, newChildren); + return processing.child; } diff --git a/libs/horizon/src/renderer/render/ContextProvider.ts b/libs/horizon/src/renderer/render/ContextProvider.ts index 9f2f2e88..c0ef07e6 100644 --- a/libs/horizon/src/renderer/render/ContextProvider.ts +++ b/libs/horizon/src/renderer/render/ContextProvider.ts @@ -14,7 +14,7 @@ import {launchUpdateFromVNode} from '../TreeBuilder'; import {onlyUpdateChildVNodes} from '../vnode/VNodeCreator'; import {setParentsChildShouldUpdate} from '../vnode/VNodeShouldUpdate'; -export function captureRender(processing: VNode): Array | null { +export function captureRender(processing: VNode): VNode | null { return captureContextProvider(processing); } @@ -22,7 +22,7 @@ export function bubbleRender(processing: VNode) { resetContextCtx(processing); } -function captureContextProvider(processing: VNode): Array | null { +function captureContextProvider(processing: VNode): VNode | null { const providerType: ProviderType = processing.type; const contextType: ContextType = providerType._context; @@ -50,8 +50,8 @@ function captureContextProvider(processing: VNode): Array | null { } const newElements = newProps.children; - processing.children = createVNodeChildren(processing, newElements); - return processing.children; + processing.child = createVNodeChildren(processing, newElements); + return processing.child; } // 从依赖中找到匹配context的VNode @@ -80,7 +80,7 @@ function matchDependencies(depContexts, context, vNode): boolean { // 从当前子节点开始向下遍历,找到消费此context的组件,并更新 function handleContextChange(processing: VNode, context: ContextType): void { - const vNode = getFirstChild(processing); + const vNode = processing.child; if (vNode === null) { return; } diff --git a/libs/horizon/src/renderer/render/DomComponent.ts b/libs/horizon/src/renderer/render/DomComponent.ts index 5339ac6a..88122643 100644 --- a/libs/horizon/src/renderer/render/DomComponent.ts +++ b/libs/horizon/src/renderer/render/DomComponent.ts @@ -17,7 +17,7 @@ import {createVNodeChildren, markRef} from './BaseComponent'; import {DomComponent, DomPortal, DomText} from '../vnode/VNodeTags'; import {getFirstChild, travelVNodeTree} from '../vnode/VNodeUtils'; -export function captureRender(processing: VNode): Array | null { +export function captureRender(processing: VNode): VNode | null { return captureDomComponent(processing); } @@ -63,7 +63,7 @@ export function bubbleRender(processing: VNode) { } } -function captureDomComponent(processing: VNode): Array | null { +function captureDomComponent(processing: VNode): VNode | null { setNamespaceCtx(processing); const type = processing.type; @@ -82,13 +82,13 @@ function captureDomComponent(processing: VNode): Array | null { } markRef(processing); - processing.children = createVNodeChildren(processing, nextChildren); - return processing.children; + processing.child = createVNodeChildren(processing, nextChildren); + return processing.child; } // 把dom类型的子节点append到parent dom中 function appendAllChildren(parent: Element, processing: VNode) { - const vNode = getFirstChild(processing); + const vNode = processing.child; if (vNode === null) { return; } @@ -126,7 +126,7 @@ function updateDom( ); processing.changeList = changeList; - // 输入类的直接标记更新 + // 输入类型的直接标记更新 if (type === 'input' || type === 'textarea' || type === 'select' || type === 'option') { FlagUtils.markUpdate(processing); } else { diff --git a/libs/horizon/src/renderer/render/DomPortal.ts b/libs/horizon/src/renderer/render/DomPortal.ts index d1bd60ac..90f0545c 100644 --- a/libs/horizon/src/renderer/render/DomPortal.ts +++ b/libs/horizon/src/renderer/render/DomPortal.ts @@ -4,7 +4,7 @@ import { createChildrenByDiff } from '../diff/nodeDiffComparator'; import { createVNodeChildren } from './BaseComponent'; import { prePortal } from '../../dom/DOMOperator'; -export function captureRender(processing: VNode): Array | null { +export function captureRender(processing: VNode): VNode | null { return capturePortalComponent(processing); } @@ -21,9 +21,9 @@ function capturePortalComponent(processing: VNode) { const newElements = processing.props; if (processing.isCreated) { - processing.children = createChildrenByDiff(processing, [], newElements); + processing.child = createChildrenByDiff(processing, null, newElements); } else { - processing.children = createVNodeChildren(processing, newElements); + processing.child = createVNodeChildren(processing, newElements); } - return processing.children; + return processing.child; } diff --git a/libs/horizon/src/renderer/render/ForwardRef.ts b/libs/horizon/src/renderer/render/ForwardRef.ts index 6ae2fb64..35680a3f 100644 --- a/libs/horizon/src/renderer/render/ForwardRef.ts +++ b/libs/horizon/src/renderer/render/ForwardRef.ts @@ -1,7 +1,7 @@ import type {VNode} from '../Types'; import {captureRender as funCaptureRender} from './FunctionComponent'; -export function captureRender(processing: VNode, shouldUpdate?: boolean): Array | null { +export function captureRender(processing: VNode, shouldUpdate?: boolean): VNode | null { return funCaptureRender(processing, shouldUpdate); } diff --git a/libs/horizon/src/renderer/render/Fragment.ts b/libs/horizon/src/renderer/render/Fragment.ts index 286335f9..a64c2532 100644 --- a/libs/horizon/src/renderer/render/Fragment.ts +++ b/libs/horizon/src/renderer/render/Fragment.ts @@ -1,7 +1,7 @@ import type {VNode} from '../Types'; import {createVNodeChildren} from './BaseComponent'; -export function captureRender(processing: VNode): Array | null { +export function captureRender(processing: VNode): VNode | null { return captureFragment(processing); } @@ -9,6 +9,6 @@ export function bubbleRender() {} function captureFragment(processing: VNode) { const newElement = processing.props; - processing.children = createVNodeChildren(processing, newElement); - return processing.children; + processing.child = createVNodeChildren(processing, newElement); + return processing.child; } diff --git a/libs/horizon/src/renderer/render/FunctionComponent.ts b/libs/horizon/src/renderer/render/FunctionComponent.ts index 862e62c0..4956d57b 100644 --- a/libs/horizon/src/renderer/render/FunctionComponent.ts +++ b/libs/horizon/src/renderer/render/FunctionComponent.ts @@ -13,7 +13,7 @@ import {onlyUpdateChildVNodes} from '../vnode/VNodeCreator'; // 在useState, useReducer的时候,会触发state变化 let stateChange = false; -export function captureRender(processing: VNode, shouldUpdate?: boolean): Array | null { +export function captureRender(processing: VNode, shouldUpdate?: boolean): VNode | null { const Component = processing.type; const unresolvedProps = processing.props; const resolvedProps = @@ -62,8 +62,8 @@ export function captureFunctionComponent( return onlyUpdateChildVNodes(processing); } - processing.children = createVNodeChildren(processing, newElements); - return processing.children; + processing.child = createVNodeChildren(processing, newElements); + return processing.child; } // 判断children是否可以复用 diff --git a/libs/horizon/src/renderer/render/MemoComponent.ts b/libs/horizon/src/renderer/render/MemoComponent.ts index 4be5060c..2c24c0d7 100644 --- a/libs/horizon/src/renderer/render/MemoComponent.ts +++ b/libs/horizon/src/renderer/render/MemoComponent.ts @@ -10,7 +10,7 @@ import { } from '../utils/elementType'; import {Fragment} from '../vnode/VNodeTags'; -export function captureRender(processing: VNode, shouldUpdate: boolean): Array | null { +export function captureRender(processing: VNode, shouldUpdate: boolean): VNode | null { return captureMemoComponent(processing, shouldUpdate); } @@ -19,7 +19,7 @@ export function bubbleRender() {} export function captureMemoComponent( processing: VNode, shouldUpdate: boolean, -): Array | null { +): VNode | null { const Component = processing.type; // 合并 函数组件或类组件 的defaultProps const newProps = mergeDefaultProps(Component, processing.props); @@ -35,12 +35,12 @@ export function captureMemoComponent( newChild.parent = processing; newChild.ref = processing.ref; updateVNodePath(newChild); - processing.children = [newChild]; + processing.child = newChild; - return processing.children; + return newChild; } - const firstChild = processing.children.length ? processing.children[0] : null; // Memo只有一个child + const firstChild = processing.child; // Memo只有一个child if (!shouldUpdate) { const oldProps = firstChild.props; // 默认是浅对比 @@ -55,7 +55,7 @@ export function captureMemoComponent( newChild.cIndex = 0; updateVNodePath(newChild); newChild.ref = processing.ref; - processing.children = [newChild]; + processing.child = newChild; - return processing.children; + return newChild; } diff --git a/libs/horizon/src/renderer/render/SuspenseComponent.ts b/libs/horizon/src/renderer/render/SuspenseComponent.ts index 119c1f9c..d0cb6f9e 100644 --- a/libs/horizon/src/renderer/render/SuspenseComponent.ts +++ b/libs/horizon/src/renderer/render/SuspenseComponent.ts @@ -37,7 +37,7 @@ export function captureRender(processing: VNode, shouldUpdate: boolean): Array | VNode | null { - const childFragment: VNode = processing.children[0]; + const childFragment: VNode = processing.child; if (childFragment.childShouldUpdate) { if (processing.promiseResolve) { @@ -45,7 +45,7 @@ function updateFallback(processing: VNode): Array | VNode | null { return captureSuspenseComponent(processing); } else { // promise未完成,继续显示fallback,不需要继续刷新子节点 - const fallbackFragment: VNode = processing.children[1]; + const fallbackFragment: VNode = processing.child.next; childFragment.childShouldUpdate = false; fallbackFragment.childShouldUpdate = false; return null; @@ -92,10 +92,11 @@ export function captureSuspenseComponent(processing: VNode) { function createSuspenseChildren(processing: VNode, newChildren) { let childFragment: VNode; if (!processing.isCreated) { - const oldChildFragment: VNode = processing.children[0]; - const oldFallbackFragment: VNode | null = processing.children.length > 1 ? processing.children[1] : null; + const oldChildFragment: VNode = processing.child; + const oldFallbackFragment: VNode | null = oldChildFragment.next; childFragment = updateVNode(oldChildFragment); + childFragment.next = null; // 将Suspense新的子参数传给子Fragment childFragment.props = processing.props.children; childFragment.shouldUpdate = true; @@ -114,19 +115,19 @@ function createSuspenseChildren(processing: VNode, newChildren) { childFragment.parent = processing; childFragment.cIndex = 0; updateVNodePath(childFragment); - processing.children = [childFragment]; + processing.child = childFragment; processing.promiseResolve = false; - return processing.children; + return processing.child; } // 创建fallback子节点 function createFallback(processing: VNode, fallbackChildren) { - const childFragment: VNode = processing.children[0]; + const childFragment: VNode = processing.child; let fallbackFragment; childFragment.childShouldUpdate = false; if (!processing.isCreated) { - const oldFallbackFragment: VNode | null = processing.oldChildren.length > 1 ? processing.oldChildren[1] : null; + const oldFallbackFragment: VNode | null = processing.oldChild ? processing.oldChild.next : null; if (oldFallbackFragment !== null) { fallbackFragment = updateVNode(oldFallbackFragment, fallbackChildren); @@ -139,7 +140,8 @@ function createFallback(processing: VNode, fallbackChildren) { fallbackFragment = createVNode(Fragment, null, fallbackChildren); } - processing.children = [childFragment, fallbackFragment]; + processing.child = childFragment; + childFragment.next = fallbackFragment; childFragment.parent = processing; fallbackFragment.parent = processing; fallbackFragment.eIndex = 1; @@ -147,7 +149,7 @@ function createFallback(processing: VNode, fallbackChildren) { updateVNodePath(fallbackFragment); processing.suspenseChildStatus = SuspenseChildStatus.ShowFallback; - return [fallbackFragment]; + return fallbackFragment; } // 处理Suspense子组件抛出的promise diff --git a/libs/horizon/src/renderer/render/TreeRoot.ts b/libs/horizon/src/renderer/render/TreeRoot.ts index 075e3984..8b4f2026 100644 --- a/libs/horizon/src/renderer/render/TreeRoot.ts +++ b/libs/horizon/src/renderer/render/TreeRoot.ts @@ -37,6 +37,6 @@ function updateTreeRoot(processing) { if (newElement === oldElement) { return onlyUpdateChildVNodes(processing); } - processing.children = createVNodeChildren(processing, newElement); - return processing.children; + processing.child = createVNodeChildren(processing, newElement); + return processing.child; } diff --git a/libs/horizon/src/renderer/submit/LifeCycleHandler.ts b/libs/horizon/src/renderer/submit/LifeCycleHandler.ts index 97322327..b23beb93 100644 --- a/libs/horizon/src/renderer/submit/LifeCycleHandler.ts +++ b/libs/horizon/src/renderer/submit/LifeCycleHandler.ts @@ -124,8 +124,7 @@ function callAfterSubmitLifeCycles( return; } case TreeRoot: { - const childVNode = (vNode.children && vNode.children.length) ? vNode.children[0] : null; - const instance = childVNode !== null ? childVNode.realNode : null; + const instance = vNode.child !== null ? vNode.child.realNode : null; callStateCallback(vNode, instance); return; } @@ -261,10 +260,11 @@ function insertOrAppendPlacementNode( // 这里不做处理,直接在portal中处理 } else { // 插入子节点们 - const children = node.children || []; - children.forEach(child => { + let child = node.child; + while (child !== null) { insertOrAppendPlacementNode(child, beforeDom, parent); - }); + child = child.next; + } } } @@ -289,7 +289,7 @@ function unmountDomComponents(vNode: VNode): void { // 在所有子项都卸载后,删除dom树中的节点 removeChildDom(currentParent, node.realNode); } else if (node.tag === DomPortal) { - if (node.children.length) { + if (node.child !== null) { currentParent = node.outerDom; } } else { @@ -336,10 +336,10 @@ function submitUpdate(vNode: VNode): void { } } -function submitSuspenseComponent(finishedWork: VNode) { - const suspenseChildStatus = finishedWork.suspenseChildStatus; +function submitSuspenseComponent(vNode: VNode) { + const suspenseChildStatus = vNode.suspenseChildStatus; if (suspenseChildStatus !== SuspenseChildStatus.Init) { - hideOrUnhideAllChildren(finishedWork.children[0], suspenseChildStatus === SuspenseChildStatus.ShowFallback); + hideOrUnhideAllChildren(vNode.child, suspenseChildStatus === SuspenseChildStatus.ShowFallback); } } diff --git a/libs/horizon/src/renderer/submit/Submit.ts b/libs/horizon/src/renderer/submit/Submit.ts index 654d5367..95ada780 100644 --- a/libs/horizon/src/renderer/submit/Submit.ts +++ b/libs/horizon/src/renderer/submit/Submit.ts @@ -202,6 +202,7 @@ export function clearDirtyNodes(dirtyNodes) { dirtyNodes.forEach(node => { if (node.flags.Deletion) { node.realNode = null; + node.next = null; } }); } diff --git a/libs/horizon/src/renderer/vnode/VNode.ts b/libs/horizon/src/renderer/vnode/VNode.ts index f726c4a4..997d8bca 100644 --- a/libs/horizon/src/renderer/vnode/VNode.ts +++ b/libs/horizon/src/renderer/vnode/VNode.ts @@ -14,7 +14,8 @@ export class VNode { // 关系结构 parent: VNode | null = null; // 父节点 - children: Array | null = null; // 子节点 + child: VNode | null = null; // 子节点 + next: VNode | null = null; // 兄弟节点 cIndex: number = 0; // 节点在children数组中的位置 eIndex: number = 0; // HorizonElement在jsx中的位置,例如:jsx中的null不会生成vNode,所以eIndex和cIndex不一致 @@ -68,7 +69,7 @@ export class VNode { oldRef: RefType | ((handle: any) => void) | null = null; suspenseChildThrow = false; oldSuspenseChildStatus: string = ''; // 上一次Suspense的Children是否显示 - oldChildren: Array | null = null; + oldChild: VNode | null = null; suspenseDidCapture: boolean = false; // suspense是否捕获了异常 promiseResolve: boolean = false; // suspense的promise是否resolve diff --git a/libs/horizon/src/renderer/vnode/VNodeCreator.ts b/libs/horizon/src/renderer/vnode/VNodeCreator.ts index e648e3b1..71c4a8dc 100644 --- a/libs/horizon/src/renderer/vnode/VNodeCreator.ts +++ b/libs/horizon/src/renderer/vnode/VNodeCreator.ts @@ -69,7 +69,7 @@ export function updateVNode(vNode: VNode, vNodeProps?: any): VNode { if (vNode.tag === SuspenseComponent) { vNode.oldSuspenseChildStatus = vNode.suspenseChildStatus; - vNode.oldChildren = vNode.children; + vNode.oldChild = vNode.child; } vNode.oldProps = vNode.props; @@ -169,20 +169,22 @@ export function createVNodeFromElement(element: HorizonElement): VNode { } // 直接更新子节点属性即可,不需要diff -export function onlyUpdateChildVNodes(processing: VNode): Array | null { +export function onlyUpdateChildVNodes(processing: VNode): VNode | null { // 检查子树是否需要更新 if (processing.childShouldUpdate) { // 此vNode无需更新,但是子树需要 - if (!processing.isCreated && processing.children && processing.children.length) { + if (!processing.isCreated && processing.child !== null) { // 更新子节点 - processing.children.forEach(child => { + let child = processing.child; + while (child !== null) { updateVNode(child, child.props); updateVNodePath(child); - }); + child = child.next; + } } // 返回子节点,继续遍历 - return processing.children; + return processing.child; } // 子树无需工作 diff --git a/libs/horizon/src/renderer/vnode/VNodeShouldUpdate.ts b/libs/horizon/src/renderer/vnode/VNodeShouldUpdate.ts index 00ebe158..d0f27198 100644 --- a/libs/horizon/src/renderer/vnode/VNodeShouldUpdate.ts +++ b/libs/horizon/src/renderer/vnode/VNodeShouldUpdate.ts @@ -25,15 +25,15 @@ export function updateShouldUpdateOfTree(vNode: VNode): VNode | null { // 设置节点的childShouldUpdate export function updateChildShouldUpdate(vNode: VNode) { - const children = vNode.children || []; - - for (let i = 0; i < children.length; i++) { - const child = children[i]; - if (child && (child.shouldUpdate || child.childShouldUpdate)) { + let child = vNode.child; + while (child !== null) { + if (child.shouldUpdate || child.childShouldUpdate) { vNode.childShouldUpdate = true; return; } + child = child.next; } + vNode.childShouldUpdate = false; } diff --git a/libs/horizon/src/renderer/vnode/VNodeUtils.ts b/libs/horizon/src/renderer/vnode/VNodeUtils.ts index 3b921738..0183dd67 100644 --- a/libs/horizon/src/renderer/vnode/VNodeUtils.ts +++ b/libs/horizon/src/renderer/vnode/VNodeUtils.ts @@ -9,16 +9,21 @@ import {isComment} from '../../dom/utils/Common'; import {getNearestVNode} from '../../dom/DOMInternalKeys'; export function getSiblingVNode(node) { - let siblingVNode = null; - const index = node.cIndex; - if (node && node.parent && node.parent.children && node.parent.children.length > index + 1) { - siblingVNode = node.parent.children[index + 1]; - } - return siblingVNode; + return node.next; } -export function getFirstChild(vNode: VNode) { - return (vNode.children && vNode.children.length) ? vNode.children[0] : null; +export function travelChildren(beginVNode: VNode, handleVNode: Function, isFinish?: Function) { + let node = beginVNode; + + while (node !== null) { + if (isFinish && isFinish(node)) { + return; + } + + handleVNode(node); + + node = node.next; + } } // 从beginVNode开始深度遍历vNode树,对每个vNode调用handleVNode方法 @@ -40,7 +45,7 @@ export function travelVNodeTree( } // 找子节点 - const childVNode = getFirstChild(node); + const childVNode = node.child; if (childVNode !== null && !childFilter(node)) { childVNode.parent = node; node = childVNode; @@ -76,7 +81,8 @@ export function clearVNode(vNode: VNode) { } function clearOneVNode(vNode: VNode) { - vNode.children = []; + vNode.child = null; + vNode.next = null; vNode.depContexts = []; vNode.dirtyNodes = []; vNode.oldProps = null; @@ -95,7 +101,7 @@ function clearOneVNode(vNode: VNode) { vNode.oldRef = null; vNode.suspenseChildThrow = false; vNode.oldSuspenseChildStatus = ''; - vNode.oldChildren = null; + vNode.oldChild = null; vNode.path = []; vNode.toUpdateNodes = null; @@ -193,10 +199,10 @@ export function getSiblingDom(vNode: VNode): Element | null { } // 没有子节点,或是DomPortal - if (!node.children || !node.children.length || node.tag === DomPortal) { + if (!node.child || node.tag === DomPortal) { continue findSibling; } else { - const childVNode = getFirstChild(node); + const childVNode = node.child; childVNode.parent = node; node = childVNode; } @@ -274,5 +280,3 @@ export function getExactNode(targetVNode, targetContainer) { } return targetVNode; } - -