inula/libs/horizon/src/renderer/vnode/VNodeCreator.ts

205 lines
5.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import type { VNodeTag } from './VNodeTags';
import { FlagUtils } from './VNodeFlags';
import {
ClassComponent,
ContextConsumer,
ContextProvider,
ForwardRef,
Fragment,
FunctionComponent,
DomComponent,
DomPortal,
TreeRoot,
DomText,
ClsOrFunComponent,
LazyComponent,
MemoComponent,
SuspenseComponent,
} from './VNodeTags';
import { createUpdateArray } from '../UpdateHandler';
import {
TYPE_CONTEXT,
TYPE_FORWARD_REF, TYPE_FRAGMENT,
TYPE_LAZY,
TYPE_MEMO, TYPE_PROFILER,
TYPE_PROVIDER, TYPE_STRICT_MODE,
TYPE_SUSPENSE,
} from '../../external/JSXElementType';
import { VNode } from './VNode';
import {JSXElement} from '../Types';
const typeLazyMap = {
[TYPE_FORWARD_REF]: ForwardRef,
[TYPE_MEMO]: MemoComponent,
};
const typeMap = {
...typeLazyMap,
[TYPE_PROVIDER]: ContextProvider,
[TYPE_CONTEXT]: ContextConsumer,
[TYPE_LAZY]: LazyComponent,
};
const newVirtualNode = function(tag: VNodeTag, key?: null | string, vNodeProps?: any, outerDom?: any): VNode {
return new VNode(tag, vNodeProps, key, outerDom);
};
function isClassComponent(comp: Function) {
// 如果使用 getPrototypeOf 方法获取构造函数,不能兼容业务组组件继承组件的使用方式,会误认为是函数组件
// 如果使用静态属性,部分函数高阶组件会将类组件的静态属性复制到自身,导致误判为类组件
// 既然已经兼容使用了该标识符,那么继续使用
return comp.prototype?.isReactComponent === true;
}
// 解析懒组件的tag
export function getLazyVNodeTag(lazyComp: any): string {
let vNodeTag = ClsOrFunComponent;
if (typeof lazyComp === 'function') {
vNodeTag = isClassComponent(lazyComp) ? ClassComponent : FunctionComponent;
} else if (lazyComp !== undefined && lazyComp !== null && typeLazyMap[lazyComp.vtype]) {
vNodeTag = typeLazyMap[lazyComp.vtype];
}
return vNodeTag;
}
// 创建processing
export function updateVNode(vNode: VNode, vNodeProps?: any): VNode {
if (vNode.tag === ClassComponent) {
vNode.oldState = vNode.state;
}
if (vNode.tag === SuspenseComponent) {
vNode.oldSuspenseChildStatus = vNode.suspenseChildStatus;
vNode.oldChild = vNode.child;
}
vNode.oldProps = vNode.props;
vNode.props = vNodeProps;
vNode.oldRef = vNode.ref;
FlagUtils.setNoFlags(vNode);
vNode.dirtyNodes = null;
vNode.isCreated = false;
return vNode;
}
function getVNodeTag(type: any) {
let vNodeTag = ClsOrFunComponent;
let isLazy = false;
const componentType = typeof type;
if (componentType === 'function') {
if (isClassComponent(type)) {
vNodeTag = ClassComponent;
}
} else if (componentType === 'string') {
vNodeTag = DomComponent;
} else if (type === TYPE_SUSPENSE) {
vNodeTag = SuspenseComponent;
} else if (componentType === 'object' && type !== null && typeMap[type.vtype]) {
vNodeTag = typeMap[type.vtype];
isLazy = type.vtype === TYPE_LAZY;
} else {
throw Error(`Component type is invalid, got: ${type == null ? type : componentType}`);
}
return { vNodeTag, isLazy };
}
export function createFragmentVNode(fragmentKey, fragmentProps) {
const vNode = newVirtualNode(Fragment, fragmentKey, fragmentProps);
vNode.shouldUpdate = true;
return vNode;
}
export function createDomTextVNode(content) {
const vNode = newVirtualNode(DomText, null, content);
vNode.shouldUpdate = true;
return vNode;
}
export function createPortalVNode(portal) {
const children = portal.children ?? [];
const vNode = newVirtualNode(DomPortal, portal.key, children);
vNode.shouldUpdate = true;
vNode.outerDom = portal.outerDom;
return vNode;
}
export function createUndeterminedVNode(type, key, props) {
const { vNodeTag, isLazy } = getVNodeTag(type);
const vNode = newVirtualNode(vNodeTag, key, props);
vNode.type = type;
vNode.shouldUpdate = true;
if (isLazy) {
vNode.isLazyComponent = isLazy;
vNode.lazyType = type;
}
return vNode;
}
export function createTreeRootVNode(container) {
const vNode = newVirtualNode(TreeRoot, null, null, container);
vNode.path += 0;
createUpdateArray(vNode);
return vNode;
}
// TODO: 暂时保留给测试用例使用,后续修改测试用例
export function createVNode(tag: VNodeTag | string, ...secondArg) {
let vNode = null;
switch (tag) {
case TreeRoot:
// 创建treeRoot
vNode = newVirtualNode(TreeRoot, null, null, secondArg[0]);
vNode.path += 0;
createUpdateArray(vNode);
break;
}
return vNode;
}
export function updateVNodePath(vNode: VNode) {
vNode.path = vNode.parent.path + vNode.cIndex;
}
export function createVNodeFromElement(element: JSXElement): VNode {
const type = element.type;
const key = element.key;
const props = element.props;
if (type === TYPE_STRICT_MODE || type === TYPE_FRAGMENT || type === TYPE_PROFILER) {
return createFragmentVNode(key, props.children);
} else {
return createUndeterminedVNode(type, key, props);
}
}
// 直接更新子节点属性即可不需要diff
export function onlyUpdateChildVNodes(processing: VNode): VNode | null {
// 检查子树是否需要更新
if (processing.childShouldUpdate) {
// 此vNode无需更新但是子树需要
if (!processing.isCreated && processing.child !== null) {
// 更新子节点
let child: VNode | null = processing.child;
while (child !== null) {
updateVNode(child, child.props);
updateVNodePath(child);
child = child.next;
}
}
// 返回子节点,继续遍历
return processing.child;
}
// 子树无需工作
return null;
}