Match-id-c7d7219afb77e78b0a781cb327a722014d610037
This commit is contained in:
commit
9741aeacf2
|
@ -0,0 +1,131 @@
|
|||
import type {VNode} from '../Types';
|
||||
import type {Props} from '../../dom/DOMOperator';
|
||||
|
||||
import {
|
||||
getNamespaceCtx,
|
||||
setNamespaceCtx,
|
||||
resetNamespaceCtx,
|
||||
} from '../ContextSaver';
|
||||
import {
|
||||
appendChildElement,
|
||||
newDom,
|
||||
initDomProps, getPropChangeList,
|
||||
isTextChild,
|
||||
} from '../../dom/DOMOperator';
|
||||
import {FlagUtils} from '../vnode/VNodeFlags';
|
||||
import {createVNodeChildren, markRef} from './BaseComponent';
|
||||
import {DomComponent, DomPortal, DomText} from '../vnode/VNodeTags';
|
||||
import {getFirstChild, travelVNodeTree} from '../vnode/VNodeUtils';
|
||||
|
||||
export function captureRender(processing: VNode): Array<VNode> | null {
|
||||
return captureDomComponent(processing);
|
||||
}
|
||||
|
||||
export function bubbleRender(processing: VNode) {
|
||||
resetNamespaceCtx(processing);
|
||||
|
||||
const type = processing.type;
|
||||
const newProps = processing.props;
|
||||
if (!processing.isCreated && processing.realNode != null) {
|
||||
// 更新dom属性
|
||||
updateDom(
|
||||
processing,
|
||||
type,
|
||||
newProps,
|
||||
);
|
||||
|
||||
if (processing.oldRef !== processing.ref) {
|
||||
FlagUtils.markRef(processing);
|
||||
}
|
||||
} else {
|
||||
const parentNamespace = getNamespaceCtx();
|
||||
|
||||
// 创建dom
|
||||
const dom = newDom(
|
||||
type,
|
||||
newProps,
|
||||
parentNamespace,
|
||||
processing,
|
||||
);
|
||||
|
||||
appendAllChildren(dom, processing);
|
||||
|
||||
processing.realNode = dom;
|
||||
|
||||
if (initDomProps(dom, type, newProps)) {
|
||||
FlagUtils.markUpdate(processing);
|
||||
}
|
||||
|
||||
// 处理ref导致的更新
|
||||
if (processing.ref !== null) {
|
||||
FlagUtils.markRef(processing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function captureDomComponent(processing: VNode): Array<VNode> | null {
|
||||
setNamespaceCtx(processing);
|
||||
|
||||
const type = processing.type;
|
||||
const newProps = processing.props;
|
||||
const oldProps = !processing.isCreated ? processing.oldProps : null;
|
||||
|
||||
let nextChildren = newProps.children;
|
||||
const isDirectTextChild = isTextChild(type, newProps);
|
||||
|
||||
if (isDirectTextChild) {
|
||||
// 如果为文本节点,则认为没有子节点
|
||||
nextChildren = null;
|
||||
} else if (oldProps !== null && isTextChild(type, oldProps)) {
|
||||
// 将纯文本的子节点改为vNode节点
|
||||
FlagUtils.markContentReset(processing);
|
||||
}
|
||||
|
||||
markRef(processing);
|
||||
processing.children = createVNodeChildren(processing, nextChildren);
|
||||
return processing.children;
|
||||
}
|
||||
|
||||
// 把dom类型的子节点append到parent dom中
|
||||
function appendAllChildren(parent: Element, processing: VNode) {
|
||||
const vNode = getFirstChild(processing);
|
||||
if (vNode === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 向下递归它的子节点,查找所有终端节点。
|
||||
travelVNodeTree(vNode, (node) => {
|
||||
if (node.tag === DomComponent || node.tag === DomText) {
|
||||
appendChildElement(parent, node.realNode);
|
||||
}
|
||||
}, (node) => {
|
||||
// 已经append到父节点,或者是DomPortal都不需要处理child了
|
||||
return node.tag === DomComponent || node.tag === DomText || node.tag === DomPortal;
|
||||
}, processing);
|
||||
}
|
||||
|
||||
function updateDom(
|
||||
processing: VNode,
|
||||
type: any,
|
||||
newProps: Props,
|
||||
) {
|
||||
// 如果oldProps !== newProps,意味着存在更新,并且需要处理其相关的副作用
|
||||
const oldProps = processing.oldProps;
|
||||
if (oldProps === newProps) {
|
||||
// 如果props没有发生变化,即使它的children发生了变化,我们也不会改变它
|
||||
return;
|
||||
}
|
||||
|
||||
const dom: Element = processing.realNode;
|
||||
|
||||
const changeList = getPropChangeList(
|
||||
dom,
|
||||
type,
|
||||
oldProps,
|
||||
newProps,
|
||||
);
|
||||
processing.changeList = changeList;
|
||||
|
||||
// 标记为更新
|
||||
FlagUtils.markUpdate(processing);
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
import type { VNode } from '../Types';
|
||||
import { resetNamespaceCtx, setNamespaceCtx } from '../ContextSaver';
|
||||
import { createChildrenByDiff } from '../diff/nodeDiffComparator';
|
||||
import { createVNodeChildren } from './BaseComponent';
|
||||
import { prePortal } from '../../dom/DOMOperator';
|
||||
|
||||
export function captureRender(processing: VNode): Array<VNode> | null {
|
||||
return capturePortalComponent(processing);
|
||||
}
|
||||
|
||||
export function bubbleRender(processing: VNode) {
|
||||
resetNamespaceCtx(processing);
|
||||
|
||||
if (processing.isCreated) {
|
||||
prePortal(processing.outerDom);
|
||||
}
|
||||
}
|
||||
|
||||
function capturePortalComponent(processing: VNode) {
|
||||
setNamespaceCtx(processing, processing.outerDom);
|
||||
|
||||
const newElements = processing.props;
|
||||
if (processing.isCreated) {
|
||||
processing.children = createChildrenByDiff(processing, [], newElements);
|
||||
} else {
|
||||
processing.children = createVNodeChildren(processing, newElements);
|
||||
}
|
||||
return processing.children;
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
import type {VNode} from '../Types';
|
||||
|
||||
import {throwIfTrue} from '../utils/throwIfTrue';
|
||||
import {newTextDom} from '../../dom/DOMOperator';
|
||||
import {FlagUtils} from '../vnode/VNodeFlags';
|
||||
|
||||
export function captureRender(): VNode | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
export function bubbleRender(processing: VNode) {
|
||||
const newText = processing.props;
|
||||
|
||||
if (!processing.isCreated && processing.realNode != null) { // 更新
|
||||
const oldText = processing.oldProps;
|
||||
// 如果文本不同,将其标记为更新
|
||||
if (oldText !== newText) {
|
||||
FlagUtils.markUpdate(processing);
|
||||
}
|
||||
} else { // 初始化
|
||||
if (typeof newText !== 'string') {
|
||||
// 如果存在bug,可能出现这种情况
|
||||
throwIfTrue(
|
||||
processing.realNode === null,
|
||||
'We must have new text for new mounted node. This error is likely ' +
|
||||
'caused by a bug in Horizon. Please file an issue.',
|
||||
);
|
||||
}
|
||||
// 获得对应节点
|
||||
processing.realNode = newTextDom(
|
||||
newText,
|
||||
processing,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
import type {VNode} from '../Types';
|
||||
import {captureRender as funCaptureRender} from './FunctionComponent';
|
||||
|
||||
export function captureRender(processing: VNode, shouldUpdate?: boolean): Array<VNode> | null {
|
||||
return funCaptureRender(processing, shouldUpdate);
|
||||
}
|
||||
|
||||
export function bubbleRender() {}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
import type {VNode} from '../Types';
|
||||
import {createVNodeChildren} from './BaseComponent';
|
||||
|
||||
export function captureRender(processing: VNode): Array<VNode> | null {
|
||||
return captureFragment(processing);
|
||||
}
|
||||
|
||||
export function bubbleRender() {}
|
||||
|
||||
function captureFragment(processing: VNode) {
|
||||
const newElement = processing.props;
|
||||
processing.children = createVNodeChildren(processing, newElement);
|
||||
return processing.children;
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
import type {VNode} from '../Types';
|
||||
|
||||
import {mergeDefaultProps} from './LazyComponent';
|
||||
import {getOldContext} from '../components/context/CompatibleContext';
|
||||
import {resetDepContexts} from '../components/context/Context';
|
||||
import {exeFunctionHook} from '../hooks/HookMain';
|
||||
import {createVNodeChildren} from './BaseComponent';
|
||||
import {ForwardRef} from '../vnode/VNodeTags';
|
||||
import {FlagUtils, Update} from '../vnode/VNodeFlags';
|
||||
import {getContextChangeCtx} from '../ContextSaver';
|
||||
import {onlyUpdateChildVNodes} from '../vnode/VNodeCreator';
|
||||
|
||||
// 在useState, useReducer的时候,会触发state变化
|
||||
let stateChange = false;
|
||||
|
||||
export function captureRender(processing: VNode, shouldUpdate?: boolean): Array<VNode> | null {
|
||||
const Component = processing.type;
|
||||
const unresolvedProps = processing.props;
|
||||
const resolvedProps =
|
||||
processing.isLazyComponent
|
||||
? mergeDefaultProps(Component, unresolvedProps)
|
||||
: unresolvedProps;
|
||||
|
||||
return captureFunctionComponent(
|
||||
processing,
|
||||
Component,
|
||||
resolvedProps,
|
||||
shouldUpdate
|
||||
);
|
||||
}
|
||||
|
||||
export function bubbleRender() {}
|
||||
|
||||
export function captureFunctionComponent(
|
||||
processing: VNode,
|
||||
funcComp: any,
|
||||
nextProps: any,
|
||||
shouldUpdate?: boolean
|
||||
) {
|
||||
let context;
|
||||
if (processing.tag !== ForwardRef) {
|
||||
context = getOldContext(processing, funcComp, true);
|
||||
}
|
||||
|
||||
resetDepContexts(processing);
|
||||
|
||||
const isCanReuse = checkIfCanReuseChildren(processing, shouldUpdate);
|
||||
// 在执行exeFunctionHook前先设置stateChange为false
|
||||
setStateChange(false);
|
||||
|
||||
const newElements = exeFunctionHook(
|
||||
processing.tag === ForwardRef ? funcComp.render : funcComp,
|
||||
nextProps,
|
||||
processing.tag === ForwardRef ? processing.ref : context,
|
||||
processing,
|
||||
);
|
||||
|
||||
// 这里需要判断是否可以复用,因为函数组件比起其他组价,多了context和stateChange两个因素
|
||||
if (isCanReuse && !isStateChange()) {
|
||||
FlagUtils.removeFlag(processing, Update);
|
||||
|
||||
return onlyUpdateChildVNodes(processing);
|
||||
}
|
||||
|
||||
processing.children = createVNodeChildren(processing, newElements);
|
||||
return processing.children;
|
||||
}
|
||||
|
||||
// 判断children是否可以复用
|
||||
function checkIfCanReuseChildren(processing: VNode, shouldUpdate?: boolean) {
|
||||
let isCanReuse = true;
|
||||
|
||||
if (!processing.isCreated) {
|
||||
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) {
|
||||
stateChange = isUpdate;
|
||||
}
|
||||
|
||||
export function isStateChange() {
|
||||
return stateChange;
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
import type {VNode} from '../Types';
|
||||
|
||||
import {mergeDefaultProps} from './LazyComponent';
|
||||
import {ClassComponent} from '../vnode/VNodeTags';
|
||||
import {resetDepContexts} from '../components/context/Context';
|
||||
import {getIncompleteClassComponent} from './ClassComponent';
|
||||
import {
|
||||
isOldProvider,
|
||||
resetOldCtx,
|
||||
cacheOldCtx,
|
||||
} from '../components/context/CompatibleContext';
|
||||
|
||||
export function captureRender(processing: VNode): Array<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) {
|
||||
// 处理与类组件相同。
|
||||
const Component = processing.type;
|
||||
if (isOldProvider(Component)) {
|
||||
resetOldCtx(processing);
|
||||
}
|
||||
}
|
||||
|
||||
function captureIncompleteClassComponent(processing, Component, nextProps) {
|
||||
processing.tag = ClassComponent;
|
||||
|
||||
const hasOldContext = isOldProvider(Component);
|
||||
cacheOldCtx(processing, hasOldContext);
|
||||
|
||||
resetDepContexts(processing);
|
||||
|
||||
return getIncompleteClassComponent(Component, processing, nextProps);
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
import type {VNode} from '../Types';
|
||||
|
||||
import {FlagUtils} from '../vnode/VNodeFlags';
|
||||
import {getLazyVNodeTag} from '../vnode/VNodeCreator';
|
||||
import {
|
||||
ClassComponent,
|
||||
ForwardRef,
|
||||
FunctionComponent,
|
||||
MemoComponent,
|
||||
} from '../vnode/VNodeTags';
|
||||
import {throwIfTrue} from '../utils/throwIfTrue';
|
||||
import {captureFunctionComponent} from './FunctionComponent';
|
||||
import {captureClassComponent} from './ClassComponent';
|
||||
import {captureMemoComponent} from './MemoComponent';
|
||||
|
||||
export function captureRender(processing: VNode, shouldUpdate: boolean): VNode | null {
|
||||
return captureLazyComponent(processing, processing.type, shouldUpdate);
|
||||
}
|
||||
|
||||
export function bubbleRender() {}
|
||||
|
||||
const LazyRendererMap = {
|
||||
[FunctionComponent]: captureFunctionComponent,
|
||||
[ClassComponent]: captureClassComponent,
|
||||
[ForwardRef]: captureFunctionComponent,
|
||||
[MemoComponent]: captureMemoComponent,
|
||||
};
|
||||
|
||||
function captureLazyComponent(
|
||||
processing,
|
||||
lazyComponent,
|
||||
shouldUpdate,
|
||||
) {
|
||||
if (!processing.isCreated) {
|
||||
// 每次加载lazy都当作mount来处理
|
||||
processing.isCreated = true;
|
||||
FlagUtils.markAddition(processing);
|
||||
}
|
||||
|
||||
const Component = lazyComponent._load(lazyComponent._content);
|
||||
|
||||
// ======================loaded===============================
|
||||
// 加载得到的Component存在type中
|
||||
processing.type = Component;
|
||||
|
||||
const lazyVNodeTag = processing.tag = getLazyVNodeTag(Component);
|
||||
const lazyVNodeProps = mergeDefaultProps(Component, processing.props);
|
||||
|
||||
const lazyRender = LazyRendererMap[lazyVNodeTag];
|
||||
if (lazyRender) {
|
||||
if (lazyVNodeTag === MemoComponent) {
|
||||
// Memo要特殊处理
|
||||
const memoVNodeProps = mergeDefaultProps(Component.type, lazyVNodeProps); // 需要整合defaultProps
|
||||
return lazyRender(processing, Component, memoVNodeProps, shouldUpdate);
|
||||
} else {
|
||||
return lazyRender(processing, Component, lazyVNodeProps, false);
|
||||
}
|
||||
} else {
|
||||
// lazy加载的组件类型未受支持
|
||||
throwIfTrue(
|
||||
true,
|
||||
'Element type is invalid. Received a promise that resolves to: %s. ' +
|
||||
'Lazy element type must resolve to a class or function.%s',
|
||||
Component,
|
||||
'',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function mergeDefaultProps(Component: any, props: object): object {
|
||||
if (Component && Component.defaultProps) {
|
||||
const clonedProps = {...props};
|
||||
const defaultProps = Component.defaultProps;
|
||||
Object.keys(defaultProps).forEach(key => {
|
||||
if (clonedProps[key] === undefined) {
|
||||
clonedProps[key] = defaultProps[key];
|
||||
}
|
||||
});
|
||||
return clonedProps;
|
||||
}
|
||||
return props;
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
import type {VNode} from '../Types';
|
||||
|
||||
import {mergeDefaultProps} from './LazyComponent';
|
||||
import {updateVNode, createVNode, onlyUpdateChildVNodes, updateVNodePath} from '../vnode/VNodeCreator';
|
||||
import {shallowCompare} from '../utils/compare';
|
||||
import {
|
||||
TYPE_FRAGMENT,
|
||||
TYPE_PROFILER,
|
||||
TYPE_STRICT_MODE,
|
||||
} from '../utils/elementType';
|
||||
import {Fragment} from '../vnode/VNodeTags';
|
||||
|
||||
export function captureRender(processing: VNode, shouldUpdate: boolean): Array<VNode> | null {
|
||||
return captureMemoComponent(processing, shouldUpdate);
|
||||
}
|
||||
|
||||
export function bubbleRender() {}
|
||||
|
||||
export function captureMemoComponent(
|
||||
processing: VNode,
|
||||
shouldUpdate: boolean,
|
||||
): Array<VNode> | null {
|
||||
const Component = processing.type;
|
||||
// 合并 函数组件或类组件 的defaultProps
|
||||
const newProps = mergeDefaultProps(Component, processing.props);
|
||||
|
||||
if (processing.isCreated) {
|
||||
let newChild = null;
|
||||
const type = Component.type;
|
||||
if (type === TYPE_STRICT_MODE || type === TYPE_FRAGMENT || type === TYPE_PROFILER) {
|
||||
newChild = createVNode(Fragment, null, newProps.children);
|
||||
} else {
|
||||
newChild = createVNode('props', type, null, newProps, processing);
|
||||
}
|
||||
newChild.parent = processing;
|
||||
newChild.ref = processing.ref;
|
||||
updateVNodePath(newChild);
|
||||
processing.children = [newChild];
|
||||
|
||||
return processing.children;
|
||||
}
|
||||
|
||||
const firstChild = processing.children.length ? processing.children[0] : null; // Memo只有一个child
|
||||
if (!shouldUpdate) {
|
||||
const oldProps = firstChild.props;
|
||||
// 默认是浅对比
|
||||
const compare = Component.compare ? Component.compare : shallowCompare;
|
||||
if (compare(oldProps, newProps) && processing.oldRef === processing.ref) {
|
||||
return onlyUpdateChildVNodes(processing);
|
||||
}
|
||||
}
|
||||
|
||||
const newChild = updateVNode(firstChild, newProps);
|
||||
newChild.parent = processing;
|
||||
newChild.cIndex = 0;
|
||||
updateVNodePath(newChild);
|
||||
newChild.ref = processing.ref;
|
||||
processing.children = [newChild];
|
||||
|
||||
return processing.children;
|
||||
}
|
|
@ -0,0 +1,239 @@
|
|||
import type {VNode, PromiseType} from '../Types';
|
||||
|
||||
import {FlagUtils, Interrupted} from '../vnode/VNodeFlags';
|
||||
import {createVNode, onlyUpdateChildVNodes, updateVNode, updateVNodePath} from '../vnode/VNodeCreator';
|
||||
import {
|
||||
ClassComponent,
|
||||
IncompleteClassComponent,
|
||||
SuspenseComponent,
|
||||
Fragment,
|
||||
} from '../vnode/VNodeTags';
|
||||
import {pushForceUpdate} from '../UpdateHandler';
|
||||
import {launchUpdateFromVNode, tryRenderRoot} from '../TreeBuilder';
|
||||
import {updateShouldUpdateOfTree} from '../vnode/VNodeShouldUpdate';
|
||||
import {getContextChangeCtx} from '../ContextSaver';
|
||||
|
||||
export enum SuspenseChildStatus {
|
||||
Init = '',
|
||||
ShowChild = 'showChild',
|
||||
ShowFallback = 'showFallback',
|
||||
}
|
||||
|
||||
export function captureRender(processing: VNode, shouldUpdate: boolean): Array<VNode> | VNode | null {
|
||||
if (
|
||||
!processing.isCreated &&
|
||||
processing.oldProps === processing.props &&
|
||||
!getContextChangeCtx() &&
|
||||
!shouldUpdate
|
||||
) {
|
||||
if (processing.suspenseChildStatus === SuspenseChildStatus.ShowFallback) {
|
||||
// 当显示fallback时,suspense的子组件要更新
|
||||
return updateFallback(processing);
|
||||
}
|
||||
return onlyUpdateChildVNodes(processing);
|
||||
}
|
||||
|
||||
return captureSuspenseComponent(processing);
|
||||
}
|
||||
|
||||
function updateFallback(processing: VNode): Array<VNode> | VNode | null {
|
||||
const childFragment: VNode = processing.children[0];
|
||||
|
||||
if (childFragment.childShouldUpdate) {
|
||||
if (processing.promiseResolve) {
|
||||
// promise已完成,展示promise返回的新节点
|
||||
return captureSuspenseComponent(processing);
|
||||
} else {
|
||||
// promise未完成,继续显示fallback,不需要继续刷新子节点
|
||||
const fallbackFragment: VNode = processing.children[1];
|
||||
childFragment.childShouldUpdate = false;
|
||||
fallbackFragment.childShouldUpdate = false;
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
const children = onlyUpdateChildVNodes(processing);
|
||||
|
||||
if (children !== null) {
|
||||
// child不需要更新,跳过child处理fallback
|
||||
return children[1];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function bubbleRender(processing: VNode) {
|
||||
if (processing.suspenseChildStatus === SuspenseChildStatus.ShowFallback
|
||||
|| (!processing.isCreated && processing.oldSuspenseChildStatus === SuspenseChildStatus.ShowFallback)
|
||||
) {
|
||||
FlagUtils.markUpdate(processing);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function captureSuspenseComponent(processing: VNode) {
|
||||
const nextProps = processing.props;
|
||||
|
||||
// suspense被捕获后需要展示fallback
|
||||
const showFallback = processing.suspenseDidCapture;
|
||||
|
||||
if (showFallback) {
|
||||
processing.suspenseDidCapture = false;
|
||||
const nextFallbackChildren = nextProps.fallback;
|
||||
return createFallback(processing, nextFallbackChildren);
|
||||
} else {
|
||||
const newChildren = nextProps.children;
|
||||
return createSuspenseChildren(processing, newChildren);
|
||||
}
|
||||
}
|
||||
|
||||
// 创建子节点
|
||||
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;
|
||||
|
||||
childFragment = updateVNode(oldChildFragment);
|
||||
// 将Suspense新的子参数传给子Fragment
|
||||
childFragment.props = processing.props.children;
|
||||
childFragment.shouldUpdate = true;
|
||||
|
||||
// 删除fallback
|
||||
if (oldFallbackFragment !== null) {
|
||||
FlagUtils.setDeletion(oldFallbackFragment);
|
||||
processing.dirtyNodes = [oldFallbackFragment];
|
||||
}
|
||||
// SuspenseComponent 中使用
|
||||
processing.suspenseChildStatus = SuspenseChildStatus.ShowChild;
|
||||
} else {
|
||||
childFragment = createVNode(Fragment, null, newChildren);
|
||||
}
|
||||
|
||||
childFragment.parent = processing;
|
||||
childFragment.cIndex = 0;
|
||||
updateVNodePath(childFragment);
|
||||
processing.children = [childFragment];
|
||||
processing.promiseResolve = false;
|
||||
return processing.children;
|
||||
}
|
||||
|
||||
// 创建fallback子节点
|
||||
function createFallback(processing: VNode, fallbackChildren) {
|
||||
const childFragment: VNode = processing.children[0];
|
||||
let fallbackFragment;
|
||||
childFragment.childShouldUpdate = false;
|
||||
|
||||
if (!processing.isCreated) {
|
||||
const oldFallbackFragment: VNode | null = processing.oldChildren.length > 1 ? processing.oldChildren[1] : null;
|
||||
|
||||
if (oldFallbackFragment !== null) {
|
||||
fallbackFragment = updateVNode(oldFallbackFragment, fallbackChildren);
|
||||
} else {
|
||||
fallbackFragment = createVNode(Fragment, null, fallbackChildren);
|
||||
FlagUtils.markAddition(fallbackFragment);
|
||||
}
|
||||
} else {
|
||||
// 创建
|
||||
fallbackFragment = createVNode(Fragment, null, fallbackChildren);
|
||||
}
|
||||
|
||||
processing.children = [childFragment, fallbackFragment];
|
||||
childFragment.parent = processing;
|
||||
fallbackFragment.parent = processing;
|
||||
fallbackFragment.eIndex = 1;
|
||||
fallbackFragment.cIndex = 1;
|
||||
updateVNodePath(fallbackFragment);
|
||||
processing.suspenseChildStatus = SuspenseChildStatus.ShowFallback;
|
||||
|
||||
return [fallbackFragment];
|
||||
}
|
||||
|
||||
// 处理Suspense子组件抛出的promise
|
||||
export function handleSuspenseChildThrowError(parent: VNode, processing: VNode, error: any): boolean {
|
||||
let vNode = parent;
|
||||
|
||||
// 向上找到最近的不在fallback状态的Suspense,并触发重新渲染
|
||||
do {
|
||||
if (vNode.tag === SuspenseComponent && canCapturePromise(vNode)) {
|
||||
if (vNode.suspensePromises === null) {
|
||||
vNode.suspensePromises = new Set();
|
||||
}
|
||||
vNode.suspensePromises.add(error);
|
||||
|
||||
processing.suspenseChildThrow = true;
|
||||
|
||||
// 移除生命周期flag 和 中断flag
|
||||
FlagUtils.removeLifecycleEffectFlags(processing);
|
||||
FlagUtils.removeFlag(processing, Interrupted);
|
||||
|
||||
if (processing.tag === ClassComponent) {
|
||||
if (processing.isCreated) {
|
||||
// 渲染类组件场景,要标志未完成(否则会触发componentWillUnmount)
|
||||
processing.tag = IncompleteClassComponent;
|
||||
} else {
|
||||
// 类组件更新,标记强制更新,否则被memo等优化跳过
|
||||
pushForceUpdate(processing);
|
||||
launchUpdateFromVNode(processing);
|
||||
}
|
||||
}
|
||||
|
||||
// 应该抛出promise未完成更新,标志待更新
|
||||
processing.shouldUpdate = true;
|
||||
|
||||
vNode.suspenseDidCapture = true;
|
||||
launchUpdateFromVNode(vNode);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
vNode = vNode.parent;
|
||||
} while (vNode !== null);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function canCapturePromise(vNode: VNode): boolean {
|
||||
return vNode.suspenseChildStatus !== SuspenseChildStatus.ShowFallback && vNode.props.fallback !== undefined;
|
||||
}
|
||||
|
||||
const PossiblyWeakSet = typeof WeakSet === 'function' ? WeakSet : Set;
|
||||
|
||||
// 对于每个promise,添加一个侦听器,以便当它resolve时,重新渲染
|
||||
export function listenToPromise(suspenseVNode: VNode) {
|
||||
const promises: Set<PromiseType<any>> | null = suspenseVNode.suspensePromises;
|
||||
if (promises !== null) {
|
||||
suspenseVNode.suspensePromises = null;
|
||||
|
||||
// 记录已经监听的 promise
|
||||
let promiseCache = suspenseVNode.realNode;
|
||||
if (promiseCache === null) {
|
||||
// @ts-ignore
|
||||
promiseCache = suspenseVNode.realNode = new PossiblyWeakSet();
|
||||
}
|
||||
|
||||
promises.forEach(promise => {
|
||||
const resole = resolvePromise.bind(null, suspenseVNode, promise);
|
||||
if (!promiseCache.has(promise)) {
|
||||
promiseCache.add(promise);
|
||||
// 监听promise
|
||||
promise.then(resole, resole);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function resolvePromise(suspenseVNode: VNode, promise: PromiseType<any>) {
|
||||
const promiseCache = suspenseVNode.realNode;
|
||||
if (promiseCache !== null) {
|
||||
promiseCache.delete(promise);
|
||||
}
|
||||
suspenseVNode.promiseResolve = true;
|
||||
const root = updateShouldUpdateOfTree(suspenseVNode);
|
||||
if (root !== null) {
|
||||
tryRenderRoot(root);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
import type {VNode} from '../Types';
|
||||
import {throwIfTrue} from '../utils/throwIfTrue';
|
||||
import {processUpdates} from '../UpdateHandler';
|
||||
import {createVNodeChildren} from './BaseComponent';
|
||||
import {resetNamespaceCtx, setNamespaceCtx} from '../ContextSaver';
|
||||
import {resetOldCtx} from '../components/context/CompatibleContext';
|
||||
import {onlyUpdateChildVNodes} from '../vnode/VNodeCreator';
|
||||
|
||||
export function captureRender(processing: VNode): VNode | null {
|
||||
return updateTreeRoot(processing);
|
||||
}
|
||||
|
||||
export function bubbleRender(processing: VNode) {
|
||||
resetNamespaceCtx(processing);
|
||||
resetOldCtx(processing);
|
||||
}
|
||||
|
||||
function updateTreeRoot(processing) {
|
||||
setNamespaceCtx(processing, processing.outerDom);
|
||||
|
||||
const updates = processing.updates;
|
||||
throwIfTrue(
|
||||
processing.isCreated || updates === null,
|
||||
'If the root does not have an updates, we should have already ' +
|
||||
'bailed out. This error is likely caused by a bug. Please ' +
|
||||
'file an issue.',
|
||||
);
|
||||
|
||||
const newProps = processing.props;
|
||||
const oldState = processing.state;
|
||||
const oldElement = oldState !== null ? oldState.element : null;
|
||||
processUpdates(processing, null, newProps);
|
||||
|
||||
const newState = processing.state;
|
||||
// 为了保持对Dev Tools的兼容,这里还是使用element
|
||||
const newElement = newState.element;
|
||||
if (newElement === oldElement) {
|
||||
return onlyUpdateChildVNodes(processing);
|
||||
}
|
||||
processing.children = createVNodeChildren(processing, newElement);
|
||||
return processing.children;
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
import * as BaseComponentRender from './BaseComponent';
|
||||
import * as ClassComponentRender from './ClassComponent';
|
||||
import * as ContextConsumerRender from './ContextConsumer';
|
||||
import * as ContextProviderRender from './ContextProvider';
|
||||
import * as ForwardRefRender from './ForwardRef';
|
||||
import * as FragmentRender from './Fragment';
|
||||
import * as FunctionComponentRender from './FunctionComponent';
|
||||
import * as DomComponentRender from './DomComponent';
|
||||
import * as DomPortalRender from './DomPortal';
|
||||
import * as TreeRootRender from './TreeRoot';
|
||||
import * as DomTextRender from './DomText';
|
||||
import * as IncompleteClassComponentRender from './IncompleteClassComponent';
|
||||
import * as ClsOrFunComponentRender from './ClsOrFunComponent';
|
||||
import * as LazyComponentRender from './LazyComponent';
|
||||
import * as MemoComponentRender from './MemoComponent';
|
||||
import * as SuspenseComponentRender from './SuspenseComponent';
|
||||
|
||||
import {
|
||||
ClassComponent,
|
||||
ContextConsumer,
|
||||
ContextProvider,
|
||||
ForwardRef,
|
||||
Fragment,
|
||||
FunctionComponent,
|
||||
DomComponent,
|
||||
DomPortal,
|
||||
TreeRoot,
|
||||
DomText,
|
||||
IncompleteClassComponent,
|
||||
ClsOrFunComponent,
|
||||
LazyComponent,
|
||||
MemoComponent,
|
||||
SuspenseComponent,
|
||||
} from '../vnode/VNodeTags';
|
||||
|
||||
export {
|
||||
BaseComponentRender
|
||||
}
|
||||
|
||||
export default {
|
||||
[ClassComponent]: ClassComponentRender,
|
||||
[ContextConsumer]: ContextConsumerRender,
|
||||
[ContextProvider]: ContextProviderRender,
|
||||
[ForwardRef]: ForwardRefRender,
|
||||
[Fragment]: FragmentRender,
|
||||
[FunctionComponent]: FunctionComponentRender,
|
||||
[DomComponent]: DomComponentRender,
|
||||
[DomPortal]: DomPortalRender,
|
||||
[TreeRoot]: TreeRootRender,
|
||||
[DomText]: DomTextRender,
|
||||
[IncompleteClassComponent]: IncompleteClassComponentRender,
|
||||
[ClsOrFunComponent]: ClsOrFunComponentRender,
|
||||
[LazyComponent]: LazyComponentRender,
|
||||
[MemoComponent]: MemoComponentRender,
|
||||
[SuspenseComponent]: SuspenseComponentRender,
|
||||
}
|
Loading…
Reference in New Issue