Match-id-4e86fb01997446c1652d23ba021eb495a4df4d0f

This commit is contained in:
* 2021-12-23 11:01:28 +08:00 committed by *
commit 90a8629dba
3 changed files with 727 additions and 0 deletions

View File

@ -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();
}
});
}

View File

@ -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,
};

View File

@ -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;
}
// 非批量即同步执行的没有必要去执行RenderQueueRenderQueue放的是异步的
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;
}
});
}