diff --git a/libs/horizon/src/external/ChildrenUtil.ts b/libs/horizon/src/external/ChildrenUtil.ts index d46e217b..c1b681ad 100644 --- a/libs/horizon/src/external/ChildrenUtil.ts +++ b/libs/horizon/src/external/ChildrenUtil.ts @@ -12,45 +12,6 @@ function getItemKey(item: any, index: number): string { return '.' + index.toString(36); } -function mapChildrenToArray( - children: any, - arr: Array, - prefix: string, - callback?: Function, -): number | void { - const type = typeof children; - switch (type) { - // 继承原有规格,undefined和boolean类型按照null处理 - case 'undefined': - case 'boolean': - callMapFun(null, arr, prefix, callback); - return; - case 'number': - case 'string': - callMapFun(children, arr, prefix, callback); - return; - case 'object': - if (children === null) { - callMapFun(null, arr, prefix, callback); - return; - } - const vtype = children.vtype; - if (vtype === TYPE_ELEMENT || vtype === TYPE_PORTAL) { - callMapFun(children, arr, prefix, callback) ; - return; - } - if (Array.isArray(children)) { - processArrayChildren(children, arr, prefix, callback); - return; - } - throw new Error( - 'Object is invalid as a Horizon child. ' - ); - - // No Default - } -} - function processArrayChildren( children: any, arr: Array, @@ -100,6 +61,44 @@ function callMapFun( } } +function mapChildrenToArray( + children: any, + arr: Array, + prefix: string, + callback?: Function, +): number | void { + const type = typeof children; + switch (type) { + // 继承原有规格,undefined和boolean类型按照null处理 + case 'undefined': + case 'boolean': + callMapFun(null, arr, prefix, callback); + return; + case 'number': + case 'string': + callMapFun(children, arr, prefix, callback); + return; + case 'object': + if (children === null) { + callMapFun(null, arr, prefix, callback); + return; + } + const vtype = children.vtype; + if (vtype === TYPE_ELEMENT || vtype === TYPE_PORTAL) { + callMapFun(children, arr, prefix, callback) ; + return; + } + if (Array.isArray(children)) { + processArrayChildren(children, arr, prefix, callback); + return; + } + throw new Error( + 'Object is invalid as a Horizon child. ' + ); + // No Default + } +} + // 在 children 里的每个直接子节点上调用一个函数,并将 this 设置为 thisArg function mapChildren( children: any, @@ -111,9 +110,7 @@ function mapChildren( } let count = 0; const result = []; - mapChildrenToArray(children, result, '', (child) => { - return func.call(context, child, count++); - }); + mapChildrenToArray(children, result, '', child => func.call(context, child, count++)); return result; } diff --git a/libs/horizon/src/external/HorizonElement.ts b/libs/horizon/src/external/HorizonElement.ts index 186f343c..c315bd6a 100644 --- a/libs/horizon/src/external/HorizonElement.ts +++ b/libs/horizon/src/external/HorizonElement.ts @@ -23,12 +23,20 @@ export function HorizonElement(type, key, ref, vNode, props) { // 记录负责创建此元素的组件。 _vNode: vNode, }; -}; +} function isValidKey(key) { return key !== 'key' && key !== 'ref' && key !== '__source'; } +function mergeDefault(sourceObj, defaultObj) { + Object.keys(defaultObj).forEach((key) => { + if (sourceObj[key] === undefined) { + sourceObj[key] = defaultObj[key]; + } + }); +} + function buildElement(isClone, type, setting, ...children) { // setting中的值优先级最高,clone情况下从 type 中取值,创建情况下直接赋值为 null const key = (setting && setting.key !== undefined) ? String(setting.key) : (isClone ? type.key : null); @@ -64,14 +72,6 @@ export function createElement(type, setting, ...children) { return buildElement(false, type, setting, ...children); } -function mergeDefault(sourceObj, defaultObj) { - Object.keys(defaultObj).forEach((key) => { - if (sourceObj[key] === undefined) { - sourceObj[key] = defaultObj[key]; - } - }); -} - export function cloneElement(element, setting, ...children) { return buildElement(true, element, setting, ...children); } diff --git a/libs/horizon/src/renderer/Renderer.ts b/libs/horizon/src/renderer/Renderer.ts index ebf8da2b..32c3cc0f 100644 --- a/libs/horizon/src/renderer/Renderer.ts +++ b/libs/horizon/src/renderer/Renderer.ts @@ -37,7 +37,7 @@ export function startUpdate( launchUpdateFromVNode(treeRoot); } -export function getFirstCustomDom(treeRoot: VNode): Element | Text | null { +export function getFirstCustomDom(treeRoot: VNode | undefined | null): Element | Text | null { if (treeRoot?.child) { return treeRoot.child.realNode; } diff --git a/libs/horizon/src/renderer/render/BaseComponent.ts b/libs/horizon/src/renderer/render/BaseComponent.ts index b045b215..8e5e56d4 100644 --- a/libs/horizon/src/renderer/render/BaseComponent.ts +++ b/libs/horizon/src/renderer/render/BaseComponent.ts @@ -15,6 +15,32 @@ import { createChildrenByDiff } from '../diff/nodeDiffComparator'; import {onlyUpdateChildVNodes} from '../vnode/VNodeCreator'; import componentRenders from './index'; +// 复用vNode时,也需对stack进行处理 +function handlerContext(processing: VNode) { + switch (processing.tag) { + case TreeRoot: + setNamespaceCtx(processing, processing.outerDom); + break; + case DomComponent: + setNamespaceCtx(processing); + break; + case ClassComponent: { + const isOldCxtExist = isOldProvider(processing.type); + cacheOldCtx(processing, isOldCxtExist); + break; + } + case DomPortal: + setNamespaceCtx(processing, processing.outerDom); + break; + case ContextProvider: { + const newValue = processing.props.value; + setContextCtx(processing, newValue); + break; + } + // No Default + } +} + export function captureVNode(processing: VNode): VNode | null { const component = componentRenders[processing.tag]; @@ -38,31 +64,6 @@ export function captureVNode(processing: VNode): VNode | null { return component.captureRender(processing, shouldUpdate); } -// 复用vNode时,也需对stack进行处理 -function handlerContext(processing: VNode) { - switch (processing.tag) { - case TreeRoot: - setNamespaceCtx(processing, processing.outerDom); - break; - case DomComponent: - setNamespaceCtx(processing); - break; - case ClassComponent: { - const isOldCxtExist = isOldProvider(processing.type); - cacheOldCtx(processing, isOldCxtExist); - break; - } - case DomPortal: - setNamespaceCtx(processing, processing.outerDom); - break; - case ContextProvider: { - const newValue = processing.props.value; - setContextCtx(processing, newValue); - break; - } - } -} - // 创建孩子节点 export function createVNodeChildren(processing: VNode, nextChildren: any) { const isComparing = !processing.isCreated; diff --git a/libs/horizon/src/renderer/render/ClassComponent.ts b/libs/horizon/src/renderer/render/ClassComponent.ts index 72ff9224..2be13d49 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 } from '../vnode/VNodeFlags'; import { createVNodeChildren, markRef } from './BaseComponent'; import { createUpdateArray, @@ -28,25 +28,80 @@ import { } from '../UpdateHandler'; import { getContextChangeCtx, setContextChangeCtx } from '../ContextSaver'; import ProcessingVNode from '../vnode/ProcessingVNode'; -import {onlyUpdateChildVNodes} from '../vnode/VNodeCreator'; +import { onlyUpdateChildVNodes } from '../vnode/VNodeCreator'; -export function captureRender(processing: VNode): VNode | null { - const clazz = processing.type; - const props = processing.props; - const nextProps = processing.isLazyComponent ? mergeDefaultProps(clazz, props) : props; - return captureClassComponent(processing, clazz, nextProps); +// 获取当前节点的context +export function getCurrentContext(clazz, processing: VNode) { + const context = clazz.contextType; + return typeof context === 'object' && context !== null + ? getNewContext(processing, context) + : getOldContext(processing, clazz, true); } -export function bubbleRender(processing: VNode) { - if (isOldProvider(processing.type)) { - resetOldCtx(processing); +// 挂载实例 +function mountInstance(clazz, processing: VNode, nextProps: object) { + if (!processing.isCreated) { + processing.isCreated = true; + FlagUtils.markAddition(processing); + } + + // 构造实例 + callConstructor(processing, clazz, nextProps); + + const inst = processing.realNode; + inst.props = nextProps; + inst.state = processing.state; + inst.context = getCurrentContext(clazz, processing); + inst.refs = {}; + + createUpdateArray(processing); + processUpdates(processing, inst, nextProps); + inst.state = processing.state; + + // 在调用类组建的渲染方法之前调用 并且在初始挂载及后续更新时都会被调用 + callDerivedStateFromProps(processing, clazz.getDerivedStateFromProps, nextProps); + callComponentWillMount(processing, inst, nextProps); + + markComponentDidMount(processing); +} + +// 构建子节点 +function createChildren(clazz: any, processing: VNode) { + markRef(processing); + + ProcessingVNode.val = processing; + processing.state = processing.realNode.state; + + const inst = processing.realNode; + const isCatchError = processing.flags.DidCapture; + + // 按照已有规格,如果捕获了错误却没有定义getDerivedStateFromError函数,返回的child为null + const newElements = isCatchError && typeof clazz.getDerivedStateFromError !== 'function' + ? null + : inst.render(); + + processing.child = createVNodeChildren(processing, newElements); + return processing.child; +} + +// 根据isUpdateComponent,执行不同的生命周期 +function callUpdateLifeCycle(processing: VNode, nextProps: object, clazz) { + const inst = processing.realNode; + const newContext = getCurrentContext(clazz, processing); + if (processing.isCreated) { + callComponentWillMount(processing, inst); + } else { + callComponentWillUpdate(inst, nextProps, processing.state, newContext); } } -// 用于未完成的类组件 -export function getIncompleteClassComponent(clazz, processing: VNode, nextProps: object):VNode | null { - mountInstance(clazz, processing, nextProps); - return createChildren(clazz, processing); +function markLifeCycle(processing: VNode, nextProps: object, shouldUpdate: Boolean) { + if (processing.isCreated) { + markComponentDidMount(processing); + } else if (processing.state !== processing.oldState || shouldUpdate) { + markComponentDidUpdate(processing); + markGetSnapshotBeforeUpdate(processing); + } } // 用于类组件 @@ -127,76 +182,21 @@ export function captureClassComponent(processing: VNode, clazz: any, nextProps: } } -// 挂载实例 -function mountInstance(clazz, processing: VNode, nextProps: object) { - if (!processing.isCreated) { - processing.isCreated = true; - FlagUtils.markAddition(processing); - } - - // 构造实例 - callConstructor(processing, clazz, nextProps); - - const inst = processing.realNode; - inst.props = nextProps; - inst.state = processing.state; - inst.context = getCurrentContext(clazz, processing); - inst.refs = {}; - - createUpdateArray(processing); - processUpdates(processing, inst, nextProps); - inst.state = processing.state; - - // 在调用类组建的渲染方法之前调用 并且在初始挂载及后续更新时都会被调用 - callDerivedStateFromProps(processing, clazz.getDerivedStateFromProps, nextProps); - callComponentWillMount(processing, inst, nextProps); - - markComponentDidMount(processing); +export function captureRender(processing: VNode): VNode | null { + const clazz = processing.type; + const props = processing.props; + const nextProps = processing.isLazyComponent ? mergeDefaultProps(clazz, props) : props; + return captureClassComponent(processing, clazz, nextProps); } -// 构建子节点 -function createChildren(clazz: any, processing: VNode) { - markRef(processing); - - ProcessingVNode.val = processing; - processing.state = processing.realNode.state; - - const inst = processing.realNode; - const isCatchError = processing.flags.DidCapture; - - // 按照已有规格,如果捕获了错误却没有定义getDerivedStateFromError函数,返回的child为null - const newElements = (isCatchError && typeof clazz.getDerivedStateFromError !== 'function') - ? null - : inst.render(); - - processing.child = createVNodeChildren(processing, newElements); - return processing.child; -} - -// 获取当前节点的context -export function getCurrentContext(clazz, processing: VNode) { - const context = clazz.contextType; - return typeof context === 'object' && context !== null - ? getNewContext(processing, context) - : getOldContext(processing, clazz, true); -} - -// 根据isUpdateComponent,执行不同的生命周期 -function callUpdateLifeCycle(processing: VNode, nextProps: object, clazz) { - const inst = processing.realNode; - const newContext = getCurrentContext(clazz, processing); - if (processing.isCreated) { - callComponentWillMount(processing, inst); - } else { - callComponentWillUpdate(inst, nextProps, processing.state, newContext); +export function bubbleRender(processing: VNode) { + if (isOldProvider(processing.type)) { + resetOldCtx(processing); } } -function markLifeCycle(processing: VNode, nextProps: object, shouldUpdate: Boolean) { - if (processing.isCreated) { - markComponentDidMount(processing); - } else if (processing.state !== processing.oldState || shouldUpdate) { - markComponentDidUpdate(processing); - markGetSnapshotBeforeUpdate(processing); - } +// 用于未完成的类组件 +export function getIncompleteClassComponent(clazz, processing: VNode, nextProps: object): VNode | null { + mountInstance(clazz, processing, nextProps); + return createChildren(clazz, processing); } diff --git a/libs/horizon/src/renderer/render/ClsOrFunComponent.ts b/libs/horizon/src/renderer/render/ClsOrFunComponent.ts index 4a2204a1..6eccd85b 100644 --- a/libs/horizon/src/renderer/render/ClsOrFunComponent.ts +++ b/libs/horizon/src/renderer/render/ClsOrFunComponent.ts @@ -7,14 +7,8 @@ import {FlagUtils} from '../vnode/VNodeFlags'; import {exeFunctionHook} from '../hooks/HookMain'; import {createVNodeChildren} from './BaseComponent'; -export function captureRender(processing: VNode): VNode | null { - return captureIndeterminateComponent(processing); -} - -export function bubbleRender() {} - function captureIndeterminateComponent( - processing: VNode | null, + processing: VNode, ): VNode | null { const funcComp = processing.type; @@ -34,3 +28,9 @@ function captureIndeterminateComponent( processing.child = createVNodeChildren(processing, newElements); return processing.child; } + +export function captureRender(processing: VNode): VNode | null { + return captureIndeterminateComponent(processing); +} + +export function bubbleRender() {} diff --git a/libs/horizon/src/renderer/render/ContextConsumer.ts b/libs/horizon/src/renderer/render/ContextConsumer.ts index 7fcbd59c..c45934db 100644 --- a/libs/horizon/src/renderer/render/ContextConsumer.ts +++ b/libs/horizon/src/renderer/render/ContextConsumer.ts @@ -3,12 +3,6 @@ import type {VNode, ContextType} from '../Types'; import {resetDepContexts, getNewContext} from '../components/context/Context'; import {createVNodeChildren} from './BaseComponent'; -export function captureRender(processing: VNode): VNode | null { - return captureContextConsumer(processing); -} - -export function bubbleRender() {} - function captureContextConsumer(processing: VNode) { const context: ContextType = processing.type; const props = processing.props; @@ -21,3 +15,10 @@ function captureContextConsumer(processing: VNode) { processing.child = createVNodeChildren(processing, newChildren); return processing.child; } + +export function captureRender(processing: VNode): VNode | null { + return captureContextConsumer(processing); +} + +export function bubbleRender() {} + diff --git a/libs/horizon/src/renderer/render/ContextProvider.ts b/libs/horizon/src/renderer/render/ContextProvider.ts index c0ef07e6..c60156c7 100644 --- a/libs/horizon/src/renderer/render/ContextProvider.ts +++ b/libs/horizon/src/renderer/render/ContextProvider.ts @@ -14,14 +14,6 @@ import {launchUpdateFromVNode} from '../TreeBuilder'; import {onlyUpdateChildVNodes} from '../vnode/VNodeCreator'; import {setParentsChildShouldUpdate} from '../vnode/VNodeShouldUpdate'; -export function captureRender(processing: VNode): VNode | null { - return captureContextProvider(processing); -} - -export function bubbleRender(processing: VNode) { - resetContextCtx(processing); -} - function captureContextProvider(processing: VNode): VNode | null { const providerType: ProviderType = processing.type; const contextType: ContextType = providerType._context; @@ -54,6 +46,14 @@ function captureContextProvider(processing: VNode): VNode | null { return processing.child; } +export function captureRender(processing: VNode): VNode | null { + return captureContextProvider(processing); +} + +export function bubbleRender(processing: VNode) { + resetContextCtx(processing); +} + // 从依赖中找到匹配context的VNode function matchDependencies(depContexts, context, vNode): boolean { for (let i = 0; i < depContexts.length; i++) {