Match-id-4c019f9f331b82122ea9f0b46fd85c96b7883297

This commit is contained in:
* 2022-04-01 16:19:51 +08:00 committed by *
commit 4cbebd43cd
17 changed files with 148 additions and 233 deletions

View File

@ -1,6 +1,6 @@
/** /**
* context * context
* begin阶段会修改一些全局的值complete阶段会恢复 * capture阶段会修改一些全局的值bubble阶段会恢复
*/ */
import type { VNode, ContextType } from './Types'; import type { VNode, ContextType } from './Types';
@ -11,98 +11,50 @@ import { ContextProvider } from './vnode/VNodeTags';
// 保存的是“http://www.w3.org/1999/xhtml”或“http://www.w3.org/2000/svg” // 保存的是“http://www.w3.org/1999/xhtml”或“http://www.w3.org/2000/svg”
// 用于识别是使用document.createElement()还是使用document.createElementNS()创建DOM // 用于识别是使用document.createElement()还是使用document.createElementNS()创建DOM
const CTX_NAMESPACE = 'CTX_NAMESPACE';
// 保存的是Horizon.createContext()的值或Provider重新设置的值
const CTX_CONTEXT = 'CTX_CONTEXT';
// 旧版context API,是否更改。
const CTX_OLD_CHANGE = 'CTX_OLD_CHANGE';
let ctxOldChange = false;
let ctxNamespace = ''; let ctxNamespace = '';
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阶段设置 // capture阶段设置
function setNamespaceCtx(vNode: VNode, dom?: Container) { export function setNamespaceCtx(vNode: VNode, dom?: Container) {
const nextContext = getNSCtx(ctxNamespace, vNode.type, dom); const nextContext = getNSCtx(ctxNamespace, vNode.type, dom);
setContext(vNode, CTX_NAMESPACE, ctxNamespace); vNode.context = ctxNamespace;
ctxNamespace = nextContext; ctxNamespace = nextContext;
} }
// bubble阶段恢复 // bubble阶段恢复
function resetNamespaceCtx(vNode: VNode) { export function resetNamespaceCtx(vNode: VNode) {
ctxNamespace = getContext(vNode, CTX_NAMESPACE); ctxNamespace = vNode.context;
} }
function getNamespaceCtx(): string { export function getNamespaceCtx(): string {
return ctxNamespace; return ctxNamespace;
} }
function setContextCtx<T>(providerVNode: VNode, nextValue: T) { export function setContext<T>(providerVNode: VNode, nextValue: T) {
const context: ContextType<T> = providerVNode.type._context; const context: ContextType<T> = providerVNode.type._context;
setContext(providerVNode, CTX_CONTEXT, context.value); providerVNode.context = context.value;
context.value = nextValue; context.value = nextValue;
} }
function resetContextCtx(providerVNode: VNode) { export function resetContext(providerVNode: VNode) {
const context: ContextType<any> = providerVNode.type._context; const context: ContextType<any> = providerVNode.type._context;
context.value = getContext(providerVNode, CTX_CONTEXT); context.value = providerVNode.context;
} }
// 在局部更新时恢复父节点的context // 在局部更新时恢复父节点的context
function recoverParentsContextCtx(vNode: VNode) { export function recoverParentContext(vNode: VNode) {
let parent = vNode.parent; let parent = vNode.parent;
while (parent !== null) { while (parent !== null) {
if (parent.tag === ContextProvider) { if (parent.tag === ContextProvider) {
const newValue = parent.props.value; parent.context = parent.props.value;
setContextCtx(parent, newValue);
} }
parent = parent.parent; parent = parent.parent;
} }
} }
function setContextChangeCtx(providerVNode: VNode, didChange: boolean) {
setContext(providerVNode, CTX_OLD_CHANGE, didChange);
ctxOldChange = didChange;
}
function getContextChangeCtx() {
return ctxOldChange;
}
function resetContextChangeCtx(vNode: VNode) {
ctxOldChange = getContext(vNode, CTX_OLD_CHANGE);
}
export {
getNamespaceCtx,
resetNamespaceCtx,
setNamespaceCtx,
setContextCtx,
resetContextCtx,
recoverParentsContextCtx,
setContextChangeCtx,
getContextChangeCtx,
resetContextChangeCtx,
};

View File

@ -2,7 +2,7 @@
* *
*/ */
import type {VNode} from './Types'; import type { PromiseType, VNode } from './Types';
import type {Update} from './UpdateHandler'; import type {Update} from './UpdateHandler';
import {ClassComponent, TreeRoot} from './vnode/VNodeTags'; import {ClassComponent, TreeRoot} from './vnode/VNodeTags';
@ -62,7 +62,9 @@ function createClassErrorUpdate(
} }
return update; return update;
} }
function isPromise(error: any): error is PromiseType<any> {
return error !== null && typeof error === 'object' && typeof error.then === 'function'
}
// 处理capture和bubble阶段抛出的错误 // 处理capture和bubble阶段抛出的错误
export function handleRenderThrowError( export function handleRenderThrowError(
sourceVNode: VNode, sourceVNode: VNode,
@ -74,7 +76,7 @@ export function handleRenderThrowError(
sourceVNode.dirtyNodes = null; sourceVNode.dirtyNodes = null;
// error是个promise // error是个promise
if (error !== null && typeof error === 'object' && typeof error.then === 'function') { if (isPromise(error)) {
// 抛出异常的节点向上寻找是否有suspense组件 // 抛出异常的节点向上寻找是否有suspense组件
const foundSuspense = handleSuspenseChildThrowError(sourceVNode.parent, sourceVNode, error); const foundSuspense = handleSuspenseChildThrowError(sourceVNode.parent, sourceVNode, error);
if (foundSuspense) { if (foundSuspense) {

View File

@ -29,7 +29,7 @@ import {
isExecuting, isExecuting,
setExecuteMode setExecuteMode
} from './ExecuteMode'; } from './ExecuteMode';
import { recoverParentsContextCtx, resetNamespaceCtx, setNamespaceCtx } from './ContextSaver'; import { recoverParentContext, resetNamespaceCtx, setNamespaceCtx } from './ContextSaver';
import { import {
updateChildShouldUpdate, updateChildShouldUpdate,
updateParentsChildShouldUpdate, updateParentsChildShouldUpdate,
@ -231,7 +231,7 @@ function buildVNodeTree(treeRoot: VNode) {
} }
// 恢复父节点的context // 恢复父节点的context
recoverParentsContextCtx(startVNode); recoverParentContext(startVNode);
} }
// 重置环境变量,为重新进行深度遍历做准备 // 重置环境变量,为重新进行深度遍历做准备

View File

@ -54,3 +54,10 @@ export interface PromiseType<R> {
): void | PromiseType<U>; ): void | PromiseType<U>;
} }
export interface SuspenseState {
promiseSet: Set<PromiseType<any>> | null; // suspense组件的promise列表
childStatus: string;
oldChildStatus: string; // 上一次Suspense的Children是否显示
didCapture: boolean; // suspense是否捕获了异常
promiseResolved: boolean; // suspense的promise是否resolve
}

View File

@ -9,7 +9,7 @@ import {
import {HookStage, setHookStage} from './HookStage'; import {HookStage, setHookStage} from './HookStage';
// hook对外入口 // hook对外入口
export function exeFunctionHook<Props extends Record<string, any>, Arg>( export function runFunctionWithHooks<Props extends Record<string, any>, Arg>(
funcComp: (props: Props, arg: Arg) => any, funcComp: (props: Props, arg: Arg) => any,
props: Props, props: Props,
arg: Arg, arg: Arg,

View File

@ -7,7 +7,7 @@ import {
TreeRoot, TreeRoot,
SuspenseComponent, SuspenseComponent,
} from '../vnode/VNodeTags'; } from '../vnode/VNodeTags';
import { getContextChangeCtx, setContextCtx, setNamespaceCtx } from '../ContextSaver'; import { setContext, setNamespaceCtx } from '../ContextSaver';
import { FlagUtils } from '../vnode/VNodeFlags'; import { FlagUtils } from '../vnode/VNodeFlags';
import {onlyUpdateChildVNodes} from '../vnode/VNodeCreator'; import {onlyUpdateChildVNodes} from '../vnode/VNodeCreator';
import componentRenders from './index'; import componentRenders from './index';
@ -26,7 +26,7 @@ function handlerContext(processing: VNode) {
break; break;
case ContextProvider: { case ContextProvider: {
const newValue = processing.props.value; const newValue = processing.props.value;
setContextCtx(processing, newValue); setContext(processing, newValue);
break; break;
} }
// No Default // No Default
@ -41,7 +41,6 @@ export function captureVNode(processing: VNode): VNode | null {
if ( if (
!processing.isCreated && !processing.isCreated &&
processing.oldProps === processing.props && processing.oldProps === processing.props &&
!getContextChangeCtx() &&
!processing.shouldUpdate !processing.shouldUpdate
) { ) {
// 复用还需对stack进行处理 // 复用还需对stack进行处理

View File

@ -18,7 +18,6 @@ import { markRef } from './BaseComponent';
import { import {
processUpdates, processUpdates,
} from '../UpdateHandler'; } from '../UpdateHandler';
import { getContextChangeCtx } from '../ContextSaver';
import { setProcessingClassVNode } from '../GlobalVar'; import { setProcessingClassVNode } from '../GlobalVar';
import { onlyUpdateChildVNodes } from '../vnode/VNodeCreator'; import { onlyUpdateChildVNodes } from '../vnode/VNodeCreator';
import { createChildrenByDiff } from '../diff/nodeDiffComparator'; import { createChildrenByDiff } from '../diff/nodeDiffComparator';
@ -33,25 +32,25 @@ export function getCurrentContext(clazz, processing: VNode) {
} }
// 挂载实例 // 挂载实例
function mountInstance(clazz, processing: VNode, nextProps: object) { function mountInstance(ctor, processing: VNode, nextProps: object) {
if (!processing.isCreated) { if (!processing.isCreated) {
processing.isCreated = true; processing.isCreated = true;
FlagUtils.markAddition(processing); FlagUtils.markAddition(processing);
} }
// 构造实例 // 构造实例
const inst = callConstructor(processing, clazz, nextProps); const inst = callConstructor(processing, ctor, nextProps);
inst.props = nextProps; inst.props = nextProps;
inst.state = processing.state; inst.state = processing.state;
inst.context = getCurrentContext(clazz, processing); inst.context = getCurrentContext(ctor, processing);
inst.refs = {}; inst.refs = {};
processUpdates(processing, inst, nextProps); processUpdates(processing, inst, nextProps);
inst.state = processing.state; inst.state = processing.state;
// 在调用类组建的渲染方法之前调用 并且在初始挂载及后续更新时都会被调用 // 在调用类组建的渲染方法之前调用 并且在初始挂载及后续更新时都会被调用
callDerivedStateFromProps(processing, clazz.getDerivedStateFromProps, nextProps); callDerivedStateFromProps(processing, ctor.getDerivedStateFromProps, nextProps);
callComponentWillMount(processing, inst, nextProps); callComponentWillMount(processing, inst, nextProps);
markComponentDidMount(processing); markComponentDidMount(processing);
@ -87,7 +86,7 @@ function callUpdateLifeCycle(processing: VNode, nextProps: object, clazz) {
} }
} }
function markLifeCycle(processing: VNode, nextProps: object, shouldUpdate: Boolean) { function markLifeCycle(processing: VNode, nextProps: object, shouldUpdate: boolean) {
if (processing.isCreated) { if (processing.isCreated) {
markComponentDidMount(processing); markComponentDidMount(processing);
} else if (processing.state !== processing.oldState || shouldUpdate) { } else if (processing.state !== processing.oldState || shouldUpdate) {
@ -98,26 +97,30 @@ function markLifeCycle(processing: VNode, nextProps: object, shouldUpdate: Boole
// 用于类组件 // 用于类组件
export function captureRender(processing: VNode): VNode | null { export function captureRender(processing: VNode): VNode | null {
let clazz = processing.type; const ctor = processing.type;
let nextProps = processing.props; let nextProps = processing.props;
if (processing.isLazyComponent) { if (processing.isLazyComponent) {
nextProps = mergeDefaultProps(clazz, nextProps); nextProps = mergeDefaultProps(ctor, nextProps);
if (processing.promiseResolve) { // 该函数被 lazy 组件使用,未加载的组件需要加载组件的真实内容
clazz = clazz._load(clazz._content);
}
} }
resetDepContexts(processing); resetDepContexts(processing);
// suspense打断后再次render只需初次渲染
if (processing.isSuspended) {
mountInstance(ctor, processing, nextProps);
processing.isSuspended = false;
return createChildren(ctor, processing);
}
// 通过 shouldUpdate 判断是否要复用 children该值和props,state,context的变化shouldComponentUpdate,forceUpdate api的调用结果有关 // 通过 shouldUpdate 判断是否要复用 children该值和props,state,context的变化shouldComponentUpdate,forceUpdate api的调用结果有关
let shouldUpdate; let shouldUpdate;
const inst = processing.realNode; const inst = processing.realNode;
if (inst === null) { if (inst === null) {
// 挂载新组件,一定会更新 // 挂载新组件,一定会更新
mountInstance(clazz, processing, nextProps); mountInstance(ctor, processing, nextProps);
shouldUpdate = true; shouldUpdate = true;
} else { // 更新 } else { // 更新
const newContext = getCurrentContext(clazz, processing); const newContext = getCurrentContext(ctor, processing);
// 子节点抛出异常时如果本class是个捕获异常的处理节点这时候oldProps是null所以需要使用props // 子节点抛出异常时如果本class是个捕获异常的处理节点这时候oldProps是null所以需要使用props
const oldProps = (processing.flags & DidCapture) === DidCapture ? processing.props : processing.oldProps; const oldProps = (processing.flags & DidCapture) === DidCapture ? processing.props : processing.oldProps;
@ -132,18 +135,17 @@ export function captureRender(processing: VNode): VNode | null {
// 如果 props, state, context 都没有变化且 isForceUpdate 为 false则不需要更新 // 如果 props, state, context 都没有变化且 isForceUpdate 为 false则不需要更新
shouldUpdate = oldProps !== processing.props || shouldUpdate = oldProps !== processing.props ||
inst.state !== processing.state || inst.state !== processing.state ||
getContextChangeCtx() ||
processing.isForceUpdate; processing.isForceUpdate;
if (shouldUpdate) { if (shouldUpdate) {
// derivedStateFromProps会修改nextState因此需要调用 // derivedStateFromProps会修改nextState因此需要调用
callDerivedStateFromProps(processing, clazz.getDerivedStateFromProps, nextProps); callDerivedStateFromProps(processing, ctor.getDerivedStateFromProps, nextProps);
if (!processing.isForceUpdate) { if (!processing.isForceUpdate) {
// 业务可以通过 shouldComponentUpdate 函数进行优化阻止更新 // 业务可以通过 shouldComponentUpdate 函数进行优化阻止更新
shouldUpdate = callShouldComponentUpdate(processing, oldProps, nextProps, processing.state, newContext); shouldUpdate = callShouldComponentUpdate(processing, oldProps, nextProps, processing.state, newContext);
} }
if (shouldUpdate) { if (shouldUpdate) {
callUpdateLifeCycle(processing, nextProps, clazz); callUpdateLifeCycle(processing, nextProps, ctor);
} }
inst.state = processing.state; inst.state = processing.state;
inst.context = newContext; inst.context = newContext;
@ -162,16 +164,10 @@ export function captureRender(processing: VNode): VNode | null {
// 不复用 // 不复用
if (shouldUpdate) { if (shouldUpdate) {
return createChildren(clazz, processing); return createChildren(ctor, processing);
} else { } else {
return onlyUpdateChildVNodes(processing); return onlyUpdateChildVNodes(processing);
} }
} }
export function bubbleRender(processing: VNode) {} export function bubbleRender() {}
// 用于未完成的类组件
export function getIncompleteClassComponent(clazz, processing: VNode, nextProps: object): VNode | null {
mountInstance(clazz, processing, nextProps);
return createChildren(clazz, processing);
}

View File

@ -4,9 +4,8 @@ import { isSame } from '../utils/compare';
import { ClassComponent, ContextProvider } from '../vnode/VNodeTags'; import { ClassComponent, ContextProvider } from '../vnode/VNodeTags';
import { pushForceUpdate } from '../UpdateHandler'; import { pushForceUpdate } from '../UpdateHandler';
import { import {
getContextChangeCtx, resetContext,
resetContextCtx, setContext,
setContextCtx,
} from '../ContextSaver'; } from '../ContextSaver';
import { travelVNodeTree } from '../vnode/VNodeUtils'; import { travelVNodeTree } from '../vnode/VNodeUtils';
import { launchUpdateFromVNode } from '../TreeBuilder'; import { launchUpdateFromVNode } from '../TreeBuilder';
@ -75,14 +74,14 @@ function captureContextProvider(processing: VNode): VNode | null {
const newCtx = newProps.value; const newCtx = newProps.value;
// 更新processing的context值为newProps.value // 更新processing的context值为newProps.value
setContextCtx(processing, newCtx); setContext(processing, newCtx);
if (oldProps !== null) { if (oldProps !== null) {
const oldCtx = oldProps.value; const oldCtx = oldProps.value;
const isSameContext = isSame(oldCtx, newCtx); const isSameContext = isSame(oldCtx, newCtx);
if (isSameContext) { if (isSameContext) {
// context没有改变复用 // context没有改变复用
if (oldProps.children === newProps.children && !getContextChangeCtx()) { if (oldProps.children === newProps.children) {
return onlyUpdateChildVNodes(processing); return onlyUpdateChildVNodes(processing);
} }
} else { } else {
@ -101,6 +100,6 @@ export function captureRender(processing: VNode): VNode | null {
} }
export function bubbleRender(processing: VNode) { export function bubbleRender(processing: VNode) {
resetContextCtx(processing); resetContext(processing);
} }

View File

@ -1,8 +1,8 @@
import type {VNode} from '../Types'; import type {VNode} from '../Types';
import {captureRender as funCaptureRender} from './FunctionComponent'; import {captureRender as funCaptureRender} from './FunctionComponent';
export function captureRender(processing: VNode, shouldUpdate?: boolean): VNode | null { export function captureRender(processing: VNode): VNode | null {
return funCaptureRender(processing, shouldUpdate); return funCaptureRender(processing);
} }
export function bubbleRender() {} export function bubbleRender() {}

View File

@ -2,10 +2,9 @@ import type { VNode } from '../Types';
import { mergeDefaultProps } from './LazyComponent'; import { mergeDefaultProps } from './LazyComponent';
import { resetDepContexts } from '../components/context/Context'; import { resetDepContexts } from '../components/context/Context';
import { exeFunctionHook } from '../hooks/HookMain'; import { runFunctionWithHooks } from '../hooks/HookMain';
import { ForwardRef } from '../vnode/VNodeTags'; import { ForwardRef } from '../vnode/VNodeTags';
import { FlagUtils, Update } from '../vnode/VNodeFlags'; import { FlagUtils, Update } from '../vnode/VNodeFlags';
import { getContextChangeCtx } from '../ContextSaver';
import { onlyUpdateChildVNodes } from '../vnode/VNodeCreator'; import { onlyUpdateChildVNodes } from '../vnode/VNodeCreator';
import { createChildrenByDiff } from '../diff/nodeDiffComparator'; import { createChildrenByDiff } from '../diff/nodeDiffComparator';
@ -16,28 +15,10 @@ export function bubbleRender() {
} }
// 判断children是否可以复用 // 判断children是否可以复用
function checkIfCanReuseChildren(processing: VNode, shouldUpdate?: boolean) { function checkIfCanReuseChildren(processing: VNode) {
let isCanReuse = true; return !processing.isCreated &&
processing.oldProps === processing.props &&
if (!processing.isCreated) { !processing.isDepContextChange;
const oldProps = processing.oldProps;
const newProps = processing.props;
// 如果props或者context改变了
if (oldProps !== newProps || getContextChangeCtx() || processing.isDepContextChange) {
isCanReuse = false;
} else {
if (shouldUpdate && processing.suspenseChildThrow) {
// 使用完后恢复
processing.suspenseChildThrow = false;
isCanReuse = false;
}
}
} else {
isCanReuse = false;
}
return isCanReuse;
} }
export function setStateChange(isUpdate) { export function setStateChange(isUpdate) {
@ -52,7 +33,6 @@ export function captureFunctionComponent(
processing: VNode, processing: VNode,
funcComp: any, funcComp: any,
nextProps: any, nextProps: any,
shouldUpdate?: boolean,
) { ) {
// 函数组件内已完成异步动作 // 函数组件内已完成异步动作
if (processing.isSuspended) { if (processing.isSuspended) {
@ -64,11 +44,11 @@ export function captureFunctionComponent(
} }
resetDepContexts(processing); resetDepContexts(processing);
const isCanReuse = checkIfCanReuseChildren(processing, shouldUpdate); const isCanReuse = checkIfCanReuseChildren(processing);
// 在执行exeFunctionHook前先设置stateChange为false // 在执行exeFunctionHook前先设置stateChange为false
setStateChange(false); setStateChange(false);
const newElements = exeFunctionHook( const newElements = runFunctionWithHooks(
processing.tag === ForwardRef ? funcComp.render : funcComp, processing.tag === ForwardRef ? funcComp.render : funcComp,
nextProps, nextProps,
processing.tag === ForwardRef ? processing.ref : undefined, processing.tag === ForwardRef ? processing.ref : undefined,
@ -88,7 +68,7 @@ export function captureFunctionComponent(
return processing.child; return processing.child;
} }
export function captureRender(processing: VNode, shouldUpdate?: boolean): VNode | null { export function captureRender(processing: VNode): VNode | null {
const Component = processing.type; const Component = processing.type;
const unresolvedProps = processing.props; const unresolvedProps = processing.props;
const resolvedProps = const resolvedProps =
@ -100,7 +80,6 @@ export function captureRender(processing: VNode, shouldUpdate?: boolean): VNode
processing, processing,
Component, Component,
resolvedProps, resolvedProps,
shouldUpdate,
); );
} }

View File

@ -1,27 +0,0 @@
import type {VNode} from '../Types';
import {mergeDefaultProps} from './LazyComponent';
import {ClassComponent} from '../vnode/VNodeTags';
import {resetDepContexts} from '../components/context/Context';
import {getIncompleteClassComponent} from './ClassComponent';
function captureIncompleteClassComponent(processing, Component, nextProps) {
processing.tag = ClassComponent;
resetDepContexts(processing);
return getIncompleteClassComponent(Component, processing, nextProps);
}
export function captureRender(processing: VNode): VNode | null {
const Component = processing.type;
const unresolvedProps = processing.props;
const resolvedProps =
processing.isLazyComponent
? mergeDefaultProps(Component, unresolvedProps)
: unresolvedProps;
return captureIncompleteClassComponent(processing, Component, resolvedProps);
}
export function bubbleRender(processing: VNode) {}

View File

@ -1,17 +1,16 @@
import type {VNode, PromiseType} from '../Types'; import type { VNode, PromiseType } from '../Types';
import {FlagUtils, Interrupted} from '../vnode/VNodeFlags'; import { FlagUtils, Interrupted } from '../vnode/VNodeFlags';
import {onlyUpdateChildVNodes, updateVNode, createFragmentVNode} from '../vnode/VNodeCreator'; import { onlyUpdateChildVNodes, updateVNode, createFragmentVNode } from '../vnode/VNodeCreator';
import { import {
ClassComponent, ClassComponent,
ForwardRef,
FunctionComponent, FunctionComponent,
IncompleteClassComponent,
SuspenseComponent, SuspenseComponent,
} from '../vnode/VNodeTags'; } from '../vnode/VNodeTags';
import {pushForceUpdate} from '../UpdateHandler'; import { pushForceUpdate } from '../UpdateHandler';
import {launchUpdateFromVNode, tryRenderFromRoot} from '../TreeBuilder'; import { launchUpdateFromVNode, tryRenderFromRoot } from '../TreeBuilder';
import {updateShouldUpdateOfTree} from '../vnode/VNodeShouldUpdate'; import { updateShouldUpdateOfTree } from '../vnode/VNodeShouldUpdate';
import {getContextChangeCtx} from '../ContextSaver';
import { markVNodePath } from '../utils/vNodePath'; import { markVNodePath } from '../utils/vNodePath';
export enum SuspenseChildStatus { export enum SuspenseChildStatus {
@ -47,7 +46,7 @@ function createFallback(processing: VNode, fallbackChildren) {
fallbackFragment.eIndex = 1; fallbackFragment.eIndex = 1;
fallbackFragment.cIndex = 1; fallbackFragment.cIndex = 1;
markVNodePath(fallbackFragment); markVNodePath(fallbackFragment);
processing.suspenseChildStatus = SuspenseChildStatus.ShowFallback; processing.suspenseState.childStatus = SuspenseChildStatus.ShowFallback;
return fallbackFragment; return fallbackFragment;
} }
@ -71,7 +70,7 @@ function createSuspenseChildren(processing: VNode, newChildren) {
processing.dirtyNodes = [oldFallbackFragment]; processing.dirtyNodes = [oldFallbackFragment];
} }
// SuspenseComponent 中使用 // SuspenseComponent 中使用
processing.suspenseChildStatus = SuspenseChildStatus.ShowChild; processing.suspenseState.childStatus = SuspenseChildStatus.ShowChild;
} else { } else {
childFragment = createFragmentVNode(null, newChildren); childFragment = createFragmentVNode(null, newChildren);
} }
@ -80,7 +79,7 @@ function createSuspenseChildren(processing: VNode, newChildren) {
childFragment.cIndex = 0; childFragment.cIndex = 0;
markVNodePath(childFragment); markVNodePath(childFragment);
processing.child = childFragment; processing.child = childFragment;
processing.promiseResolve = false; processing.suspenseState.promiseResolved = false;
return processing.child; return processing.child;
} }
@ -88,10 +87,10 @@ export function captureSuspenseComponent(processing: VNode) {
const nextProps = processing.props; const nextProps = processing.props;
// suspense被捕获后需要展示fallback // suspense被捕获后需要展示fallback
const showFallback = processing.suspenseDidCapture; const showFallback = processing.suspenseState.didCapture;
if (showFallback) { if (showFallback) {
processing.suspenseDidCapture = false; processing.suspenseState.didCapture = false;
const nextFallbackChildren = nextProps.fallback; const nextFallbackChildren = nextProps.fallback;
return createFallback(processing, nextFallbackChildren); return createFallback(processing, nextFallbackChildren);
} else { } else {
@ -101,15 +100,15 @@ export function captureSuspenseComponent(processing: VNode) {
} }
function updateFallback(processing: VNode): Array<VNode> | VNode | null { function updateFallback(processing: VNode): Array<VNode> | VNode | null {
const childFragment: VNode | null= processing.child; const childFragment: VNode | null = processing.child;
if (childFragment?.childShouldUpdate) { if (childFragment?.childShouldUpdate) {
if (processing.promiseResolve) { if (processing.suspenseState.promiseResolved) {
// promise已完成展示promise返回的新节点 // promise已完成展示promise返回的新节点
return captureSuspenseComponent(processing); return captureSuspenseComponent(processing);
} else { } else {
// promise未完成继续显示fallback不需要继续刷新子节点 // promise未完成继续显示fallback不需要继续刷新子节点
const fallbackFragment: VNode = processing.child.next; const fallbackFragment: VNode = processing.child!.next!;
childFragment.childShouldUpdate = false; childFragment.childShouldUpdate = false;
fallbackFragment.childShouldUpdate = false; fallbackFragment.childShouldUpdate = false;
return null; return null;
@ -130,10 +129,9 @@ export function captureRender(processing: VNode, shouldUpdate: boolean): Array<V
if ( if (
!processing.isCreated && !processing.isCreated &&
processing.oldProps === processing.props && processing.oldProps === processing.props &&
!getContextChangeCtx() &&
!shouldUpdate !shouldUpdate
) { ) {
if (processing.suspenseChildStatus === SuspenseChildStatus.ShowFallback) { if (processing.suspenseState.childStatus === SuspenseChildStatus.ShowFallback) {
// 当显示fallback时suspense的子组件要更新 // 当显示fallback时suspense的子组件要更新
return updateFallback(processing); return updateFallback(processing);
} }
@ -144,8 +142,9 @@ export function captureRender(processing: VNode, shouldUpdate: boolean): Array<V
} }
export function bubbleRender(processing: VNode) { export function bubbleRender(processing: VNode) {
if (processing.suspenseChildStatus === SuspenseChildStatus.ShowFallback const { childStatus, oldChildStatus } = processing.suspenseState;
|| (!processing.isCreated && processing.oldSuspenseChildStatus === SuspenseChildStatus.ShowFallback) if (childStatus === SuspenseChildStatus.ShowFallback
|| (!processing.isCreated && oldChildStatus === SuspenseChildStatus.ShowFallback)
) { ) {
FlagUtils.markUpdate(processing); FlagUtils.markUpdate(processing);
} }
@ -154,22 +153,21 @@ export function bubbleRender(processing: VNode) {
} }
function canCapturePromise(vNode: VNode | null): boolean { function canCapturePromise(vNode: VNode | null): boolean {
return vNode?.suspenseChildStatus !== SuspenseChildStatus.ShowFallback && vNode?.props.fallback !== undefined; return vNode?.suspenseState.childStatus !== SuspenseChildStatus.ShowFallback && vNode?.props.fallback !== undefined;
} }
// 处理Suspense子组件抛出的promise // 处理Suspense子组件抛出的promise
export function handleSuspenseChildThrowError(parent: VNode, processing: VNode, error: any): boolean { export function handleSuspenseChildThrowError(parent: VNode, processing: VNode, promise: PromiseType<any>): boolean {
let vNode = parent; let vNode = parent;
// 向上找到最近的不在fallback状态的Suspense并触发重新渲染 // 向上找到最近的不在fallback状态的Suspense并触发重新渲染
do { do {
if (vNode.tag === SuspenseComponent && canCapturePromise(vNode)) { if (vNode.tag === SuspenseComponent && canCapturePromise(vNode)) {
if (vNode.suspensePromises === null) { if (vNode.suspenseState.promiseSet === null) {
vNode.suspensePromises = new Set(); vNode.suspenseState.promiseSet = new Set();
} }
vNode.suspensePromises.add(error); vNode.suspenseState.promiseSet.add(promise);
processing.suspenseChildThrow = true;
// 移除生命周期flag 和 中断flag // 移除生命周期flag 和 中断flag
FlagUtils.removeLifecycleEffectFlags(processing); FlagUtils.removeLifecycleEffectFlags(processing);
@ -178,7 +176,7 @@ export function handleSuspenseChildThrowError(parent: VNode, processing: VNode,
if (processing.tag === ClassComponent) { if (processing.tag === ClassComponent) {
if (processing.isCreated) { if (processing.isCreated) {
// 渲染类组件场景要标志未完成否则会触发componentWillUnmount // 渲染类组件场景要标志未完成否则会触发componentWillUnmount
processing.tag = IncompleteClassComponent; processing.isSuspended = true;
} else { } else {
// 类组件更新标记强制更新否则被memo等优化跳过 // 类组件更新标记强制更新否则被memo等优化跳过
pushForceUpdate(processing); pushForceUpdate(processing);
@ -186,13 +184,13 @@ export function handleSuspenseChildThrowError(parent: VNode, processing: VNode,
} }
} }
if(processing.tag === FunctionComponent) { if (processing.tag === FunctionComponent || processing.tag === ForwardRef) {
processing.isSuspended = true; processing.isSuspended = true;
} }
// 应该抛出promise未完成更新标志待更新 // 应该抛出promise未完成更新标志待更新
processing.shouldUpdate = true; processing.shouldUpdate = true;
vNode.suspenseDidCapture = true; vNode.suspenseState.didCapture = true;
launchUpdateFromVNode(vNode); launchUpdateFromVNode(vNode);
return true; return true;
@ -211,7 +209,7 @@ function resolvePromise(suspenseVNode: VNode, promise: PromiseType<any>) {
if (promiseCache !== null) { if (promiseCache !== null) {
promiseCache.delete(promise); promiseCache.delete(promise);
} }
suspenseVNode.promiseResolve = true; suspenseVNode.suspenseState.promiseResolved = true;
const root = updateShouldUpdateOfTree(suspenseVNode); const root = updateShouldUpdateOfTree(suspenseVNode);
if (root !== null) { if (root !== null) {
tryRenderFromRoot(root); tryRenderFromRoot(root);
@ -220,9 +218,9 @@ function resolvePromise(suspenseVNode: VNode, promise: PromiseType<any>) {
// 对于每个promise添加一个侦听器以便当它resolve时重新渲染 // 对于每个promise添加一个侦听器以便当它resolve时重新渲染
export function listenToPromise(suspenseVNode: VNode) { export function listenToPromise(suspenseVNode: VNode) {
const promises: Set<PromiseType<any>> | null = suspenseVNode.suspensePromises; const promises: Set<PromiseType<any>> | null = suspenseVNode.suspenseState.promiseSet;
if (promises !== null) { if (promises !== null) {
suspenseVNode.suspensePromises = null; suspenseVNode.suspenseState.promiseSet = null;
// 记录已经监听的 promise // 记录已经监听的 promise
let promiseCache = suspenseVNode.realNode; let promiseCache = suspenseVNode.realNode;

View File

@ -9,7 +9,6 @@ import * as DomComponentRender from './DomComponent';
import * as DomPortalRender from './DomPortal'; import * as DomPortalRender from './DomPortal';
import * as TreeRootRender from './TreeRoot'; import * as TreeRootRender from './TreeRoot';
import * as DomTextRender from './DomText'; import * as DomTextRender from './DomText';
import * as IncompleteClassComponentRender from './IncompleteClassComponent';
import * as LazyComponentRender from './LazyComponent'; import * as LazyComponentRender from './LazyComponent';
import * as MemoComponentRender from './MemoComponent'; import * as MemoComponentRender from './MemoComponent';
import * as SuspenseComponentRender from './SuspenseComponent'; import * as SuspenseComponentRender from './SuspenseComponent';
@ -25,15 +24,14 @@ import {
DomPortal, DomPortal,
TreeRoot, TreeRoot,
DomText, DomText,
IncompleteClassComponent,
LazyComponent, LazyComponent,
MemoComponent, MemoComponent,
SuspenseComponent, SuspenseComponent,
} from '../vnode/VNodeTags'; } from '../vnode/VNodeTags';
export { export {
BaseComponentRender BaseComponentRender,
} };
export default { export default {
[ClassComponent]: ClassComponentRender, [ClassComponent]: ClassComponentRender,
@ -46,8 +44,7 @@ export default {
[DomPortal]: DomPortalRender, [DomPortal]: DomPortalRender,
[TreeRoot]: TreeRootRender, [TreeRoot]: TreeRootRender,
[DomText]: DomTextRender, [DomText]: DomTextRender,
[IncompleteClassComponent]: IncompleteClassComponentRender,
[LazyComponent]: LazyComponentRender, [LazyComponent]: LazyComponentRender,
[MemoComponent]: MemoComponentRender, [MemoComponent]: MemoComponentRender,
[SuspenseComponent]: SuspenseComponentRender, [SuspenseComponent]: SuspenseComponentRender,
} };

View File

@ -32,7 +32,7 @@ import {
callEffectRemove, callEffectRemove,
callUseEffects, callUseEffects,
callUseLayoutEffectCreate, callUseLayoutEffectCreate,
callUseLayoutEffectRemove callUseLayoutEffectRemove,
} from './HookEffectHandler'; } from './HookEffectHandler';
import { handleSubmitError } from '../ErrorHandler'; import { handleSubmitError } from '../ErrorHandler';
import { import {
@ -192,7 +192,8 @@ function unmountVNode(vNode: VNode): void {
const instance = vNode.realNode; const instance = vNode.realNode;
// 当constructor中抛出异常时instance会是null这里判断一下instance是否为空 // 当constructor中抛出异常时instance会是null这里判断一下instance是否为空
if (instance && typeof instance.componentWillUnmount === 'function') { // suspense打断时不需要触发WillUnmount
if (instance && typeof instance.componentWillUnmount === 'function' && !vNode.isSuspended) {
callComponentWillUnmount(vNode, instance); callComponentWillUnmount(vNode, instance);
} }
break; break;
@ -212,11 +213,11 @@ function unmountVNode(vNode: VNode): void {
// 卸载vNode递归遍历子vNode // 卸载vNode递归遍历子vNode
function unmountNestedVNodes(vNode: VNode): void { function unmountNestedVNodes(vNode: VNode): void {
travelVNodeTree(vNode, (node) => { travelVNodeTree(vNode, (node) => {
unmountVNode(node); unmountVNode(node);
}, node => }, node =>
// 如果是DomPortal不需要遍历child // 如果是DomPortal不需要遍历child
node.tag === DomPortal node.tag === DomPortal
, vNode, null); , vNode, null);
} }
function submitAddition(vNode: VNode): void { function submitAddition(vNode: VNode): void {
@ -329,7 +330,7 @@ function submitClear(vNode: VNode): void {
// 但考虑到用户可能自定义其他属性,所以采用遍历赋值的方式 // 但考虑到用户可能自定义其他属性,所以采用遍历赋值的方式
const customizeKeys = Object.keys(realNode); const customizeKeys = Object.keys(realNode);
const keyLength = customizeKeys.length; const keyLength = customizeKeys.length;
for(let i = 0; i < keyLength; i++) { for (let i = 0; i < keyLength; i++) {
const key = customizeKeys[i]; const key = customizeKeys[i];
// 测试代码 mock 实例的全部可遍历属性都会被Object.keys方法读取到 // 测试代码 mock 实例的全部可遍历属性都会被Object.keys方法读取到
// children 属性被复制意味着复制了子节点,因此要排除 // children 属性被复制意味着复制了子节点,因此要排除
@ -351,7 +352,7 @@ function submitClear(vNode: VNode): void {
} }
let clearChild = vNode.clearChild as VNode; // 上次渲染的child保存在clearChild属性中 let clearChild = vNode.clearChild as VNode; // 上次渲染的child保存在clearChild属性中
// 卸载 clearChild 和 它的兄弟节点 // 卸载 clearChild 和 它的兄弟节点
while(clearChild) { while (clearChild) {
// 卸载子vNode递归遍历子vNode // 卸载子vNode递归遍历子vNode
unmountNestedVNodes(clearChild); unmountNestedVNodes(clearChild);
clearVNode(clearChild); clearVNode(clearChild);
@ -399,9 +400,9 @@ function submitUpdate(vNode: VNode): void {
} }
function submitSuspenseComponent(vNode: VNode) { function submitSuspenseComponent(vNode: VNode) {
const suspenseChildStatus = vNode.suspenseChildStatus; const { childStatus } = vNode.suspenseState;
if (suspenseChildStatus !== SuspenseChildStatus.Init) { if (childStatus !== SuspenseChildStatus.Init) {
hideOrUnhideAllChildren(vNode.child, suspenseChildStatus === SuspenseChildStatus.ShowFallback); hideOrUnhideAllChildren(vNode.child, childStatus === SuspenseChildStatus.ShowFallback);
} }
} }

View File

@ -1,9 +1,24 @@
/** /**
* DOM结构体 * DOM结构体
*/ */
import { TreeRoot, FunctionComponent, ClassComponent, DomPortal, DomText, ContextConsumer, ForwardRef, SuspenseComponent, LazyComponent, DomComponent, Fragment, ContextProvider, Profiler, MemoComponent, IncompleteClassComponent } from './VNodeTags'; import {
TreeRoot,
FunctionComponent,
ClassComponent,
DomPortal,
DomText,
ContextConsumer,
ForwardRef,
SuspenseComponent,
LazyComponent,
DomComponent,
Fragment,
ContextProvider,
Profiler,
MemoComponent,
} from './VNodeTags';
import type { VNodeTag } from './VNodeTags'; import type { VNodeTag } from './VNodeTags';
import type { RefType, ContextType } from '../Types'; import type { RefType, ContextType, SuspenseState } from '../Types';
import type { Hook } from '../hooks/HookType'; import type { Hook } from '../hooks/HookType';
import { InitFlag } from './VNodeFlags'; import { InitFlag } from './VNodeFlags';
@ -24,7 +39,6 @@ export class VNode {
ref: RefType | ((handle: any) => void) | null = null; // 包裹一个函数submit阶段使用比如将外部useRef生成的对象赋值到ref上 ref: RefType | ((handle: any) => void) | null = null; // 包裹一个函数submit阶段使用比如将外部useRef生成的对象赋值到ref上
oldProps: any = null; oldProps: any = null;
suspensePromises: any; // suspense组件的promise列表
changeList: any; // DOM的变更列表 changeList: any; // DOM的变更列表
effectList: any[] | null; // useEffect 的更新数组 effectList: any[] | null; // useEffect 的更新数组
updates: any[] | null; // TreeRoot和ClassComponent使用的更新数组 updates: any[] | null; // TreeRoot和ClassComponent使用的更新数组
@ -33,7 +47,6 @@ export class VNode {
isSuspended = false; // 是否被suspense打断更新 isSuspended = false; // 是否被suspense打断更新
state: any; // ClassComponent和TreeRoot的状态 state: any; // ClassComponent和TreeRoot的状态
hooks: Array<Hook<any, any>> | null; // 保存hook hooks: Array<Hook<any, any>> | null; // 保存hook
suspenseChildStatus = ''; // Suspense的Children是否显示
depContexts: Array<ContextType<any>> | null; // FunctionComponent和ClassComponent对context的依赖列表 depContexts: Array<ContextType<any>> | null; // FunctionComponent和ClassComponent对context的依赖列表
isDepContextChange: boolean; // context是否变更 isDepContextChange: boolean; // context是否变更
dirtyNodes: Array<VNode> | null = null; // 需要改动的节点数组 dirtyNodes: Array<VNode> | null = null; // 需要改动的节点数组
@ -42,7 +55,7 @@ export class VNode {
task: any; task: any;
// 使用这个变量来记录修改前的值,用于恢复。 // 使用这个变量来记录修改前的值,用于恢复。
contexts: any; context: any;
// 因为LazyComponent会修改tag和type属性为了能识别增加一个属性 // 因为LazyComponent会修改tag和type属性为了能识别增加一个属性
isLazyComponent: boolean; isLazyComponent: boolean;
@ -55,12 +68,11 @@ export class VNode {
oldHooks: Array<Hook<any, any>> | null; // 保存上一次执行的hook oldHooks: Array<Hook<any, any>> | null; // 保存上一次执行的hook
oldState: any; oldState: any;
oldRef: RefType | ((handle: any) => void) | null = null; oldRef: RefType | ((handle: any) => void) | null = null;
suspenseChildThrow: boolean;
oldSuspenseChildStatus: string; // 上一次Suspense的Children是否显示
oldChild: VNode | null = null; oldChild: VNode | null = null;
suspenseDidCapture: boolean; // suspense是否捕获了异常
promiseResolve: boolean; // suspense的promise是否resolve promiseResolve: boolean; // suspense的promise是否resolve
suspenseState: SuspenseState;
path = ''; // 保存从根到本节点的路径 path = ''; // 保存从根到本节点的路径
toUpdateNodes: Set<VNode> | null; // 保存要更新的节点 toUpdateNodes: Set<VNode> | null; // 保存要更新的节点
@ -85,7 +97,7 @@ export class VNode {
this.stateCallbacks = null; this.stateCallbacks = null;
this.state = null; this.state = null;
this.oldState = null; this.oldState = null;
this.contexts = null; this.context = null;
break; break;
case FunctionComponent: case FunctionComponent:
this.realNode = null; this.realNode = null;
@ -106,30 +118,32 @@ export class VNode {
this.depContexts = null; this.depContexts = null;
this.isDepContextChange = false; this.isDepContextChange = false;
this.oldState = null; this.oldState = null;
this.contexts = null; this.context = null;
break; break;
case DomPortal: case DomPortal:
this.realNode = null; this.realNode = null;
this.contexts = null; this.context = null;
break; break;
case DomComponent: case DomComponent:
this.realNode = null; this.realNode = null;
this.changeList = null; this.changeList = null;
this.contexts = null; this.context = null;
break; break;
case DomText: case DomText:
this.realNode = null; this.realNode = null;
break; break;
case SuspenseComponent: case SuspenseComponent:
this.realNode = null; this.realNode = null;
this.suspensePromises = null; this.suspenseState = {
this.suspenseChildThrow = false; promiseSet: null,
this.suspenseDidCapture = false; didCapture: false,
this.promiseResolve = false; promiseResolved: false,
this.oldSuspenseChildStatus = ''; oldChildStatus: '',
childStatus: ''
};
break; break;
case ContextProvider: case ContextProvider:
this.contexts = null; this.context = null;
break; break;
case MemoComponent: case MemoComponent:
this.effectList = null; this.effectList = null;
@ -149,8 +163,6 @@ export class VNode {
break; break;
case Profiler: case Profiler:
break; break;
case IncompleteClassComponent:
break;
} }
} }
} }

View File

@ -38,9 +38,9 @@ const typeMap = {
[TYPE_LAZY]: LazyComponent, [TYPE_LAZY]: LazyComponent,
}; };
const newVirtualNode = function(tag: VNodeTag, key?: null | string, vNodeProps?: any, realNode?: any): VNode { function newVirtualNode (tag: VNodeTag, key?: null | string, vNodeProps?: any, realNode?: any): VNode {
return new VNode(tag, vNodeProps, key, realNode); return new VNode(tag, vNodeProps, key, realNode);
}; }
function isClassComponent(comp: Function) { function isClassComponent(comp: Function) {
// 如果使用 getPrototypeOf 方法获取构造函数,不能兼容业务组组件继承组件的使用方式,会误认为是函数组件 // 如果使用 getPrototypeOf 方法获取构造函数,不能兼容业务组组件继承组件的使用方式,会误认为是函数组件
@ -66,7 +66,7 @@ export function updateVNode(vNode: VNode, vNodeProps?: any): VNode {
} }
if (vNode.tag === SuspenseComponent) { if (vNode.tag === SuspenseComponent) {
vNode.oldSuspenseChildStatus = vNode.suspenseChildStatus; vNode.suspenseState.oldChildStatus = vNode.suspenseState.childStatus;
vNode.oldChild = vNode.child; vNode.oldChild = vNode.child;
} }

View File

@ -82,7 +82,7 @@ export function clearVNode(vNode: VNode) {
vNode.hooks = null; vNode.hooks = null;
vNode.props = null; vNode.props = null;
vNode.parent = null; vNode.parent = null;
vNode.suspensePromises = null; vNode.suspenseState = null;
vNode.changeList = null; vNode.changeList = null;
vNode.effectList = null; vNode.effectList = null;
vNode.updates = null; vNode.updates = null;