Match-id-6bc4f99ab260e07592cabc5ef99f2985d3befb0e
This commit is contained in:
parent
6d5f3c6888
commit
d217302bf1
|
@ -0,0 +1,147 @@
|
|||
/**
|
||||
* 保存与深度遍历相关的一些context。
|
||||
* 在深度遍历过程中,begin阶段会修改一些全局的值,在complete阶段会恢复。
|
||||
*/
|
||||
|
||||
import type {VNode, ContextType} from './Types';
|
||||
import type {Container} from '../dom/DOMOperator';
|
||||
|
||||
import {getNSCtx} from '../dom/DOMOperator';
|
||||
import {ContextProvider} from './vnode/VNodeTags';
|
||||
|
||||
// 保存的是“http://www.w3.org/1999/xhtml”或“http://www.w3.org/2000/svg”,
|
||||
// 用于识别是使用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';
|
||||
// 旧版context API,保存的是的当前组件提供给子组件使用的context。
|
||||
const CTX_OLD_CONTEXT = 'CTX_OLD_CONTEXT';
|
||||
// 旧版context API,保存的是的上一个提供者提供给后代组件使用的context。
|
||||
const CTX_OLD_PREVIOUS_CONTEXT = 'CTX_OLD_PREVIOUS_CONTEXT';
|
||||
|
||||
let ctxNamespace: string = '';
|
||||
|
||||
let ctxOldContext: Object = {};
|
||||
let ctxOldChange: Boolean = false;
|
||||
let ctxOldPreviousContext: Object = {};
|
||||
|
||||
// capture阶段设置
|
||||
function setNamespaceCtx(vNode: VNode, dom?: Container) {
|
||||
const nextContext = getNSCtx(dom, ctxNamespace, vNode.type);
|
||||
|
||||
vNode.setContext(CTX_NAMESPACE, ctxNamespace);
|
||||
ctxNamespace = nextContext;
|
||||
}
|
||||
|
||||
// bubble阶段恢复
|
||||
function resetNamespaceCtx(vNode: VNode) {
|
||||
ctxNamespace = vNode.getContext(CTX_NAMESPACE);
|
||||
}
|
||||
|
||||
function getNamespaceCtx(): string {
|
||||
return ctxNamespace;
|
||||
}
|
||||
|
||||
function setContextCtx<T>(providerVNode: VNode, nextValue: T) {
|
||||
const context: ContextType<T> = providerVNode.type._context;
|
||||
|
||||
providerVNode.setContext(CTX_CONTEXT, context.value);
|
||||
context.value = nextValue;
|
||||
}
|
||||
|
||||
function resetContextCtx(providerVNode: VNode) {
|
||||
const context: ContextType<any> = providerVNode.type._context;
|
||||
|
||||
context.value = providerVNode.getContext(CTX_CONTEXT);
|
||||
}
|
||||
|
||||
// 在局部更新时,恢复父节点的context
|
||||
function recoverParentsContextCtx(vNode: VNode) {
|
||||
let parent = vNode.parent;
|
||||
|
||||
while (parent !== null) {
|
||||
if (parent.tag === ContextProvider) {
|
||||
const newValue = parent.props.value;
|
||||
setContextCtx(parent, newValue);
|
||||
}
|
||||
parent = parent.parent;
|
||||
}
|
||||
}
|
||||
|
||||
// ctxOldContext是 旧context提供者的context
|
||||
function setVNodeOldContext(providerVNode: VNode, context: Object) {
|
||||
providerVNode.setContext(CTX_OLD_CONTEXT, context);
|
||||
}
|
||||
|
||||
function getVNodeOldContext(vNode: VNode) {
|
||||
return vNode.getContext(CTX_OLD_CONTEXT);
|
||||
}
|
||||
|
||||
function setOldContextCtx(providerVNode: VNode, context: Object) {
|
||||
setVNodeOldContext(providerVNode, context);
|
||||
ctxOldContext = context;
|
||||
}
|
||||
|
||||
function getOldContextCtx() {
|
||||
return ctxOldContext;
|
||||
}
|
||||
|
||||
function resetOldContextCtx(vNode: VNode) {
|
||||
ctxOldContext = getVNodeOldContext(vNode);
|
||||
}
|
||||
|
||||
function setVNodeOldPreviousContext(providerVNode: VNode, context: Object) {
|
||||
providerVNode.setContext(CTX_OLD_PREVIOUS_CONTEXT, context);
|
||||
}
|
||||
|
||||
function getVNodeOldPreviousContext(vNode: VNode) {
|
||||
return vNode.getContext(CTX_OLD_PREVIOUS_CONTEXT);
|
||||
}
|
||||
|
||||
function setOldPreviousContextCtx(context: Object) {
|
||||
ctxOldPreviousContext = context;
|
||||
}
|
||||
|
||||
function getOldPreviousContextCtx() {
|
||||
return ctxOldPreviousContext;
|
||||
}
|
||||
|
||||
function setContextChangeCtx(providerVNode: VNode, didChange: boolean) {
|
||||
providerVNode.setContext(CTX_OLD_CHANGE, didChange);
|
||||
ctxOldChange = didChange;
|
||||
}
|
||||
|
||||
function getContextChangeCtx() {
|
||||
return ctxOldChange;
|
||||
}
|
||||
|
||||
function resetContextChangeCtx(vNode: VNode) {
|
||||
ctxOldChange = vNode.getContext(CTX_OLD_CHANGE);
|
||||
}
|
||||
|
||||
export {
|
||||
getNamespaceCtx,
|
||||
resetNamespaceCtx,
|
||||
setNamespaceCtx,
|
||||
setContextCtx,
|
||||
resetContextCtx,
|
||||
recoverParentsContextCtx,
|
||||
setOldContextCtx,
|
||||
getOldContextCtx,
|
||||
resetOldContextCtx,
|
||||
setContextChangeCtx,
|
||||
getContextChangeCtx,
|
||||
resetContextChangeCtx,
|
||||
setOldPreviousContextCtx,
|
||||
getOldPreviousContextCtx,
|
||||
setVNodeOldContext,
|
||||
getVNodeOldContext,
|
||||
setVNodeOldPreviousContext,
|
||||
getVNodeOldPreviousContext,
|
||||
};
|
||||
|
||||
|
|
@ -0,0 +1,189 @@
|
|||
/**
|
||||
* 异常错误处理
|
||||
*/
|
||||
|
||||
import type {VNode} from './Types';
|
||||
import type {Update} from './UpdateHandler';
|
||||
|
||||
import {ClassComponent, TreeRoot} from './vnode/VNodeTags';
|
||||
import {FlagUtils, Interrupted} from './vnode/VNodeFlags';
|
||||
import {newUpdate, UpdateState, pushUpdate} from './UpdateHandler';
|
||||
import {launchUpdateFromVNode, setBuildResultError, tryRenderRoot} from './TreeBuilder';
|
||||
import {setRootThrowError} from './submit/Submit';
|
||||
import {handleSuspenseChildThrowError} from './render/SuspenseComponent';
|
||||
import {updateShouldUpdateOfTree} from './vnode/VNodeShouldUpdate';
|
||||
|
||||
// 处理capture和bubble阶段抛出的错误
|
||||
export function handleRenderThrowError(
|
||||
sourceVNode: VNode,
|
||||
error: any,
|
||||
) {
|
||||
// vNode抛出了异常,标记Interrupted中断
|
||||
FlagUtils.markInterrupted(sourceVNode);
|
||||
// dirtyNodes 不再有效
|
||||
sourceVNode.dirtyNodes = [];
|
||||
|
||||
// error是个promise
|
||||
if (error !== null && typeof error === 'object' && typeof error.then === 'function') {
|
||||
// 抛出异常的节点,向上寻找,是否有suspense组件
|
||||
const foundSuspense = handleSuspenseChildThrowError(sourceVNode.parent, sourceVNode, error);
|
||||
if (foundSuspense) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 抛出错误无法作为suspense内容处理(或无suspense来处理),这次当成真的错误来处理
|
||||
setBuildResultError();
|
||||
|
||||
// 向上遍历寻找ClassComponent组件(同时也是Error Boundaries组件) 或者 TreeRoot
|
||||
let vNode = sourceVNode.parent;
|
||||
do {
|
||||
switch (vNode.tag) {
|
||||
case TreeRoot: {
|
||||
vNode.shouldUpdate = true;
|
||||
launchUpdateFromVNode(vNode);
|
||||
handleRootError(error);
|
||||
return;
|
||||
}
|
||||
case ClassComponent:
|
||||
const ctor = vNode.type;
|
||||
const instance = vNode.realNode;
|
||||
if (
|
||||
!vNode.flags.DidCapture &&
|
||||
(
|
||||
typeof ctor.getDerivedStateFromError === 'function' ||
|
||||
(instance !== null && typeof instance.componentDidCatch === 'function')
|
||||
)
|
||||
) {
|
||||
FlagUtils.markShouldCapture(vNode);
|
||||
|
||||
// Class捕捉到异常,触发一次刷新
|
||||
const update = createClassErrorUpdate(vNode, error);
|
||||
pushUpdate(vNode, update);
|
||||
|
||||
launchUpdateFromVNode(vNode);
|
||||
|
||||
// 有异常处理类,把抛出异常的节点的Interrupted标志去掉,继续走正常的绘制流程
|
||||
FlagUtils.removeFlag(sourceVNode, Interrupted);
|
||||
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
vNode = vNode.parent;
|
||||
} while (vNode !== null);
|
||||
}
|
||||
|
||||
// 处理submit阶段的异常
|
||||
export function handleSubmitError(vNode: VNode, error: any) {
|
||||
if (vNode.tag === TreeRoot) {
|
||||
handleRootError(error);
|
||||
return;
|
||||
}
|
||||
|
||||
let node = vNode.parent;
|
||||
// 向上遍历
|
||||
while (node !== null) {
|
||||
if (node.tag === TreeRoot) {
|
||||
handleRootError(error);
|
||||
return;
|
||||
} else if (node.tag === ClassComponent) { // 只有 class 组件才可以成为错误边界组件
|
||||
const ctor = node.type;
|
||||
const instance = node.realNode;
|
||||
if (
|
||||
typeof ctor.getDerivedStateFromError === 'function' ||
|
||||
typeof instance.componentDidCatch === 'function'
|
||||
) {
|
||||
const getDerivedStateFromError = node.type.getDerivedStateFromError;
|
||||
if (typeof getDerivedStateFromError === 'function') {
|
||||
// 打印错误
|
||||
consoleError(error);
|
||||
|
||||
const retState = getDerivedStateFromError(error);
|
||||
if (retState) { // 有返回值
|
||||
// 触发更新
|
||||
triggerUpdate(node, retState);
|
||||
}
|
||||
}
|
||||
|
||||
// 处理componentDidCatch
|
||||
if (instance !== null && typeof instance.componentDidCatch === 'function') {
|
||||
if (typeof getDerivedStateFromError !== 'function') { // 没有getDerivedStateFromError
|
||||
// 打印错误
|
||||
consoleError(error);
|
||||
}
|
||||
|
||||
instance.componentDidCatch(error, {
|
||||
componentStack: '',
|
||||
});
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
node = node.parent;
|
||||
}
|
||||
}
|
||||
|
||||
function createClassErrorUpdate(
|
||||
vNode: VNode,
|
||||
error: any,
|
||||
): Update {
|
||||
const update = newUpdate();
|
||||
update.type = UpdateState.Error;
|
||||
|
||||
const getDerivedStateFromError = vNode.type.getDerivedStateFromError;
|
||||
if (typeof getDerivedStateFromError === 'function') {
|
||||
update.content = () => {
|
||||
consoleError(error);
|
||||
return getDerivedStateFromError(error);
|
||||
};
|
||||
}
|
||||
|
||||
const inst = vNode.realNode;
|
||||
if (inst !== null && typeof inst.componentDidCatch === 'function') {
|
||||
update.callback = function callback() {
|
||||
if (typeof getDerivedStateFromError !== 'function') {
|
||||
// 打印错误
|
||||
consoleError(error);
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
this.componentDidCatch(error, {
|
||||
componentStack: '',
|
||||
});
|
||||
};
|
||||
}
|
||||
return update;
|
||||
}
|
||||
|
||||
// 新增一个update,并且触发调度
|
||||
function triggerUpdate(vNode, state) {
|
||||
const update = newUpdate();
|
||||
update.content = state;
|
||||
pushUpdate(vNode, update);
|
||||
|
||||
const root = updateShouldUpdateOfTree(vNode);
|
||||
if (root !== null) {
|
||||
tryRenderRoot(root);
|
||||
}
|
||||
}
|
||||
|
||||
function handleRootError(
|
||||
error: any,
|
||||
) {
|
||||
// 注意:如果根节点抛出错误,不会销毁整棵树,只打印日志,抛出异常。
|
||||
setRootThrowError(error);
|
||||
consoleError(error);
|
||||
}
|
||||
|
||||
function consoleError(error: any): void {
|
||||
if (isDev) {
|
||||
// 只打印message为了让测试用例能pass
|
||||
console['error']('The codes throw the error: ' + error.message);
|
||||
} else {
|
||||
console['error'](error);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
|
||||
export const ByAsync = 'BY_ASYNC';
|
||||
export const BySync = 'BY_SYNC';
|
||||
export const InRender = 'IN_RENDER';
|
||||
|
||||
type RenderMode = typeof ByAsync | typeof BySync | typeof InRender;
|
||||
|
||||
// 当前执行阶段标记
|
||||
let executeMode = {
|
||||
[ByAsync]: false,
|
||||
[BySync]: false,
|
||||
[InRender]: false,
|
||||
};
|
||||
|
||||
export function changeMode(mode: RenderMode, state = true) {
|
||||
executeMode[mode] = state;
|
||||
}
|
||||
|
||||
export function checkMode(mode: RenderMode) {
|
||||
return executeMode[mode];
|
||||
}
|
||||
|
||||
export function isExecuting() {
|
||||
return executeMode[ByAsync] || executeMode[BySync] || executeMode[InRender];
|
||||
}
|
||||
|
||||
export function copyExecuteMode() {
|
||||
return {...executeMode};
|
||||
}
|
||||
|
||||
export function setExecuteMode(mode: typeof executeMode) {
|
||||
executeMode = mode;
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
import type {VNode} from './Types';
|
||||
import type {Update} from './UpdateHandler';
|
||||
|
||||
import {
|
||||
asyncUpdates,
|
||||
syncUpdates,
|
||||
runDiscreteUpdates,
|
||||
launchUpdateFromVNode,
|
||||
} from './TreeBuilder';
|
||||
import {runAsyncEffects} from './submit/HookEffectHandler';
|
||||
import {Callback, newUpdate, pushUpdate} from './UpdateHandler';
|
||||
import {getFirstChild} from './vnode/VNodeUtils';
|
||||
|
||||
export {createVNode} from './vnode/VNodeCreator';
|
||||
export {createPortal} from './components/CreatePortal';
|
||||
export {
|
||||
asyncUpdates,
|
||||
syncUpdates,
|
||||
runDiscreteUpdates,
|
||||
runAsyncEffects,
|
||||
};
|
||||
|
||||
export function startUpdate(
|
||||
element: any,
|
||||
treeRoot: VNode,
|
||||
callback?: Callback,
|
||||
) {
|
||||
const update: Update = newUpdate();
|
||||
update.content = {element};
|
||||
|
||||
if (typeof callback === 'function') {
|
||||
update.callback = callback;
|
||||
}
|
||||
|
||||
pushUpdate(treeRoot, update);
|
||||
|
||||
launchUpdateFromVNode(treeRoot);
|
||||
}
|
||||
|
||||
export function getFirstCustomDom(treeRoot: VNode): Element | Text | null {
|
||||
const firstChild = getFirstChild(treeRoot);
|
||||
if (!firstChild) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return firstChild.realNode;
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
export { VNode } from './vnode/VNode';
|
||||
|
||||
type Trigger<A> = (A) => void;
|
||||
|
||||
export type ReadContextHookType = { readContext<T>(context: ContextType<T>): T };
|
||||
export type UseStateHookType = {
|
||||
useState<S>(
|
||||
initialState: (() => S) | S
|
||||
): [S, Trigger<((S) => S) | S>]
|
||||
};
|
||||
export type UseReducerHookType = {
|
||||
useReducer<S, P, A>(
|
||||
reducer: (S, A) => S,
|
||||
initArg: P, init?: (P) => S,
|
||||
): [S, Trigger<A>]
|
||||
};
|
||||
export type UseContextHookType = { useContext<T>(context: ContextType<T>,): T };
|
||||
|
||||
export type HorizonElement = {
|
||||
vtype: any,
|
||||
type: any,
|
||||
key: any,
|
||||
ref: any,
|
||||
props: any,
|
||||
|
||||
_vNode: any,
|
||||
};
|
||||
|
||||
export type ProviderType<T> = {
|
||||
vtype: number;
|
||||
_context: ContextType<T>;
|
||||
};
|
||||
|
||||
export type ContextType<T> = {
|
||||
vtype: number;
|
||||
Consumer: ContextType<T>;
|
||||
Provider: ProviderType<T>;
|
||||
value: T;
|
||||
};
|
||||
|
||||
export type PortalType = {
|
||||
vtype: number;
|
||||
key: null | string;
|
||||
outerDom: any;
|
||||
children: any;
|
||||
};
|
||||
|
||||
export type RefType = {
|
||||
current: any;
|
||||
};
|
||||
|
||||
export interface PromiseType<R> {
|
||||
then<U>(
|
||||
onFulfill: (value: R) => void | PromiseType<U> | U,
|
||||
onReject: (error: any) => void | PromiseType<U> | U,
|
||||
): void | PromiseType<U>;
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
/**
|
||||
* 兼容IE浏览器没有Object.is
|
||||
*/
|
||||
export function isSame(x: any, y: any) {
|
||||
if (!(typeof Object.is === 'function')) {
|
||||
if (x === y) {
|
||||
// +0 != -0
|
||||
return x !== 0 || 1 / x === 1 / y;
|
||||
} else {
|
||||
// NaN == NaN
|
||||
return x !== x && y !== y;
|
||||
}
|
||||
} else {
|
||||
return Object.is(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
export function isArrayEqual(nextParam: Array<any>, lastParam: Array<any> | null) {
|
||||
if (lastParam === null || lastParam.length !== nextParam.length) {
|
||||
return false;
|
||||
}
|
||||
for (let i = 0; i < lastParam.length; i++) {
|
||||
if (!isSame(nextParam[i], lastParam[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export function shallowCompare(paramX: any, paramY: any): boolean {
|
||||
if (isSame(paramX, paramY)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 对比对象
|
||||
if (typeof paramX === 'object' && typeof paramY === 'object' && paramX !== null && paramY !== null) {
|
||||
const keysX = Object.keys(paramX);
|
||||
const keysY = Object.keys(paramY);
|
||||
|
||||
// key长度不相等时直接返回不相等
|
||||
if (keysX.length !== keysY.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return keysX.every((key, i) => {
|
||||
return Object.prototype.hasOwnProperty.call(paramY, key) && isSame(paramX[key], paramY[keysX[i]]);
|
||||
});
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
export const TYPE_ELEMENT = 1;
|
||||
export const TYPE_PORTAL = 2;
|
||||
export const TYPE_FRAGMENT = 3;
|
||||
export const TYPE_STRICT_MODE = 4;
|
||||
export const TYPE_PROVIDER = 5;
|
||||
export const TYPE_CONTEXT = 6;
|
||||
export const TYPE_FORWARD_REF = 7;
|
||||
export const TYPE_SUSPENSE = 8;
|
||||
export const TYPE_PROFILER = 9;
|
||||
export const TYPE_MEMO = 10;
|
||||
export const TYPE_LAZY = 11;
|
|
@ -0,0 +1,13 @@
|
|||
// 当条件不成立报错
|
||||
// 接收模板
|
||||
export function throwIfTrue(condition: boolean, errTemplate: string, ...errExpressions: string[]) {
|
||||
if (condition) {
|
||||
// 将%s 替换成对应的变量
|
||||
const msg = errTemplate.split('%s').reduce((prevSentence: string, part: string, idx: number) => {
|
||||
// %s对应的变量
|
||||
const expression = idx < errExpressions.length ? errExpressions[idx] : '' ;
|
||||
return prevSentence + part + expression;
|
||||
}, '');
|
||||
throw Error(msg);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
import type {VNode} from '../Types';
|
||||
|
||||
// 当前所有者是应拥有当前正在构建的任何组件的组件。
|
||||
const ProcessingVNode: { val: VNode | null } = {
|
||||
val: null,
|
||||
};
|
||||
|
||||
export default ProcessingVNode;
|
|
@ -0,0 +1,98 @@
|
|||
/**
|
||||
* 虚拟DOM结构体
|
||||
*/
|
||||
import {TreeRoot} from './VNodeTags';
|
||||
import type {VNodeTag} from './VNodeTags';
|
||||
import type {RefType, ContextType} from '../Types';
|
||||
import type {Hook} from '../hooks/HookType';
|
||||
|
||||
export class VNode {
|
||||
tag: VNodeTag;
|
||||
key: string | null; // 唯一标识符
|
||||
type: any = null;
|
||||
realNode: any = null; // 如果是类,则存放实例;如果是div这种,则存放真实DOM;
|
||||
|
||||
// 关系结构
|
||||
parent: VNode | null = null; // 父节点
|
||||
children: Array<VNode> | null = null; // 子节点
|
||||
cIndex: number = 0; // 节点在children数组中的位置
|
||||
eIndex: number = 0; // HorizonElement在jsx中的位置,例如:jsx中的null不会生成vNode,所以eIndex和cIndex不一致
|
||||
|
||||
ref: RefType | ((handle: any) => void) | null = null; // 包裹一个函数,submit阶段使用,比如将外部useRef生成的对象赋值到ref上
|
||||
props: any; // 传给组件的props的值,类组件包含defaultProps,Lazy组件不包含
|
||||
oldProps: any = null;
|
||||
|
||||
suspensePromises: any = null; // suspense组件的promise列表
|
||||
changeList: any = null; // DOM的变更列表
|
||||
effectList: any[] = []; // useEffect 的更新数组
|
||||
updates: any[] = null; // TreeRoot和ClassComponent使用的更新数组
|
||||
stateCallbacks: any[] = []; // 存放存在setState的第二个参数和HorizonDOM.render的第三个参数所在的node数组
|
||||
isForceUpdate: boolean = false; // 是否使用强制更新
|
||||
|
||||
state: any = null; // ClassComponent和TreeRoot的状态
|
||||
hooks: Array<Hook<any, any>> = []; // 保存hook
|
||||
suspenseChildStatus: string = ''; // Suspense的Children是否显示
|
||||
depContexts: Array<ContextType<any>> = []; // FunctionComponent和ClassComponent对context的依赖列表
|
||||
isDepContextChange: boolean = false; // context是否变更
|
||||
dirtyNodes: Array<VNode> = []; // 需要改动的节点数组
|
||||
shouldUpdate: boolean = false;
|
||||
childShouldUpdate: boolean = false;
|
||||
outerDom: any;
|
||||
task: any;
|
||||
|
||||
// 使用这个变量来记录修改前的值,用于恢复。
|
||||
contexts = {};
|
||||
// 因为LazyComponent会修改tag和type属性,为了能识别,增加一个属性
|
||||
isLazyComponent: boolean = false;
|
||||
|
||||
// 因为LazyComponent会修改type属性,为了在diff中判断是否可以复用,需要增加一个lazyType
|
||||
lazyType: any = null;
|
||||
flags: {
|
||||
Addition?: boolean,
|
||||
Update?: boolean,
|
||||
Deletion?: boolean,
|
||||
ResetText?: boolean,
|
||||
Callback?: boolean,
|
||||
DidCapture?: boolean,
|
||||
Ref?: boolean,
|
||||
Snapshot?: boolean,
|
||||
Interrupted?: boolean,
|
||||
ShouldCapture?: boolean,
|
||||
ForceUpdate?: boolean,
|
||||
} = {};
|
||||
|
||||
// one tree相关属性
|
||||
isCreated: boolean = true;
|
||||
oldHooks: Array<Hook<any, any>> = []; // 保存上一次执行的hook
|
||||
oldState: any = null;
|
||||
oldRef: RefType | ((handle: any) => void) | null = null;
|
||||
suspenseChildThrow = false;
|
||||
oldSuspenseChildStatus: string = ''; // 上一次Suspense的Children是否显示
|
||||
oldChildren: Array<VNode> | null = null;
|
||||
suspenseDidCapture: boolean = false; // suspense是否捕获了异常
|
||||
promiseResolve: boolean = false; // suspense的promise是否resolve
|
||||
|
||||
path: Array<number> = []; // 保存从根到本节点的路径
|
||||
toUpdateNodes: Set<VNode> | null = null; // 保存要更新的节点
|
||||
|
||||
constructor(tag: VNodeTag, props: any, key: null | string, outerDom) {
|
||||
this.tag = tag; // 对应组件的类型,比如ClassComponent等
|
||||
this.key = key;
|
||||
|
||||
this.props = props;
|
||||
|
||||
// 根节点
|
||||
if (tag === TreeRoot) {
|
||||
this.outerDom = outerDom;
|
||||
this.task = null;
|
||||
this.toUpdateNodes = new Set<VNode>();
|
||||
}
|
||||
}
|
||||
|
||||
setContext(contextName, value) {
|
||||
this.contexts[contextName] = value;
|
||||
}
|
||||
getContext(contextName) {
|
||||
return this.contexts[contextName];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
/**
|
||||
* vNode结构的变化标志
|
||||
*/
|
||||
|
||||
import type { VNode } from '../Types';
|
||||
|
||||
// vNode节点的flags
|
||||
export const Addition = 'Addition';
|
||||
export const Update = 'Update';
|
||||
export const Deletion = 'Deletion';
|
||||
export const ResetText = 'ResetText';
|
||||
export const Callback = 'Callback';
|
||||
export const DidCapture = 'DidCapture';
|
||||
export const Ref = 'Ref';
|
||||
export const Snapshot = 'Snapshot';
|
||||
// 被中断了,抛出错误的vNode以及它的父vNode
|
||||
export const Interrupted = 'Interrupted';
|
||||
export const ShouldCapture = 'ShouldCapture';
|
||||
// For suspense
|
||||
export const ForceUpdate = 'ForceUpdate';
|
||||
|
||||
const flagArr = [Addition, Update, Deletion, ResetText, Callback, DidCapture, Ref, Snapshot, Interrupted, ShouldCapture, ForceUpdate];
|
||||
|
||||
const LifecycleEffectArr = [Update, Callback, Ref, Snapshot];
|
||||
|
||||
function resetFlag(node) {
|
||||
node.flags = {};
|
||||
}
|
||||
|
||||
export class FlagUtils {
|
||||
static removeFlag(node: VNode, flag: string) {
|
||||
node.flags[flag] = false;
|
||||
}
|
||||
static removeLifecycleEffectFlags(node) {
|
||||
LifecycleEffectArr.forEach(key => {
|
||||
node.flags[key] = false;
|
||||
});
|
||||
}
|
||||
static hasAnyFlag(node: VNode) { // 有标志位
|
||||
let keyFlag = false;
|
||||
flagArr.forEach(key => {
|
||||
if (node.flags[key]) {
|
||||
keyFlag = true;
|
||||
}
|
||||
});
|
||||
return keyFlag;
|
||||
}
|
||||
|
||||
static setNoFlags(node: VNode) {
|
||||
resetFlag(node);
|
||||
}
|
||||
|
||||
static markAddition(node: VNode) {
|
||||
node.flags.Addition = true;
|
||||
}
|
||||
static setAddition(node: VNode) {
|
||||
resetFlag(node);
|
||||
node.flags.Addition = true;
|
||||
}
|
||||
static markUpdate(node: VNode) {
|
||||
node.flags.Update = true;
|
||||
}
|
||||
static setDeletion(node: VNode) {
|
||||
resetFlag(node);
|
||||
node.flags.Deletion = true;
|
||||
}
|
||||
static markContentReset(node: VNode) {
|
||||
node.flags.ResetText = true;
|
||||
}
|
||||
static markCallback(node: VNode) {
|
||||
node.flags.Callback = true;
|
||||
}
|
||||
static markDidCapture(node: VNode) {
|
||||
node.flags.DidCapture = true;
|
||||
}
|
||||
static markShouldCapture(node: VNode) {
|
||||
node.flags.ShouldCapture = true;
|
||||
}
|
||||
static markRef(node: VNode) {
|
||||
node.flags.Ref = true;
|
||||
}
|
||||
static markSnapshot(node: VNode) {
|
||||
node.flags.Snapshot = true;
|
||||
}
|
||||
static markInterrupted(node: VNode) {
|
||||
node.flags.Interrupted = true;
|
||||
}
|
||||
static markForceUpdate(node: VNode) {
|
||||
node.flags.ForceUpdate = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
// 从当前节点向上遍历,更新shouldUpdate和childShouldUpdate
|
||||
import {VNode} from './VNode';
|
||||
import {TreeRoot} from './VNodeTags';
|
||||
|
||||
export function updateShouldUpdateOfTree(vNode: VNode): VNode | null {
|
||||
vNode.shouldUpdate = true;
|
||||
|
||||
// 一直向上遍历,修改childShouldUpdate
|
||||
let node = vNode;
|
||||
let parent = vNode.parent;
|
||||
while (parent !== null) {
|
||||
parent.childShouldUpdate = true;
|
||||
node = parent;
|
||||
parent = parent.parent;
|
||||
}
|
||||
|
||||
if (node.tag === TreeRoot) {
|
||||
node.shouldUpdate = true;
|
||||
// 返回根节点
|
||||
return node;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// 设置节点的childShouldUpdate
|
||||
export function updateChildShouldUpdate(vNode: VNode) {
|
||||
const children = vNode.children || [];
|
||||
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
const child = children[i];
|
||||
if (child && (child.shouldUpdate || child.childShouldUpdate)) {
|
||||
vNode.childShouldUpdate = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
vNode.childShouldUpdate = false;
|
||||
}
|
||||
|
||||
// 设置节点的所有父节点的childShouldUpdate
|
||||
export function updateParentsChildShouldUpdate(vNode: VNode) {
|
||||
let node = vNode.parent;
|
||||
let isShouldUpdate = vNode.shouldUpdate || vNode.childShouldUpdate;
|
||||
|
||||
if (isShouldUpdate) { // 开始节点是shouldUpdate或childShouldUpdate
|
||||
setParentsChildShouldUpdate(node);
|
||||
} else {
|
||||
while (node !== null) {
|
||||
updateChildShouldUpdate(node);
|
||||
node = node.parent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 更新从当前节点到根节点的childShouldUpdate为true
|
||||
export function setParentsChildShouldUpdate(parent: VNode | null) {
|
||||
let node = parent;
|
||||
while (node !== null) {
|
||||
if (node.childShouldUpdate) {
|
||||
break;
|
||||
}
|
||||
node.childShouldUpdate = true;
|
||||
|
||||
node = node.parent;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
/**
|
||||
* 定义vNode的类型
|
||||
*/
|
||||
export type VNodeTag = string;
|
||||
|
||||
export const TreeRoot = 'TreeRoot'; // tree的根节点,用于存放一些tree级的变量
|
||||
export const FunctionComponent = 'FunctionComponent';
|
||||
export const ClassComponent = 'ClassComponent';
|
||||
export const ClsOrFunComponent = 'ClsOrFunComponent';
|
||||
export const DomPortal = 'DomPortal';
|
||||
export const DomComponent = 'DomComponent';
|
||||
export const DomText = 'DomText';
|
||||
export const Fragment = 'Fragment';
|
||||
export const ContextConsumer = 'ContextConsumer';
|
||||
export const ContextProvider = 'ContextProvider';
|
||||
export const ForwardRef = 'ForwardRef';
|
||||
export const Profiler = 'Profiler';
|
||||
export const SuspenseComponent = 'SuspenseComponent';
|
||||
export const MemoComponent = 'MemoComponent';
|
||||
export const LazyComponent = 'LazyComponent';
|
||||
export const IncompleteClassComponent = 'IncompleteClassComponent';
|
|
@ -0,0 +1,278 @@
|
|||
/**
|
||||
* 提供:vNode的“遍历”,“查找”,“判断”的相关工具方法
|
||||
*/
|
||||
|
||||
import type {VNode} from '../Types';
|
||||
|
||||
import {DomComponent, DomPortal, DomText, TreeRoot} from './VNodeTags';
|
||||
import {isComment} from '../../dom/utils/Common';
|
||||
import {getNearestVNode} from '../../dom/DOMInternalKeys';
|
||||
|
||||
export function getSiblingVNode(node) {
|
||||
let siblingVNode = null;
|
||||
const index = node.cIndex;
|
||||
if (node && node.parent && node.parent.children && node.parent.children.length > index + 1) {
|
||||
siblingVNode = node.parent.children[index + 1];
|
||||
}
|
||||
return siblingVNode;
|
||||
}
|
||||
|
||||
export function getFirstChild(vNode: VNode) {
|
||||
return (vNode.children && vNode.children.length) ? vNode.children[0] : null;
|
||||
}
|
||||
|
||||
// 从beginVNode开始深度遍历vNode树,对每个vNode调用handleVNode方法
|
||||
export function travelVNodeTree(
|
||||
beginVNode: VNode,
|
||||
handleVNode: Function,
|
||||
childFilter: Function = () => false, // 返回true不处理child
|
||||
finishVNode?: VNode, // 结束遍历节点,有时候和beginVNode不相同
|
||||
handleWhenToParent?: Function
|
||||
): VNode | null {
|
||||
const overVNode = finishVNode || beginVNode;
|
||||
let node = beginVNode;
|
||||
|
||||
while (true) {
|
||||
const ret = handleVNode(node);
|
||||
// 如果处理一个vNode时有返回值,则中断遍历
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// 找子节点
|
||||
const childVNode = getFirstChild(node);
|
||||
if (childVNode !== null && !childFilter(node)) {
|
||||
childVNode.parent = node;
|
||||
node = childVNode;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 回到开始节点
|
||||
if (node === overVNode) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 找兄弟,没有就往上再找兄弟
|
||||
while (getSiblingVNode(node) === null) {
|
||||
if (node.parent === null || node.parent === overVNode) {
|
||||
return null;
|
||||
}
|
||||
node = node.parent;
|
||||
|
||||
if (typeof handleWhenToParent === 'function') {
|
||||
handleWhenToParent(node);
|
||||
}
|
||||
}
|
||||
// 找到兄弟
|
||||
const siblingVNode = getSiblingVNode(node);
|
||||
siblingVNode.parent = node.parent;
|
||||
node = siblingVNode;
|
||||
}
|
||||
}
|
||||
|
||||
// 置空vNode
|
||||
export function clearVNode(vNode: VNode) {
|
||||
clearOneVNode(vNode);
|
||||
}
|
||||
|
||||
function clearOneVNode(vNode: VNode) {
|
||||
vNode.children = [];
|
||||
vNode.depContexts = [];
|
||||
vNode.dirtyNodes = [];
|
||||
vNode.oldProps = null;
|
||||
vNode.state = null;
|
||||
vNode.hooks = [];
|
||||
vNode.suspenseChildStatus = '';
|
||||
vNode.props = null;
|
||||
vNode.parent = null;
|
||||
vNode.suspensePromises = null;
|
||||
vNode.changeList = null;
|
||||
vNode.effectList = [];
|
||||
vNode.updates = null;
|
||||
|
||||
vNode.oldHooks = [];
|
||||
vNode.oldState = null;
|
||||
vNode.oldRef = null;
|
||||
vNode.suspenseChildThrow = false;
|
||||
vNode.oldSuspenseChildStatus = '';
|
||||
vNode.oldChildren = null;
|
||||
|
||||
vNode.path = [];
|
||||
vNode.toUpdateNodes = null;
|
||||
}
|
||||
|
||||
// 是dom类型的vNode
|
||||
export function isDomVNode(node: VNode) {
|
||||
return node.tag === DomComponent || node.tag === DomText;
|
||||
}
|
||||
|
||||
// 是容器类型的vNode
|
||||
function isDomContainer(vNode: VNode): boolean {
|
||||
return (
|
||||
vNode.tag === DomComponent ||
|
||||
vNode.tag === TreeRoot ||
|
||||
vNode.tag === DomPortal
|
||||
);
|
||||
}
|
||||
|
||||
// 找到DOM类型的父
|
||||
export function findDomParent(vNode: VNode) {
|
||||
let parent = vNode.parent;
|
||||
|
||||
while (parent !== null) {
|
||||
switch (parent.tag) {
|
||||
case DomComponent:
|
||||
return {parent, parentDom: parent.realNode};
|
||||
case TreeRoot:
|
||||
case DomPortal:
|
||||
return {parent, parentDom: parent.outerDom};
|
||||
}
|
||||
parent = parent.parent;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function findDomVNode(vNode: VNode): VNode | null {
|
||||
return travelVNodeTree(vNode, (node) => {
|
||||
if (node.tag === DomComponent || node.tag === DomText) {
|
||||
return node;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function findDOMByClassInst(inst) {
|
||||
const vNode = inst._vNode;
|
||||
if (vNode === undefined) {
|
||||
throw new Error('Unable to find the vNode by class instance.');
|
||||
}
|
||||
|
||||
const domVNode = findDomVNode(vNode);
|
||||
|
||||
return domVNode !== null ? domVNode.realNode : null;
|
||||
}
|
||||
|
||||
// 判断dom树是否已经挂载
|
||||
export function isMounted(vNode: VNode) {
|
||||
const rootNode = getTreeRootVNode(vNode);
|
||||
// 如果根节点是 Dom 类型节点,表示已经挂载
|
||||
return rootNode.tag === TreeRoot;
|
||||
}
|
||||
|
||||
function getTreeRootVNode(vNode) {
|
||||
let node = vNode;
|
||||
while (node.parent) {
|
||||
node = node.parent;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
// 找到相邻的DOM
|
||||
export function getSiblingDom(vNode: VNode): Element | null {
|
||||
let node: VNode = vNode;
|
||||
|
||||
findSibling: while (true) {
|
||||
// 没有兄弟节点,找父节点
|
||||
while (getSiblingVNode(node) === null) {
|
||||
// 没父节点,或父节点已经是根节点,则返回
|
||||
if (node.parent === null || isDomContainer(node.parent)) {
|
||||
return null;
|
||||
}
|
||||
node = node.parent;
|
||||
}
|
||||
|
||||
const siblingVNode = getSiblingVNode(node);
|
||||
siblingVNode.parent = node.parent;
|
||||
node = siblingVNode;
|
||||
|
||||
// 如果不是dom节点,往下找
|
||||
while (!isDomVNode(node)) {
|
||||
// 如果节点也是Addition
|
||||
if (node.flags.Addition) {
|
||||
continue findSibling;
|
||||
}
|
||||
|
||||
// 没有子节点,或是DomPortal
|
||||
if (!node.children || !node.children.length || node.tag === DomPortal) {
|
||||
continue findSibling;
|
||||
} else {
|
||||
const childVNode = getFirstChild(node);
|
||||
childVNode.parent = node;
|
||||
node = childVNode;
|
||||
}
|
||||
}
|
||||
|
||||
if (!node.flags.Addition) {
|
||||
// 找到
|
||||
return node.realNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isSameContainer(
|
||||
container: Element,
|
||||
targetContainer: EventTarget,
|
||||
): boolean {
|
||||
if (container === targetContainer) {
|
||||
return true;
|
||||
}
|
||||
// 注释类型的节点
|
||||
if (isComment(container) && container.parentNode === targetContainer) {
|
||||
return true
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function isPortalRoot(vNode, targetContainer) {
|
||||
if (vNode.tag === DomPortal) {
|
||||
let topVNode = vNode.parent;
|
||||
while (topVNode !== null) {
|
||||
const grandTag = topVNode.tag;
|
||||
if (grandTag === TreeRoot || grandTag === DomPortal) {
|
||||
const topContainer = topVNode.outerDom;
|
||||
// 如果topContainer是targetContainer,不需要在这里处理
|
||||
if (isSameContainer(topContainer, targetContainer)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
topVNode = topVNode.parent;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 获取根vNode节点
|
||||
export function getExactNode(targetVNode, targetContainer) {
|
||||
// 确认vNode节点是否准确,portal场景下可能祖先节点不准确
|
||||
let vNode = targetVNode;
|
||||
while (vNode !== null) {
|
||||
if (vNode.tag === TreeRoot || vNode.tag === DomPortal) {
|
||||
let container = vNode.outerDom;
|
||||
if (isSameContainer(container, targetContainer)) {
|
||||
break;
|
||||
}
|
||||
if (isPortalRoot(vNode, targetContainer)) {
|
||||
return null;
|
||||
}
|
||||
while (container !== null) {
|
||||
const parentNode = getNearestVNode(container);
|
||||
if (parentNode === null) {
|
||||
return null;
|
||||
}
|
||||
if (parentNode.tag === DomComponent || parentNode.tag === DomText) {
|
||||
vNode = parentNode;
|
||||
return getExactNode(vNode, targetContainer);
|
||||
}
|
||||
container = container.parentNode;
|
||||
}
|
||||
}
|
||||
vNode = vNode.parent;
|
||||
}
|
||||
if (vNode === null) {
|
||||
return null;
|
||||
}
|
||||
return targetVNode;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue