Match-id-4e86fb01997446c1652d23ba021eb495a4df4d0f
This commit is contained in:
commit
90a8629dba
|
@ -0,0 +1,161 @@
|
||||||
|
/**
|
||||||
|
* useEffect 和 useLayoutEffect的执行逻辑
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type {VNode} from '../Types';
|
||||||
|
import type {
|
||||||
|
Effect as HookEffect,
|
||||||
|
EffectList,
|
||||||
|
} from '../hooks/HookType';
|
||||||
|
import {
|
||||||
|
callRenderQueueImmediate,
|
||||||
|
} from '../taskExecutor/RenderQueue';
|
||||||
|
import {runAsync} from '../taskExecutor/TaskExecutor';
|
||||||
|
import {
|
||||||
|
copyExecuteMode, InRender, setExecuteMode,changeMode
|
||||||
|
} from '../ExecuteMode';
|
||||||
|
import {handleSubmitError} from '../ErrorHandler';
|
||||||
|
import {clearDirtyNodes} from './Submit';
|
||||||
|
import {EffectConstant} from '../hooks/EffectConstant';
|
||||||
|
|
||||||
|
let hookEffects: Array<HookEffect | VNode> = [];
|
||||||
|
let hookRemoveEffects: Array<HookEffect | VNode> = [];
|
||||||
|
// 是否正在异步调度effects
|
||||||
|
let isScheduling: boolean = false;
|
||||||
|
let hookEffectRoot: VNode | null = null;
|
||||||
|
|
||||||
|
export function setSchedulingEffects(value) {
|
||||||
|
isScheduling = value;
|
||||||
|
}
|
||||||
|
export function isSchedulingEffects() {
|
||||||
|
return isScheduling;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setHookEffectRoot(root: VNode | null) {
|
||||||
|
hookEffectRoot = root;
|
||||||
|
}
|
||||||
|
export function getHookEffectRoot() {
|
||||||
|
return hookEffectRoot;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function callUseEffects(vNode: VNode) {
|
||||||
|
const effectList: EffectList = vNode.effectList;
|
||||||
|
|
||||||
|
effectList.forEach(effect => {
|
||||||
|
const {effectConstant} = effect;
|
||||||
|
if (
|
||||||
|
(effectConstant & EffectConstant.Effect) !== EffectConstant.NoEffect &&
|
||||||
|
(effectConstant & EffectConstant.DepsChange) !== EffectConstant.NoEffect
|
||||||
|
) {
|
||||||
|
hookEffects.push({effect, vNode});
|
||||||
|
hookRemoveEffects.push({effect, vNode});
|
||||||
|
|
||||||
|
// 异步调用
|
||||||
|
if (!isScheduling) {
|
||||||
|
isScheduling = true;
|
||||||
|
runAsync(runAsyncEffects);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function runAsyncEffects() {
|
||||||
|
if (hookEffectRoot === null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const root = hookEffectRoot;
|
||||||
|
hookEffectRoot = null;
|
||||||
|
|
||||||
|
const preMode = copyExecuteMode();
|
||||||
|
changeMode(InRender, true);
|
||||||
|
|
||||||
|
// 调用effect destroy
|
||||||
|
const removeEffects = hookRemoveEffects;
|
||||||
|
hookRemoveEffects = [];
|
||||||
|
removeEffects.forEach(({effect, vNode}) => {
|
||||||
|
const destroy = effect.removeEffect;
|
||||||
|
effect.removeEffect = undefined;
|
||||||
|
|
||||||
|
if (typeof destroy === 'function') {
|
||||||
|
try {
|
||||||
|
destroy();
|
||||||
|
} catch (error) {
|
||||||
|
handleSubmitError(vNode, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 调用effect create
|
||||||
|
const createEffects = hookEffects;
|
||||||
|
hookEffects = [];
|
||||||
|
createEffects.forEach(({effect, vNode}) => {
|
||||||
|
try {
|
||||||
|
const create = effect.effect;
|
||||||
|
|
||||||
|
effect.removeEffect = create();
|
||||||
|
} catch (error) {
|
||||||
|
handleSubmitError(vNode, error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 清理dirtyNodes
|
||||||
|
clearDirtyNodes(root.dirtyNodes);
|
||||||
|
|
||||||
|
setExecuteMode(preMode);
|
||||||
|
|
||||||
|
callRenderQueueImmediate();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在销毁vNode的时候调用remove
|
||||||
|
export function callEffectRemove(vNode: VNode) {
|
||||||
|
const effectList: EffectList = vNode.effectList;
|
||||||
|
|
||||||
|
effectList.forEach(effect => {
|
||||||
|
const {removeEffect, effectConstant} = effect;
|
||||||
|
|
||||||
|
if (removeEffect !== undefined) {
|
||||||
|
if ((effectConstant & EffectConstant.Effect) !== EffectConstant.NoEffect) { // 如果是useEffect,就异步调用
|
||||||
|
hookRemoveEffects.push({effect, vNode});
|
||||||
|
|
||||||
|
if (!isScheduling) {
|
||||||
|
isScheduling = true;
|
||||||
|
runAsync(runAsyncEffects);
|
||||||
|
}
|
||||||
|
} else { // 是useLayoutEffect,直接执行
|
||||||
|
removeEffect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 同步执行UseLayoutEffect的remove
|
||||||
|
export function callUseLayoutEffectRemove(vNode: VNode) {
|
||||||
|
const effectList: EffectList = vNode.effectList;
|
||||||
|
|
||||||
|
const layoutLabel = EffectConstant.LayoutEffect | EffectConstant.DepsChange;
|
||||||
|
effectList.forEach(item => {
|
||||||
|
if ((item.effectConstant & layoutLabel) === layoutLabel) {
|
||||||
|
const remove = item.removeEffect;
|
||||||
|
item.removeEffect = undefined;
|
||||||
|
if (typeof remove === 'function') {
|
||||||
|
remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 同步执行UseLayoutEffect
|
||||||
|
export function callUseLayoutEffectCreate(vNode: VNode) {
|
||||||
|
const effectList: EffectList = vNode.effectList;
|
||||||
|
|
||||||
|
const layoutLabel = EffectConstant.LayoutEffect | EffectConstant.DepsChange;
|
||||||
|
effectList.forEach(item => {
|
||||||
|
if ((item.effectConstant & layoutLabel) === layoutLabel) {
|
||||||
|
const create = item.effect;
|
||||||
|
item.removeEffect = create();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,359 @@
|
||||||
|
/**
|
||||||
|
* 该文件负责把更新应用到界面上 以及 和生命周期的相关调用
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type {Container} from '../../dom/DOMOperator';
|
||||||
|
import type {RefType, VNode} from '../Types';
|
||||||
|
|
||||||
|
import {listenToPromise, SuspenseChildStatus} from '../render/SuspenseComponent';
|
||||||
|
import {
|
||||||
|
FunctionComponent,
|
||||||
|
ForwardRef,
|
||||||
|
ClassComponent,
|
||||||
|
TreeRoot,
|
||||||
|
DomComponent,
|
||||||
|
DomText,
|
||||||
|
DomPortal,
|
||||||
|
SuspenseComponent,
|
||||||
|
MemoComponent,
|
||||||
|
} from '../vnode/VNodeTags';
|
||||||
|
import {FlagUtils, ResetText} from '../vnode/VNodeFlags';
|
||||||
|
import {mergeDefaultProps} from '../render/LazyComponent';
|
||||||
|
import {
|
||||||
|
submitDomUpdate,
|
||||||
|
clearText,
|
||||||
|
appendChildElement,
|
||||||
|
insertDomBefore,
|
||||||
|
removeChildDom,
|
||||||
|
hideDom,
|
||||||
|
unHideDom,
|
||||||
|
clearContainer,
|
||||||
|
} from '../../dom/DOMOperator';
|
||||||
|
import {callEffectRemove, callUseEffects, callUseLayoutEffectCreate, callUseLayoutEffectRemove} from './HookEffectHandler';
|
||||||
|
import {handleSubmitError} from '../ErrorHandler';
|
||||||
|
import {
|
||||||
|
travelVNodeTree,
|
||||||
|
clearVNode,
|
||||||
|
isDomVNode,
|
||||||
|
findDomParent, getSiblingDom,
|
||||||
|
} from '../vnode/VNodeUtils';
|
||||||
|
import {shouldAutoFocus} from '../../dom/utils/Common';
|
||||||
|
|
||||||
|
function callComponentWillUnmount(vNode: VNode, instance: any) {
|
||||||
|
try {
|
||||||
|
instance.componentWillUnmount();
|
||||||
|
} catch (error) {
|
||||||
|
handleSubmitError(vNode, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用界面变化前的生命周期
|
||||||
|
function callBeforeSubmitLifeCycles(
|
||||||
|
vNode: VNode,
|
||||||
|
): void {
|
||||||
|
switch (vNode.tag) {
|
||||||
|
case ClassComponent: { // 调用instance.getSnapshotBeforeUpdate
|
||||||
|
if (!vNode.isCreated) {
|
||||||
|
const prevProps = vNode.isLazyComponent
|
||||||
|
? mergeDefaultProps(vNode.type, vNode.oldProps)
|
||||||
|
: vNode.oldProps;
|
||||||
|
const prevState = vNode.oldState;
|
||||||
|
const instance = vNode.realNode;
|
||||||
|
|
||||||
|
const snapshot = instance.getSnapshotBeforeUpdate(prevProps, prevState);
|
||||||
|
|
||||||
|
// __snapshotResult会在调用componentDidUpdate的时候作为第三个参数
|
||||||
|
instance.__snapshotResult = snapshot;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case TreeRoot: {
|
||||||
|
const root = vNode.realNode;
|
||||||
|
clearContainer(root.outerDom);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用vNode.stateCallbacks
|
||||||
|
function callStateCallback(vNode: VNode, obj: any): void {
|
||||||
|
const stateCallbacks = vNode.stateCallbacks;
|
||||||
|
vNode.stateCallbacks = [];
|
||||||
|
|
||||||
|
stateCallbacks.forEach(callback => {
|
||||||
|
if (typeof callback === 'function') {
|
||||||
|
callback.call(obj);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用界面变化后的生命周期
|
||||||
|
function callAfterSubmitLifeCycles(
|
||||||
|
vNode: VNode,
|
||||||
|
): void {
|
||||||
|
switch (vNode.tag) {
|
||||||
|
case FunctionComponent:
|
||||||
|
case ForwardRef: {
|
||||||
|
// 执行useLayoutEffect的create方法
|
||||||
|
callUseLayoutEffectCreate(vNode);
|
||||||
|
|
||||||
|
callUseEffects(vNode);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case ClassComponent: {
|
||||||
|
const instance = vNode.realNode;
|
||||||
|
if (vNode.flags.Update) {
|
||||||
|
if (vNode.isCreated) {
|
||||||
|
instance.componentDidMount();
|
||||||
|
} else {
|
||||||
|
const prevProps =
|
||||||
|
vNode.isLazyComponent
|
||||||
|
? mergeDefaultProps(vNode.type, vNode.oldProps)
|
||||||
|
: vNode.oldProps;
|
||||||
|
const prevState = vNode.oldState;
|
||||||
|
|
||||||
|
instance.componentDidUpdate(
|
||||||
|
prevProps,
|
||||||
|
prevState,
|
||||||
|
instance.__snapshotResult,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
callStateCallback(vNode, instance);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case TreeRoot: {
|
||||||
|
const childVNode = (vNode.children && vNode.children.length) ? vNode.children[0] : null;
|
||||||
|
const instance = childVNode !== null ? childVNode.realNode : null;
|
||||||
|
callStateCallback(vNode, instance);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case DomComponent: {
|
||||||
|
if (vNode.isCreated && vNode.flags.Update) {
|
||||||
|
// button、input、select、textarea、如果有 autoFocus 属性需要focus
|
||||||
|
if (shouldAutoFocus(vNode.type, vNode.props)) {
|
||||||
|
// button、input、select、textarea、如果有 autoFocus 属性需要focus
|
||||||
|
vNode.realNode.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideOrUnhideAllChildren(vNode, isHidden) {
|
||||||
|
travelVNodeTree(vNode, (node: VNode) => {
|
||||||
|
const instance = node.realNode;
|
||||||
|
|
||||||
|
if (node.tag === DomComponent || node.tag === DomText) {
|
||||||
|
if (isHidden) {
|
||||||
|
hideDom(node.tag, instance);
|
||||||
|
} else {
|
||||||
|
unHideDom(node.tag, instance, node.props);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function attachRef(vNode: VNode) {
|
||||||
|
const ref = vNode.ref;
|
||||||
|
if (ref !== null) {
|
||||||
|
const instance = vNode.realNode;
|
||||||
|
|
||||||
|
if (typeof ref === 'function') {
|
||||||
|
ref(instance);
|
||||||
|
} else {
|
||||||
|
(<RefType>ref).current = instance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function detachRef(vNode: VNode, isOldRef?: boolean) {
|
||||||
|
let ref = (isOldRef ? vNode.oldRef : vNode.ref);
|
||||||
|
|
||||||
|
if (ref !== null) {
|
||||||
|
if (typeof ref === 'function') {
|
||||||
|
try {
|
||||||
|
ref(null);
|
||||||
|
} catch (error) {
|
||||||
|
handleSubmitError(vNode, error);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
(<RefType>ref).current = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 卸载一个vNode,不会递归
|
||||||
|
function unmountVNode(vNode: VNode): void {
|
||||||
|
// TODO 暂时用于规避error处理逻辑,后续删除
|
||||||
|
if (vNode.flags.Addition) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (vNode.tag) {
|
||||||
|
case FunctionComponent:
|
||||||
|
case ForwardRef:
|
||||||
|
case MemoComponent: {
|
||||||
|
callEffectRemove(vNode);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ClassComponent: {
|
||||||
|
detachRef(vNode);
|
||||||
|
|
||||||
|
const instance = vNode.realNode;
|
||||||
|
// 当constructor中抛出异常时,instance会是null,这里判断一下instance是否为空
|
||||||
|
if (instance && typeof instance.componentWillUnmount === 'function') {
|
||||||
|
callComponentWillUnmount(vNode, instance);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DomComponent: {
|
||||||
|
detachRef(vNode);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DomPortal: {
|
||||||
|
// 这里会递归
|
||||||
|
unmountDomComponents(vNode);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 卸载vNode,递归遍历子vNode
|
||||||
|
function unmountNestedVNodes(vNode: VNode): void {
|
||||||
|
travelVNodeTree(vNode, (node) => {
|
||||||
|
unmountVNode(node);
|
||||||
|
}, (node) => {
|
||||||
|
// 如果是DomPortal,不需要遍历child
|
||||||
|
return node.tag === DomPortal;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function submitAddition(vNode: VNode): void {
|
||||||
|
const {parent, parentDom} = findDomParent(vNode);
|
||||||
|
|
||||||
|
if (parent.flags.ResetText) {
|
||||||
|
// 在insert之前先reset
|
||||||
|
clearText(parentDom);
|
||||||
|
FlagUtils.removeFlag(parent, ResetText);
|
||||||
|
}
|
||||||
|
|
||||||
|
const before = getSiblingDom(vNode);
|
||||||
|
insertOrAppendPlacementNode(vNode, before, parentDom);
|
||||||
|
}
|
||||||
|
|
||||||
|
function insertOrAppendPlacementNode(
|
||||||
|
node: VNode,
|
||||||
|
beforeDom: Element | null,
|
||||||
|
parent: Element | Container,
|
||||||
|
): void {
|
||||||
|
const {tag, realNode} = node;
|
||||||
|
|
||||||
|
if (isDomVNode(node)) {
|
||||||
|
if (beforeDom) {
|
||||||
|
insertDomBefore(parent, realNode, beforeDom);
|
||||||
|
} else {
|
||||||
|
appendChildElement(parent, realNode);
|
||||||
|
}
|
||||||
|
} else if (tag === DomPortal) {
|
||||||
|
// 这里不做处理,直接在portal中处理
|
||||||
|
} else {
|
||||||
|
// 插入子节点们
|
||||||
|
const children = node.children || [];
|
||||||
|
children.forEach(child => {
|
||||||
|
insertOrAppendPlacementNode(child, beforeDom, parent);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 遍历所有子节点:删除dom节点,detach ref 和 调用componentWillUnmount()
|
||||||
|
function unmountDomComponents(vNode: VNode): void {
|
||||||
|
let currentParentIsValid = false;
|
||||||
|
|
||||||
|
// 这两个变量要一起更新
|
||||||
|
let currentParent;
|
||||||
|
|
||||||
|
travelVNodeTree(vNode, (node) => {
|
||||||
|
if (!currentParentIsValid) {
|
||||||
|
const parentObj = findDomParent(node);
|
||||||
|
currentParent = parentObj.parentDom;
|
||||||
|
currentParentIsValid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.tag === DomComponent || node.tag === DomText) {
|
||||||
|
// 卸载vNode,递归遍历子vNode
|
||||||
|
unmountNestedVNodes(node);
|
||||||
|
|
||||||
|
// 在所有子项都卸载后,删除dom树中的节点
|
||||||
|
removeChildDom(currentParent, node.realNode);
|
||||||
|
} else if (node.tag === DomPortal) {
|
||||||
|
if (node.children.length) {
|
||||||
|
currentParent = node.outerDom;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unmountVNode(node);
|
||||||
|
}
|
||||||
|
}, (node) => {
|
||||||
|
// 如果是dom不用再遍历child
|
||||||
|
return node.tag === DomComponent || node.tag === DomText;
|
||||||
|
}, null, (node) => {
|
||||||
|
if (node.tag === DomPortal) {
|
||||||
|
// 当离开portal,需要重新设置parent
|
||||||
|
currentParentIsValid = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function submitDeletion(vNode: VNode): void {
|
||||||
|
// 遍历所有子节点:删除dom节点,detach ref 和 调用componentWillUnmount()
|
||||||
|
unmountDomComponents(vNode);
|
||||||
|
|
||||||
|
// 置空vNode
|
||||||
|
clearVNode(vNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
function submitUpdate(vNode: VNode): void {
|
||||||
|
switch (vNode.tag) {
|
||||||
|
case FunctionComponent:
|
||||||
|
case ForwardRef:
|
||||||
|
case MemoComponent: {
|
||||||
|
// 执行useLayoutEffect的remove方法
|
||||||
|
callUseLayoutEffectRemove(vNode);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DomComponent:
|
||||||
|
case DomText: {
|
||||||
|
submitDomUpdate(vNode.tag, vNode);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SuspenseComponent: {
|
||||||
|
submitSuspenseComponent(vNode);
|
||||||
|
listenToPromise(vNode);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function submitSuspenseComponent(finishedWork: VNode) {
|
||||||
|
const suspenseChildStatus = finishedWork.suspenseChildStatus;
|
||||||
|
if (suspenseChildStatus !== SuspenseChildStatus.Init) {
|
||||||
|
hideOrUnhideAllChildren(finishedWork.children[0], suspenseChildStatus === SuspenseChildStatus.ShowFallback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function submitResetTextContent(vNode: VNode) {
|
||||||
|
clearText(vNode.realNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
callBeforeSubmitLifeCycles,
|
||||||
|
submitResetTextContent,
|
||||||
|
submitAddition,
|
||||||
|
submitDeletion,
|
||||||
|
submitUpdate,
|
||||||
|
callAfterSubmitLifeCycles,
|
||||||
|
attachRef,
|
||||||
|
detachRef,
|
||||||
|
};
|
|
@ -0,0 +1,207 @@
|
||||||
|
import type {VNode} from '../Types';
|
||||||
|
|
||||||
|
import {callRenderQueueImmediate} from '../taskExecutor/RenderQueue';
|
||||||
|
import {throwIfTrue} from '../utils/throwIfTrue';
|
||||||
|
import {FlagUtils, Addition as AdditionFlag} from '../vnode/VNodeFlags';
|
||||||
|
import {prepareForSubmit, resetAfterSubmit} from '../../dom/DOMOperator';
|
||||||
|
import {handleSubmitError} from '../ErrorHandler';
|
||||||
|
import {
|
||||||
|
attachRef,
|
||||||
|
callAfterSubmitLifeCycles,
|
||||||
|
callBeforeSubmitLifeCycles, submitDeletion, submitAddition,
|
||||||
|
submitResetTextContent, submitUpdate, detachRef,
|
||||||
|
} from './LifeCycleHandler';
|
||||||
|
import {tryRenderRoot, setProcessing, getStartVNode} from '../TreeBuilder';
|
||||||
|
import {
|
||||||
|
BySync,
|
||||||
|
InRender,
|
||||||
|
copyExecuteMode,
|
||||||
|
setExecuteMode,
|
||||||
|
checkMode,
|
||||||
|
changeMode,
|
||||||
|
} from '../ExecuteMode';
|
||||||
|
import {
|
||||||
|
isSchedulingEffects,
|
||||||
|
setSchedulingEffects, setHookEffectRoot,
|
||||||
|
} from './HookEffectHandler';
|
||||||
|
|
||||||
|
let rootThrowError = null;
|
||||||
|
|
||||||
|
// 防止死循环调用update
|
||||||
|
const LOOPING_UPDATE_LIMIT = 50;
|
||||||
|
let loopingUpdateCount: number = 0;
|
||||||
|
let lastRoot: VNode | null = null;
|
||||||
|
|
||||||
|
export function submitToRender(treeRoot) {
|
||||||
|
treeRoot.shouldUpdate = treeRoot.childShouldUpdate;
|
||||||
|
|
||||||
|
const startVNode = getStartVNode();
|
||||||
|
|
||||||
|
// 置空task,让才能加入新的render任务
|
||||||
|
treeRoot.task = null;
|
||||||
|
|
||||||
|
setProcessing(null);
|
||||||
|
|
||||||
|
if (FlagUtils.hasAnyFlag(startVNode)) {
|
||||||
|
// 把自己加上
|
||||||
|
startVNode.dirtyNodes.push(startVNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
const dirtyNodes = startVNode.dirtyNodes;
|
||||||
|
if (dirtyNodes.length) {
|
||||||
|
const preMode = copyExecuteMode();
|
||||||
|
changeMode(InRender, true);
|
||||||
|
|
||||||
|
prepareForSubmit();
|
||||||
|
// before submit阶段
|
||||||
|
beforeSubmit(dirtyNodes);
|
||||||
|
|
||||||
|
// submit阶段
|
||||||
|
submit(dirtyNodes);
|
||||||
|
|
||||||
|
resetAfterSubmit();
|
||||||
|
|
||||||
|
// after submit阶段
|
||||||
|
afterSubmit(dirtyNodes);
|
||||||
|
|
||||||
|
setExecuteMode(preMode)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSchedulingEffects()) {
|
||||||
|
setSchedulingEffects(false);
|
||||||
|
|
||||||
|
// 记录root,说明这个root有副作用要执行
|
||||||
|
setHookEffectRoot(treeRoot);
|
||||||
|
} else {
|
||||||
|
clearDirtyNodes(dirtyNodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 统计root同步重渲染的次数,如果太多可能是无线循环
|
||||||
|
countLoopingUpdate(treeRoot);
|
||||||
|
|
||||||
|
// 在退出`submit` 之前始终调用此函数,以确保任何已计划在此根上执行的update被执行。
|
||||||
|
tryRenderRoot(treeRoot);
|
||||||
|
|
||||||
|
if (rootThrowError) {
|
||||||
|
const error = rootThrowError;
|
||||||
|
rootThrowError = null;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 非批量:即同步执行的,没有必要去执行RenderQueue,RenderQueue放的是异步的
|
||||||
|
if (!checkMode(BySync)) { // 非批量
|
||||||
|
callRenderQueueImmediate();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function beforeSubmit(dirtyNodes: Array<VNode>) {
|
||||||
|
dirtyNodes.forEach(node => {
|
||||||
|
try {
|
||||||
|
if (node.flags.Snapshot) {
|
||||||
|
callBeforeSubmitLifeCycles(node);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
throwIfTrue(node === null, 'Should be working on an effect.');
|
||||||
|
handleSubmitError(node, error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function submit(dirtyNodes: Array<VNode>) {
|
||||||
|
dirtyNodes.forEach(node => {
|
||||||
|
try {
|
||||||
|
if (node.flags.ResetText) {
|
||||||
|
submitResetTextContent(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.flags.Ref) {
|
||||||
|
if (!node.isCreated) {
|
||||||
|
// 需要执行
|
||||||
|
detachRef(node, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const {Addition, Update, Deletion} = node.flags;
|
||||||
|
if (Addition && Update) {
|
||||||
|
// Addition
|
||||||
|
submitAddition(node);
|
||||||
|
FlagUtils.removeFlag(node, AdditionFlag);
|
||||||
|
|
||||||
|
// Update
|
||||||
|
submitUpdate(node);
|
||||||
|
} else {
|
||||||
|
if (Addition) {
|
||||||
|
submitAddition(node);
|
||||||
|
FlagUtils.removeFlag(node, AdditionFlag);
|
||||||
|
} else if (Update) {
|
||||||
|
submitUpdate(node);
|
||||||
|
} else if (Deletion) {
|
||||||
|
submitDeletion(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
throwIfTrue(node === null, 'Should be working on an effect.');
|
||||||
|
handleSubmitError(node, error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function afterSubmit(dirtyNodes: Array<VNode>) {
|
||||||
|
dirtyNodes.forEach(node => {
|
||||||
|
try {
|
||||||
|
if (node.flags.Update || node.flags.Callback) {
|
||||||
|
callAfterSubmitLifeCycles(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.flags.Ref) {
|
||||||
|
attachRef(node);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
throwIfTrue(node === null, 'Should be working on an effect.');
|
||||||
|
handleSubmitError(node, error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setRootThrowError(error: any) {
|
||||||
|
if (!rootThrowError) {
|
||||||
|
rootThrowError = error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 统计root同步重渲染的次数,如果太多可能是无限循环
|
||||||
|
function countLoopingUpdate(root: VNode) {
|
||||||
|
if (root.shouldUpdate) {
|
||||||
|
if (root === lastRoot) {
|
||||||
|
loopingUpdateCount++;
|
||||||
|
} else {
|
||||||
|
loopingUpdateCount = 0;
|
||||||
|
lastRoot = root;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
loopingUpdateCount = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function checkLoopingUpdateLimit() {
|
||||||
|
if (loopingUpdateCount > LOOPING_UPDATE_LIMIT) {
|
||||||
|
loopingUpdateCount = 0;
|
||||||
|
lastRoot = null;
|
||||||
|
|
||||||
|
throw Error(
|
||||||
|
`The number of updates exceeds the upper limit ${LOOPING_UPDATE_LIMIT}.
|
||||||
|
A component maybe repeatedly invokes setState on componentWillUpdate or componentDidUpdate.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清理dirtyNodes
|
||||||
|
export function clearDirtyNodes(dirtyNodes) {
|
||||||
|
dirtyNodes.forEach(node => {
|
||||||
|
if (node.flags.Deletion) {
|
||||||
|
node.realNode = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in New Issue