Match-id-1973f30585c9bb87995fad4913f5c677e0601e84
This commit is contained in:
commit
b1458fea04
|
@ -14,14 +14,13 @@ import {
|
|||
TreeRoot,
|
||||
} from '../renderer/vnode/VNodeTags';
|
||||
|
||||
const suffixKey = new Date().getTime().toString();
|
||||
const prefix = '_horizon';
|
||||
|
||||
const internalKeys = {
|
||||
VNode: `${prefix}VNode@${suffixKey}`,
|
||||
props: `${prefix}Props@${suffixKey}`,
|
||||
events: `${prefix}Events@${suffixKey}`,
|
||||
nonDelegatedEvents: `${prefix}NonDelegatedEvents@${suffixKey}`,
|
||||
VNode: `${prefix}VNode`,
|
||||
props: `${prefix}Props`,
|
||||
events: `${prefix}Events`,
|
||||
nonDelegatedEvents: `${prefix}NonDelegatedEvents`,
|
||||
};
|
||||
|
||||
// 通过 VNode 实例获取 DOM 节点
|
||||
|
|
|
@ -73,6 +73,7 @@ function isInDocument(dom) {
|
|||
if (dom && dom.ownerDocument) {
|
||||
return isNodeContainsByTargetNode(dom.ownerDocument.documentElement, dom);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 判断一个标签是否有设置选择范围的能力
|
||||
|
|
|
@ -34,12 +34,12 @@ export function watchValueChange(dom) {
|
|||
// currentVal存储最新值,并重写value的setter、getter
|
||||
let currentVal = String(dom[keyForValue]);
|
||||
|
||||
const setFunc = descriptor.set;
|
||||
const setFunc = descriptor?.set;
|
||||
Object.defineProperty(dom, keyForValue, {
|
||||
...descriptor,
|
||||
set: function (value) {
|
||||
set: function(value) {
|
||||
currentVal = String(value);
|
||||
setFunc.apply(this, [value]);
|
||||
setFunc?.apply(this, [value]);
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import {throwIfTrue} from '../renderer/utils/throwIfTrue';
|
||||
import {TYPE_ELEMENT, TYPE_PORTAL} from '../renderer/utils/elementType';
|
||||
import {TYPE_COMMON_ELEMENT, TYPE_PORTAL} from './JSXElementType';
|
||||
|
||||
import {isValidElement, HorizonElement} from './HorizonElement';
|
||||
import {isValidElement, JSXElement} from './JSXElement';
|
||||
|
||||
// 生成key
|
||||
function getItemKey(item: any, index: number): string {
|
||||
|
@ -12,45 +12,6 @@ function getItemKey(item: any, index: number): string {
|
|||
return '.' + index.toString(36);
|
||||
}
|
||||
|
||||
function mapChildrenToArray(
|
||||
children: any,
|
||||
arr: Array<any>,
|
||||
prefix: string,
|
||||
callback?: Function,
|
||||
): number | void {
|
||||
const type = typeof children;
|
||||
switch (type) {
|
||||
// 继承原有规格,undefined和boolean类型按照null处理
|
||||
case 'undefined':
|
||||
case 'boolean':
|
||||
callMapFun(null, arr, prefix, callback);
|
||||
return;
|
||||
case 'number':
|
||||
case 'string':
|
||||
callMapFun(children, arr, prefix, callback);
|
||||
return;
|
||||
case 'object':
|
||||
if (children === null) {
|
||||
callMapFun(null, arr, prefix, callback);
|
||||
return;
|
||||
}
|
||||
const vtype = children.vtype;
|
||||
if (vtype === TYPE_ELEMENT || vtype === TYPE_PORTAL) {
|
||||
callMapFun(children, arr, prefix, callback) ;
|
||||
return;
|
||||
}
|
||||
if (Array.isArray(children)) {
|
||||
processArrayChildren(children, arr, prefix, callback);
|
||||
return;
|
||||
}
|
||||
throw new Error(
|
||||
'Object is invalid as a Horizon child. '
|
||||
);
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function processArrayChildren(
|
||||
children: any,
|
||||
arr: Array<any>,
|
||||
|
@ -88,11 +49,11 @@ function callMapFun(
|
|||
? '.$' + mappedChild.key
|
||||
: '');
|
||||
// 返回一个修改key的children
|
||||
mappedChild = HorizonElement(
|
||||
mappedChild = JSXElement(
|
||||
mappedChild.type,
|
||||
newKey,
|
||||
mappedChild.ref,
|
||||
mappedChild._vNode,
|
||||
mappedChild.belongClassVNode,
|
||||
mappedChild.props,
|
||||
);
|
||||
}
|
||||
|
@ -100,6 +61,44 @@ function callMapFun(
|
|||
}
|
||||
}
|
||||
|
||||
function mapChildrenToArray(
|
||||
children: any,
|
||||
arr: Array<any>,
|
||||
prefix: string,
|
||||
callback?: Function,
|
||||
): number | void {
|
||||
const type = typeof children;
|
||||
switch (type) {
|
||||
// 继承原有规格,undefined和boolean类型按照null处理
|
||||
case 'undefined':
|
||||
case 'boolean':
|
||||
callMapFun(null, arr, prefix, callback);
|
||||
return;
|
||||
case 'number':
|
||||
case 'string':
|
||||
callMapFun(children, arr, prefix, callback);
|
||||
return;
|
||||
case 'object':
|
||||
if (children === null) {
|
||||
callMapFun(null, arr, prefix, callback);
|
||||
return;
|
||||
}
|
||||
const vtype = children.vtype;
|
||||
if (vtype === TYPE_COMMON_ELEMENT || vtype === TYPE_PORTAL) {
|
||||
callMapFun(children, arr, prefix, callback) ;
|
||||
return;
|
||||
}
|
||||
if (Array.isArray(children)) {
|
||||
processArrayChildren(children, arr, prefix, callback);
|
||||
return;
|
||||
}
|
||||
throw new Error(
|
||||
'Object is invalid as a Horizon child. '
|
||||
);
|
||||
// No Default
|
||||
}
|
||||
}
|
||||
|
||||
// 在 children 里的每个直接子节点上调用一个函数,并将 this 设置为 thisArg
|
||||
function mapChildren(
|
||||
children: any,
|
||||
|
@ -111,9 +110,7 @@ function mapChildren(
|
|||
}
|
||||
let count = 0;
|
||||
const result = [];
|
||||
mapChildrenToArray(children, result, '', (child) => {
|
||||
return func.call(context, child, count++);
|
||||
});
|
||||
mapChildrenToArray(children, result, '', child => func.call(context, child, count++));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ import {
|
|||
TYPE_PROFILER,
|
||||
TYPE_STRICT_MODE,
|
||||
TYPE_SUSPENSE,
|
||||
} from '../renderer/utils/elementType';
|
||||
} from './JSXElementType';
|
||||
|
||||
import {Component, PureComponent} from '../renderer/components/BaseClassComponent';
|
||||
import {createRef} from '../renderer/components/CreateRef';
|
||||
|
@ -12,7 +12,7 @@ import {
|
|||
createElement,
|
||||
cloneElement,
|
||||
isValidElement,
|
||||
} from './HorizonElement';
|
||||
} from './JSXElement';
|
||||
import {createContext} from '../renderer/components/context/CreateContext';
|
||||
import {lazy} from '../renderer/components/Lazy';
|
||||
import {forwardRef} from '../renderer/components/ForwardRef';
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
import { TYPE_ELEMENT } from '../renderer/utils/elementType';
|
||||
import ProcessingVNode from '../renderer/vnode/ProcessingVNode';
|
||||
import { TYPE_COMMON_ELEMENT } from './JSXElementType';
|
||||
import { getProcessingClassVNode } from '../renderer/GlobalVar';
|
||||
|
||||
|
||||
/**
|
||||
* vtype, 节点的类型,这里固定是element
|
||||
* type,保存dom节点的名称或者组件的函数地址
|
||||
* vtype 节点的类型,这里固定是element
|
||||
* type 保存dom节点的名称或者组件的函数地址
|
||||
* key key属性
|
||||
* ref ref属性
|
||||
* props 其他常规属性
|
||||
*/
|
||||
export function HorizonElement(type, key, ref, vNode, props) {
|
||||
export function JSXElement(type, key, ref, vNode, props) {
|
||||
return {
|
||||
// Horizon元素标识符
|
||||
vtype: TYPE_ELEMENT,
|
||||
// 元素标识符
|
||||
vtype: TYPE_COMMON_ELEMENT,
|
||||
|
||||
// 属于元素的内置属性
|
||||
type: type,
|
||||
|
@ -20,50 +20,15 @@ export function HorizonElement(type, key, ref, vNode, props) {
|
|||
ref: ref,
|
||||
props: props,
|
||||
|
||||
// 记录负责创建此元素的组件。
|
||||
_vNode: vNode,
|
||||
// 所属的class组件
|
||||
belongClassVNode: vNode,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
function isValidKey(key) {
|
||||
return key !== 'key' && key !== 'ref' && key !== '__source';
|
||||
}
|
||||
|
||||
function buildElement(isClone, type, setting, ...children) {
|
||||
// setting中的值优先级最高,clone情况下从 type 中取值,创建情况下直接赋值为 null
|
||||
const key = (setting && setting.key !== undefined) ? String(setting.key) : (isClone ? type.key : null);
|
||||
const ref = (setting && setting.ref !== undefined) ? setting.ref : (isClone ? type.ref : null);
|
||||
const props = isClone ? {...type.props} : {};
|
||||
let vNode = isClone ? type._vNode : ProcessingVNode.val;
|
||||
|
||||
if (setting != null) {
|
||||
Object.keys(setting).forEach(k => {
|
||||
if (isValidKey(k)) {
|
||||
props[k] = setting[k];
|
||||
}
|
||||
});
|
||||
if (setting.ref !== undefined && isClone) {
|
||||
vNode = ProcessingVNode.val;
|
||||
}
|
||||
}
|
||||
|
||||
if (children.length) {
|
||||
props.children = children.length === 1 ? children[0] : children;
|
||||
}
|
||||
const element = isClone ? type.type : type;
|
||||
//合并默认属性
|
||||
if (element && element.defaultProps) {
|
||||
mergeDefault(props, element.defaultProps);
|
||||
}
|
||||
|
||||
return HorizonElement(element, key, ref, vNode, props);
|
||||
}
|
||||
|
||||
//创建Element结构体,供JSX编译时调用
|
||||
export function createElement(type, setting, ...children) {
|
||||
return buildElement(false, type, setting, ...children);
|
||||
}
|
||||
|
||||
function mergeDefault(sourceObj, defaultObj) {
|
||||
Object.keys(defaultObj).forEach((key) => {
|
||||
if (sourceObj[key] === undefined) {
|
||||
|
@ -72,11 +37,46 @@ function mergeDefault(sourceObj, defaultObj) {
|
|||
});
|
||||
}
|
||||
|
||||
function buildElement(isClone, type, setting, ...children) {
|
||||
// setting中的值优先级最高,clone情况下从 type 中取值,创建情况下直接赋值为 null
|
||||
const key = (setting && setting.key !== undefined) ? String(setting.key) : (isClone ? type.key : null);
|
||||
const ref = (setting && setting.ref !== undefined) ? setting.ref : (isClone ? type.ref : null);
|
||||
const props = isClone ? {...type.props} : {};
|
||||
let vNode = isClone ? type.belongClassVNode : getProcessingClassVNode();
|
||||
|
||||
if (setting != null) {
|
||||
Object.keys(setting).forEach(k => {
|
||||
if (isValidKey(k)) {
|
||||
props[k] = setting[k];
|
||||
}
|
||||
});
|
||||
if (setting.ref !== undefined && isClone) {
|
||||
vNode = getProcessingClassVNode();
|
||||
}
|
||||
}
|
||||
|
||||
if (children.length) {
|
||||
props.children = children.length === 1 ? children[0] : children;
|
||||
}
|
||||
const element = isClone ? type.type : type;
|
||||
// 合并默认属性
|
||||
if (element && element.defaultProps) {
|
||||
mergeDefault(props, element.defaultProps);
|
||||
}
|
||||
|
||||
return JSXElement(element, key, ref, vNode, props);
|
||||
}
|
||||
|
||||
// 创建Element结构体,供JSX编译时调用
|
||||
export function createElement(type, setting, ...children) {
|
||||
return buildElement(false, type, setting, ...children);
|
||||
}
|
||||
|
||||
export function cloneElement(element, setting, ...children) {
|
||||
return buildElement(true, element, setting, ...children);
|
||||
}
|
||||
|
||||
// 检测结构体是否为合法的Element
|
||||
export function isValidElement(element) {
|
||||
return !!(element && element.vtype === TYPE_ELEMENT);
|
||||
return !!(element && element.vtype === TYPE_COMMON_ELEMENT);
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
export const TYPE_ELEMENT = 1;
|
||||
export const TYPE_COMMON_ELEMENT = 1;
|
||||
export const TYPE_PORTAL = 2;
|
||||
export const TYPE_FRAGMENT = 3;
|
||||
export const TYPE_STRICT_MODE = 4;
|
|
@ -5,7 +5,7 @@ export const InRender = 'IN_RENDER';
|
|||
|
||||
type RenderMode = typeof ByAsync | typeof BySync | typeof InRender;
|
||||
|
||||
// 当前执行阶段标记
|
||||
// 当前执行模式标记
|
||||
let executeMode = {
|
||||
[ByAsync]: false,
|
||||
[BySync]: false,
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
import type {VNode} from './Types';
|
||||
|
||||
// 当前处理的classVNode,用于inst.refs用法中的
|
||||
let processingClassVNode: VNode | null = null;
|
||||
export function getProcessingClassVNode(): VNode | null {
|
||||
return processingClassVNode;
|
||||
}
|
||||
export function setProcessingClassVNode(vNode: VNode | null) {
|
||||
processingClassVNode = vNode;
|
||||
}
|
||||
|
||||
// 计算出来的刷新节点,不一定是根节点
|
||||
let startVNode: VNode | null = null;
|
||||
export function getStartVNode(): VNode | null {
|
||||
return startVNode;
|
||||
}
|
||||
export function setStartVNode(vNode: VNode | null) {
|
||||
startVNode = vNode;
|
||||
}
|
||||
|
||||
type BuildVNodeResult = 0 | 1 | 2 | 3;
|
||||
export const BuildInComplete = 0;
|
||||
export const BuildFatalErrored = 1;
|
||||
export const BuildErrored = 2;
|
||||
export const BuildCompleted = 3;
|
||||
// 根节点退出build tree时的状态,如: completed, incomplete, errored, fatalErrored.
|
||||
let buildVNodeResult: BuildVNodeResult = BuildInComplete;
|
||||
export function setBuildResult(result: BuildVNodeResult) {
|
||||
buildVNodeResult = result;
|
||||
}
|
||||
|
||||
export function getBuildResult(): BuildVNodeResult {
|
||||
return buildVNodeResult;
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import type {VNode} from './Types';
|
||||
import type {Update} from './UpdateHandler';
|
||||
import type { VNode } from './Types';
|
||||
import type { Update } from './UpdateHandler';
|
||||
|
||||
import {
|
||||
asyncUpdates,
|
||||
|
@ -7,12 +7,12 @@ import {
|
|||
runDiscreteUpdates,
|
||||
launchUpdateFromVNode,
|
||||
} from './TreeBuilder';
|
||||
import {runAsyncEffects} from './submit/HookEffectHandler';
|
||||
import {Callback, newUpdate, pushUpdate} from './UpdateHandler';
|
||||
import {getFirstChild} from './vnode/VNodeUtils';
|
||||
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 { createVNode } from './vnode/VNodeCreator';
|
||||
export { createPortal } from './components/CreatePortal';
|
||||
export {
|
||||
asyncUpdates,
|
||||
syncUpdates,
|
||||
|
@ -26,7 +26,7 @@ export function startUpdate(
|
|||
callback?: Callback,
|
||||
) {
|
||||
const update: Update = newUpdate();
|
||||
update.content = {element};
|
||||
update.content = { element };
|
||||
|
||||
if (typeof callback === 'function') {
|
||||
update.callback = callback;
|
||||
|
@ -37,11 +37,10 @@ export function startUpdate(
|
|||
launchUpdateFromVNode(treeRoot);
|
||||
}
|
||||
|
||||
export function getFirstCustomDom(treeRoot: VNode): Element | Text | null {
|
||||
if (!treeRoot.child) {
|
||||
return null;
|
||||
export function getFirstCustomDom(treeRoot?: VNode | null): Element | Text | null {
|
||||
if (treeRoot?.child) {
|
||||
return treeRoot.child.realNode;
|
||||
}
|
||||
|
||||
return treeRoot.child.realNode;
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,16 +1,24 @@
|
|||
import type {VNode} from './Types';
|
||||
import type { VNode } from './Types';
|
||||
|
||||
import {callRenderQueueImmediate, pushRenderCallback} from './taskExecutor/RenderQueue';
|
||||
import {updateVNode} from './vnode/VNodeCreator';
|
||||
import {TreeRoot} from './vnode/VNodeTags';
|
||||
import {FlagUtils} from './vnode/VNodeFlags';
|
||||
import {captureVNode} from './render/BaseComponent';
|
||||
import {checkLoopingUpdateLimit, submitToRender} from './submit/Submit';
|
||||
import {runAsyncEffects} from './submit/HookEffectHandler';
|
||||
import {handleRenderThrowError} from './ErrorHandler';
|
||||
import { callRenderQueueImmediate, pushRenderCallback } from './taskExecutor/RenderQueue';
|
||||
import { updateVNode } from './vnode/VNodeCreator';
|
||||
import { TreeRoot } from './vnode/VNodeTags';
|
||||
import { FlagUtils } from './vnode/VNodeFlags';
|
||||
import { captureVNode } from './render/BaseComponent';
|
||||
import { checkLoopingUpdateLimit, submitToRender } from './submit/Submit';
|
||||
import { runAsyncEffects } from './submit/HookEffectHandler';
|
||||
import { handleRenderThrowError } from './ErrorHandler';
|
||||
import componentRenders from './render';
|
||||
import ProcessingVNode from './vnode/ProcessingVNode';
|
||||
import {findDomParent, getSiblingVNode} from './vnode/VNodeUtils';
|
||||
import {
|
||||
BuildCompleted, BuildErrored,
|
||||
BuildFatalErrored,
|
||||
BuildInComplete, getBuildResult,
|
||||
getStartVNode,
|
||||
setBuildResult,
|
||||
setProcessingClassVNode,
|
||||
setStartVNode
|
||||
} from './GlobalVar';
|
||||
import { findDomParent, getSiblingVNode } from './vnode/VNodeUtils';
|
||||
import {
|
||||
ByAsync,
|
||||
BySync,
|
||||
|
@ -21,172 +29,23 @@ import {
|
|||
isExecuting,
|
||||
setExecuteMode
|
||||
} from './ExecuteMode';
|
||||
import {recoverParentsContextCtx, resetNamespaceCtx, setNamespaceCtx} from './ContextSaver';
|
||||
import { recoverParentsContextCtx, resetNamespaceCtx, setNamespaceCtx } from './ContextSaver';
|
||||
import {
|
||||
updateChildShouldUpdate,
|
||||
updateParentsChildShouldUpdate,
|
||||
updateShouldUpdateOfTree
|
||||
} from './vnode/VNodeShouldUpdate';
|
||||
|
||||
type BuildVNodeResult = 0 | 1 | 2 | 3;
|
||||
const BuildInComplete = 0;
|
||||
const BuildFatalErrored = 1;
|
||||
const BuildErrored = 2;
|
||||
const BuildCompleted = 3;
|
||||
|
||||
// 当前运行的vNode节点
|
||||
let processing: VNode | null = null;
|
||||
// 根节点退出build tree时的状态,如: completed, incomplete, errored, fatalErrored.
|
||||
let buildVNodeResult: BuildVNodeResult = BuildInComplete;
|
||||
|
||||
// 不可恢复错误
|
||||
let unrecoverableErrorDuringBuild: any = null;
|
||||
|
||||
function setBuildResult(result: BuildVNodeResult) {
|
||||
buildVNodeResult = result;
|
||||
}
|
||||
|
||||
function getBuildResult(): BuildVNodeResult {
|
||||
return buildVNodeResult;
|
||||
}
|
||||
|
||||
export function setProcessing(vNode: VNode | null) {
|
||||
processing = vNode;
|
||||
}
|
||||
|
||||
let startVNode: VNode | null = null;
|
||||
export function getStartVNode(): VNode | null {
|
||||
return startVNode;
|
||||
}
|
||||
export function setStartVNode(vNode: VNode | null) {
|
||||
startVNode = vNode;
|
||||
}
|
||||
|
||||
// 发起更新
|
||||
export function launchUpdateFromVNode(vNode: VNode) {
|
||||
// 检查循环调用
|
||||
checkLoopingUpdateLimit();
|
||||
|
||||
// 从当前vNode向上遍历到根节点,修改vNode.shouldUpdate和parent.childShouldUpdate
|
||||
const treeRoot = updateShouldUpdateOfTree(vNode);
|
||||
if (treeRoot === null) {
|
||||
// 可能场景是:the componentWillUnmount method 或 useEffect cleanup function 方法中写异步任务,并且修改state。
|
||||
// 因为异步回调的时候root都可能被清除了。
|
||||
return null;
|
||||
}
|
||||
|
||||
// 保存待刷新的节点
|
||||
treeRoot.toUpdateNodes.add(vNode);
|
||||
|
||||
if (checkMode(BySync) && // 非批量
|
||||
!checkMode(InRender)) { // 不是渲染阶段触发
|
||||
|
||||
// 业务直接调用Horizon.render的时候会进入这个分支,同步渲染。
|
||||
// 不能改成下面的异步,否则会有时序问题,因为业务可能会依赖这个渲染的完成。
|
||||
renderFromRoot(treeRoot);
|
||||
} else {
|
||||
tryRenderRoot(treeRoot);
|
||||
|
||||
if (!isExecuting()) {
|
||||
// 同步执行
|
||||
callRenderQueueImmediate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 从多个更新节点中,计算出开始节点。即:找到最近的共同的父辈节点
|
||||
export function calcStartUpdateVNode(treeRoot: VNode) {
|
||||
const toUpdateNodes = Array.from(treeRoot.toUpdateNodes);
|
||||
|
||||
if (toUpdateNodes.length === 0) {
|
||||
return treeRoot;
|
||||
}
|
||||
|
||||
if (toUpdateNodes.length === 1) {
|
||||
return toUpdateNodes[0];
|
||||
}
|
||||
|
||||
// 要计算的节点过多,直接返回根节点
|
||||
if (toUpdateNodes.length > 100) {
|
||||
return treeRoot;
|
||||
}
|
||||
|
||||
// 找到路径最短的长度
|
||||
let minPath = toUpdateNodes[0].path.length;
|
||||
for (let i = 1; i < toUpdateNodes.length; i++) {
|
||||
let pathLen = toUpdateNodes[i].path.length;
|
||||
if (pathLen < minPath) {
|
||||
minPath = pathLen;
|
||||
}
|
||||
}
|
||||
|
||||
// 找出开始不相等的idx
|
||||
let idx = 0;
|
||||
for (; idx < minPath; idx++) {
|
||||
if (!isEqualByIndex(idx, toUpdateNodes)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 得到相等的路径
|
||||
let startNodePath = toUpdateNodes[0].path.slice(0, idx);
|
||||
|
||||
let node = treeRoot;
|
||||
for (let i = 1; i < startNodePath.length; i++) {
|
||||
let pathIndex = startNodePath[i];
|
||||
node = getChildByIndex(node, pathIndex);
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
function getChildByIndex(vNode: VNode, idx: number) {
|
||||
let node = vNode.child;
|
||||
for (let i = 0; i < idx; i++) {
|
||||
node = node.next;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
// 判断数组中节点的path的idx元素是否都相等
|
||||
function isEqualByIndex(idx: number, nodes: Array<VNode>) {
|
||||
let val = nodes[0].path[idx];
|
||||
for (let i = 1; i < nodes.length; i++) {
|
||||
let node = nodes[i];
|
||||
if (val !== node.path[idx]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// 尝试去渲染,已有任务就跳出
|
||||
export function tryRenderRoot(treeRoot: VNode) {
|
||||
if (treeRoot.shouldUpdate && treeRoot.task === null) {
|
||||
// 任务放进queue,但是调度开始还是异步的
|
||||
treeRoot.task = pushRenderCallback(
|
||||
renderFromRoot.bind(null, treeRoot),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 总体任务入口
|
||||
function renderFromRoot(treeRoot) {
|
||||
runAsyncEffects();
|
||||
|
||||
// 1. 构建vNode树
|
||||
buildVNodeTree(treeRoot);
|
||||
|
||||
// 致命错误直接抛出
|
||||
if (getBuildResult() === BuildFatalErrored) {
|
||||
throw unrecoverableErrorDuringBuild;
|
||||
}
|
||||
|
||||
// 2. 提交变更
|
||||
submitToRender(treeRoot);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// 为重新进行深度遍历做准备
|
||||
function resetProcessingVariables(startUpdateVNode: VNode) {
|
||||
// 创建processing
|
||||
|
@ -195,81 +54,13 @@ function resetProcessingVariables(startUpdateVNode: VNode) {
|
|||
unrecoverableErrorDuringBuild = null;
|
||||
}
|
||||
|
||||
// ============================== 深度遍历 ==============================
|
||||
function buildVNodeTree(treeRoot: VNode) {
|
||||
const preMode = copyExecuteMode();
|
||||
changeMode(InRender, true);
|
||||
// 收集有变化的节点,在submit阶段继续处理
|
||||
function collectDirtyNodes(vNode: VNode, parent: VNode): void {
|
||||
// 将子树和此vNode的所有效果附加到父树的效果列表中,子项的完成顺序会影响副作用顺序。
|
||||
parent.dirtyNodes.push(...vNode.dirtyNodes);
|
||||
|
||||
// 计算出开始节点
|
||||
const startUpdateVNode = calcStartUpdateVNode(treeRoot);
|
||||
|
||||
// 缓存起来
|
||||
setStartVNode(startUpdateVNode);
|
||||
|
||||
// 清空toUpdateNodes
|
||||
treeRoot.toUpdateNodes.clear();
|
||||
|
||||
if (startUpdateVNode.tag !== TreeRoot) { // 不是根节点
|
||||
// 设置namespace,用于createElement
|
||||
const parentObj = findDomParent(startUpdateVNode);
|
||||
|
||||
// 当在componentWillUnmount中调用setState,parent可能是null,因为startUpdateVNode会被clear
|
||||
if (parentObj !== null) {
|
||||
const domParent = parentObj.parent;
|
||||
resetNamespaceCtx(domParent);
|
||||
setNamespaceCtx(domParent, domParent.outerDom);
|
||||
}
|
||||
|
||||
// 恢复父节点的context
|
||||
recoverParentsContextCtx(startUpdateVNode);
|
||||
}
|
||||
|
||||
// 重置环境变量
|
||||
resetProcessingVariables(startUpdateVNode);
|
||||
|
||||
do {
|
||||
try {
|
||||
while (processing !== null) {
|
||||
// 捕获创建 vNodes
|
||||
const next = captureVNode(processing);
|
||||
|
||||
if (next === null) {
|
||||
// 如果没有产生新的,那么就完成当前节点,向上遍历
|
||||
bubbleVNode(processing);
|
||||
} else {
|
||||
processing = next;
|
||||
}
|
||||
|
||||
ProcessingVNode.val = null;
|
||||
}
|
||||
break;
|
||||
} catch (thrownValue) {
|
||||
handleError(treeRoot, thrownValue);
|
||||
}
|
||||
} while (true);
|
||||
|
||||
setExecuteMode(preMode);
|
||||
}
|
||||
|
||||
function handleError(root, error): void {
|
||||
if (processing === null || processing.parent === null) {
|
||||
// 这是一个致命的错误,因为没有祖先可以处理它
|
||||
setBuildResult(BuildFatalErrored);
|
||||
unrecoverableErrorDuringBuild = error;
|
||||
|
||||
processing = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理capture和bubble阶段抛出的错误
|
||||
handleRenderThrowError(processing, error);
|
||||
|
||||
bubbleVNode(processing);
|
||||
}
|
||||
|
||||
export function setBuildResultError() {
|
||||
if (getBuildResult() !== BuildCompleted) {
|
||||
setBuildResult(BuildErrored);
|
||||
if (FlagUtils.hasAnyFlag(vNode)) {
|
||||
parent.dirtyNodes.push(vNode);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -322,13 +113,207 @@ function bubbleVNode(vNode: VNode): void {
|
|||
}
|
||||
}
|
||||
|
||||
// 收集有变化的节点,在submit阶段继续处理
|
||||
function collectDirtyNodes(vNode: VNode, parent: VNode): void {
|
||||
// 将子树和此vNode的所有效果附加到父树的效果列表中,子项的完成顺序会影响副作用顺序。
|
||||
parent.dirtyNodes.push(...vNode.dirtyNodes);
|
||||
function handleError(root, error): void {
|
||||
if (processing === null || processing.parent === null) {
|
||||
// 这是一个致命的错误,因为没有祖先可以处理它
|
||||
setBuildResult(BuildFatalErrored);
|
||||
unrecoverableErrorDuringBuild = error;
|
||||
|
||||
if (FlagUtils.hasAnyFlag(vNode)) {
|
||||
parent.dirtyNodes.push(vNode);
|
||||
processing = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理capture和bubble阶段抛出的错误
|
||||
handleRenderThrowError(processing, error);
|
||||
|
||||
bubbleVNode(processing);
|
||||
}
|
||||
|
||||
// 从多个更新节点中,计算出开始节点。即:找到最近的共同的父辈节点
|
||||
export function calcStartUpdateVNode(treeRoot: VNode) {
|
||||
const toUpdateNodes = Array.from(treeRoot.toUpdateNodes);
|
||||
|
||||
if (toUpdateNodes.length === 0) {
|
||||
return treeRoot;
|
||||
}
|
||||
|
||||
if (toUpdateNodes.length === 1) {
|
||||
return toUpdateNodes[0];
|
||||
}
|
||||
|
||||
// 要计算的节点过多,直接返回根节点
|
||||
if (toUpdateNodes.length > 100) {
|
||||
return treeRoot;
|
||||
}
|
||||
|
||||
// 找到路径最短的长度
|
||||
let minPath = toUpdateNodes[0].path.length;
|
||||
for (let i = 1; i < toUpdateNodes.length; i++) {
|
||||
let pathLen = toUpdateNodes[i].path.length;
|
||||
if (pathLen < minPath) {
|
||||
minPath = pathLen;
|
||||
}
|
||||
}
|
||||
|
||||
// 找出开始不相等的idx
|
||||
let idx = 0;
|
||||
for (; idx < minPath; idx++) {
|
||||
if (!isEqualByIndex(idx, toUpdateNodes)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 得到相等的路径
|
||||
let startNodePath = toUpdateNodes[0].path.slice(0, idx);
|
||||
|
||||
let node = treeRoot;
|
||||
for (let i = 1; i < startNodePath.length; i++) {
|
||||
let pathIndex = startNodePath[i];
|
||||
node = getChildByIndex(node, pathIndex);
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
// ============================== 深度遍历 ==============================
|
||||
function buildVNodeTree(treeRoot: VNode) {
|
||||
const preMode = copyExecuteMode();
|
||||
changeMode(InRender, true);
|
||||
|
||||
// 计算出开始节点
|
||||
const startUpdateVNode = calcStartUpdateVNode(treeRoot);
|
||||
// 缓存起来
|
||||
setStartVNode(startUpdateVNode);
|
||||
|
||||
// 清空toUpdateNodes
|
||||
treeRoot.toUpdateNodes.clear();
|
||||
|
||||
if (startUpdateVNode.tag !== TreeRoot) { // 不是根节点
|
||||
// 设置namespace,用于createElement
|
||||
const parentObj = findDomParent(startUpdateVNode);
|
||||
|
||||
// 当在componentWillUnmount中调用setState,parent可能是null,因为startUpdateVNode会被clear
|
||||
if (parentObj !== null) {
|
||||
const domParent = parentObj.parent;
|
||||
resetNamespaceCtx(domParent);
|
||||
setNamespaceCtx(domParent, domParent.outerDom);
|
||||
}
|
||||
|
||||
// 恢复父节点的context
|
||||
recoverParentsContextCtx(startUpdateVNode);
|
||||
}
|
||||
|
||||
// 重置环境变量,为重新进行深度遍历做准备
|
||||
resetProcessingVariables(startUpdateVNode);
|
||||
|
||||
do {
|
||||
try {
|
||||
while (processing !== null) {
|
||||
// 捕获创建 vNodes
|
||||
const next = captureVNode(processing);
|
||||
|
||||
if (next === null) {
|
||||
// 如果没有产生新的,那么就完成当前节点,向上遍历
|
||||
bubbleVNode(processing);
|
||||
} else {
|
||||
processing = next;
|
||||
}
|
||||
}
|
||||
|
||||
setProcessingClassVNode(null);
|
||||
|
||||
break;
|
||||
} catch (thrownValue) {
|
||||
handleError(treeRoot, thrownValue);
|
||||
}
|
||||
} while (true);
|
||||
|
||||
setExecuteMode(preMode);
|
||||
}
|
||||
|
||||
// 总体任务入口
|
||||
function renderFromRoot(treeRoot) {
|
||||
runAsyncEffects();
|
||||
|
||||
// 1. 构建vNode树
|
||||
buildVNodeTree(treeRoot);
|
||||
|
||||
// 致命错误直接抛出
|
||||
if (getBuildResult() === BuildFatalErrored) {
|
||||
throw unrecoverableErrorDuringBuild;
|
||||
}
|
||||
|
||||
// 2. 提交变更
|
||||
submitToRender(treeRoot);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// 尝试去渲染,已有任务就跳出
|
||||
export function tryRenderRoot(treeRoot: VNode) {
|
||||
if (treeRoot.shouldUpdate && treeRoot.task === null) {
|
||||
// 任务放进queue,但是调度开始还是异步的
|
||||
treeRoot.task = pushRenderCallback(
|
||||
renderFromRoot.bind(null, treeRoot),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 发起更新
|
||||
export function launchUpdateFromVNode(vNode: VNode) {
|
||||
// 检查循环调用
|
||||
checkLoopingUpdateLimit();
|
||||
|
||||
// 从当前vNode向上遍历到根节点,修改vNode.shouldUpdate和parent.childShouldUpdate
|
||||
const treeRoot = updateShouldUpdateOfTree(vNode);
|
||||
if (treeRoot === null) {
|
||||
// 可能场景是:the componentWillUnmount method 或 useEffect cleanup function 方法中写异步任务,并且修改state。
|
||||
// 因为异步回调的时候root都可能被清除了。
|
||||
return;
|
||||
}
|
||||
|
||||
// 保存待刷新的节点
|
||||
treeRoot.toUpdateNodes.add(vNode);
|
||||
|
||||
if (checkMode(BySync) && // 非批量
|
||||
!checkMode(InRender)) { // 不是渲染阶段触发
|
||||
|
||||
// 业务直接调用Horizon.render的时候会进入这个分支,同步渲染。
|
||||
// 不能改成下面的异步,否则会有时序问题,因为业务可能会依赖这个渲染的完成。
|
||||
renderFromRoot(treeRoot);
|
||||
} else {
|
||||
tryRenderRoot(treeRoot);
|
||||
|
||||
if (!isExecuting()) {
|
||||
// 同步执行
|
||||
callRenderQueueImmediate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 判断数组中节点的path的idx元素是否都相等
|
||||
function isEqualByIndex(idx: number, nodes: Array<VNode>) {
|
||||
let val = nodes[0].path[idx];
|
||||
for (let i = 1; i < nodes.length; i++) {
|
||||
let node = nodes[i];
|
||||
if (val !== node.path[idx]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function getChildByIndex(vNode: VNode, idx: number) {
|
||||
let node = vNode.child;
|
||||
for (let i = 0; i < idx; i++) {
|
||||
node = node.next;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
export function setBuildResultError() {
|
||||
if (getBuildResult() !== BuildCompleted) {
|
||||
setBuildResult(BuildErrored);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,28 +2,27 @@ 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>]
|
||||
): [S, Trigger<((S) => S) | S>];
|
||||
};
|
||||
export type UseReducerHookType = {
|
||||
useReducer<S, P, A>(
|
||||
reducer: (S, A) => S,
|
||||
initArg: P, init?: (P) => S,
|
||||
): [S, Trigger<A>]
|
||||
): [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 JSXElement = {
|
||||
vtype: any;
|
||||
type: any;
|
||||
key: any;
|
||||
ref: any;
|
||||
props: any;
|
||||
|
||||
belongClassVNode: any;
|
||||
};
|
||||
|
||||
export type ProviderType<T> = {
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import type {VNode} from './Types';
|
||||
import {FlagUtils, ShouldCapture} from './vnode/VNodeFlags';
|
||||
import type { VNode } from './Types';
|
||||
import { FlagUtils, ShouldCapture } from './vnode/VNodeFlags';
|
||||
|
||||
export type Update = {
|
||||
type: 'Update' | 'Override' | 'ForceUpdate' | 'Error',
|
||||
content: any,
|
||||
callback: Callback | null,
|
||||
type: 'Update' | 'Override' | 'ForceUpdate' | 'Error';
|
||||
content: any;
|
||||
callback: Callback | null;
|
||||
};
|
||||
|
||||
export type Callback = () => any;
|
||||
|
@ -35,11 +35,8 @@ export function newUpdate(): Update {
|
|||
// 将update对象加入updates
|
||||
export function pushUpdate(vNode: VNode, update: Update) {
|
||||
const updates = vNode.updates;
|
||||
if (updates === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
updates.push(update);
|
||||
updates?.push(update);
|
||||
}
|
||||
|
||||
// 根据update获取新的state
|
||||
|
@ -50,24 +47,25 @@ function calcState(
|
|||
oldState: any,
|
||||
props: any,
|
||||
): any {
|
||||
if (update.type === UpdateState.Override) {
|
||||
const content = update.content;
|
||||
return typeof content === 'function' ? content.call(inst, oldState, props) : content;
|
||||
} else if (update.type === UpdateState.ForceUpdate) {
|
||||
vNode.isForceUpdate = true;
|
||||
return oldState;
|
||||
} else if (update.type === UpdateState.Error || update.type === UpdateState.Update) {
|
||||
if (update.type === UpdateState.Error) {
|
||||
switch (update.type) {
|
||||
case UpdateState.Override:
|
||||
const content = update.content;
|
||||
return typeof content === 'function' ? content.call(inst, oldState, props) : content;
|
||||
case UpdateState.ForceUpdate:
|
||||
vNode.isForceUpdate = true;
|
||||
return oldState;
|
||||
case UpdateState.Error:
|
||||
FlagUtils.removeFlag(vNode, ShouldCapture);
|
||||
FlagUtils.markDidCapture(vNode);
|
||||
}
|
||||
const content = update.content;
|
||||
const newState = typeof content === 'function' ? content.call(inst, oldState, props) : content;
|
||||
return (newState === null || newState === undefined)
|
||||
? oldState
|
||||
: {...oldState, ...newState}
|
||||
case UpdateState.Update:
|
||||
const updateContent = update.content;
|
||||
const newState = typeof updateContent === 'function' ? updateContent.call(inst, oldState, props) : updateContent;
|
||||
return (newState === null || newState === undefined)
|
||||
? oldState
|
||||
: { ...oldState, ...newState };
|
||||
default:
|
||||
return oldState;
|
||||
}
|
||||
return oldState;
|
||||
}
|
||||
|
||||
// 收集callback
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import {TYPE_PORTAL} from '../utils/elementType';
|
||||
import {TYPE_PORTAL} from '../../external/JSXElementType';
|
||||
import type {PortalType} from '../Types';
|
||||
|
||||
export function createPortal(
|
||||
children: any,
|
||||
outerDom: any,
|
||||
key: string = null,
|
||||
key: string = '',
|
||||
): PortalType {
|
||||
return {
|
||||
vtype: TYPE_PORTAL,
|
||||
key: key == null ? null : '' + key,
|
||||
key: key == '' ? '' : '' + key,
|
||||
children,
|
||||
outerDom,
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {TYPE_FORWARD_REF} from '../utils/elementType';
|
||||
import {TYPE_FORWARD_REF} from '../../external/JSXElementType';
|
||||
|
||||
export function forwardRef(render: Function) {
|
||||
return {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import type {PromiseType} from '../Types';
|
||||
|
||||
import {TYPE_LAZY} from '../utils/elementType';
|
||||
import {TYPE_LAZY} from '../../external/JSXElementType';
|
||||
|
||||
enum LayStatus {
|
||||
UnProcessed = 'UnProcessed',
|
||||
|
@ -10,14 +10,14 @@ enum LayStatus {
|
|||
}
|
||||
|
||||
type LazyContent<T> = {
|
||||
_status: string,
|
||||
_value: () => PromiseType<{default: T}> | PromiseType<T> | T | any
|
||||
_status: string;
|
||||
_value: () => PromiseType<{default: T}> | PromiseType<T> | T | any;
|
||||
};
|
||||
|
||||
export type LazyComponent<T, P> = {
|
||||
vtype: number,
|
||||
_content: P,
|
||||
_load: (content: P) => T,
|
||||
vtype: number;
|
||||
_content: P;
|
||||
_load: (content: P) => T;
|
||||
};
|
||||
|
||||
// lazyContent随着阶段改变,_value改变:
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {TYPE_MEMO} from '../utils/elementType';
|
||||
import {TYPE_MEMO} from '../../external/JSXElementType';
|
||||
|
||||
export function memo<Props>(type, compare?: (oldProps: Props, newProps: Props) => boolean) {
|
||||
return {
|
||||
|
|
|
@ -51,7 +51,7 @@ export function getOldContext(processing: VNode, clazz: Function, ifProvider: bo
|
|||
|
||||
// 当组件既是提供者,也是消费者时,取上一个context,不能直接取最新context,因为已经被更新为当前组件的context;
|
||||
// 当组件只是消费者时,则取最新context
|
||||
const parentContext = ((ifProvider && isOldProvider(clazz))) ?
|
||||
const parentContext = (ifProvider && isOldProvider(clazz)) ?
|
||||
getOldPreviousContextCtx() :
|
||||
getOldContextCtx();
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import type {ContextType} from '../../Types';
|
||||
import {TYPE_PROVIDER, TYPE_CONTEXT} from '../../utils/elementType';
|
||||
import {TYPE_PROVIDER, TYPE_CONTEXT} from '../../../external/JSXElementType';
|
||||
|
||||
export function createContext<T>(val: T): ContextType<T> {
|
||||
const context: ContextType<T> = {
|
||||
|
|
|
@ -1,27 +1,11 @@
|
|||
import type { VNode, HorizonElement } from '../Types';
|
||||
import type { VNode, JSXElement } from '../Types';
|
||||
|
||||
// 当前vNode和element是同样的类型
|
||||
// LazyComponent 会修改type的类型,所以特殊处理这种类型
|
||||
export const isSameType = (vNode: VNode, ele: HorizonElement) => {
|
||||
export const isSameType = (vNode: VNode, ele: JSXElement) => {
|
||||
return vNode.type === ele.type || (vNode.isLazyComponent && vNode.lazyType === ele.type);
|
||||
};
|
||||
|
||||
export function createRef(element: HorizonElement) {
|
||||
const elementRef = element.ref;
|
||||
// 如果ref是null、function、object,直接返回
|
||||
if (elementRef === null || typeof elementRef === 'function' || typeof elementRef === 'object') {
|
||||
return elementRef;
|
||||
} else { // 包装成函数
|
||||
if (element._vNode) {
|
||||
let inst = element._vNode.realNode;
|
||||
|
||||
return function(instance) {
|
||||
inst.refs[String(elementRef)] = instance;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function isTextType(newChild: any) {
|
||||
return typeof newChild === 'string' || typeof newChild === 'number';
|
||||
}
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
import type { VNode } from '../Types';
|
||||
import { FlagUtils } from '../vnode/VNodeFlags';
|
||||
import { TYPE_ELEMENT, TYPE_FRAGMENT, TYPE_PORTAL } from '../utils/elementType';
|
||||
import { TYPE_COMMON_ELEMENT, TYPE_FRAGMENT, TYPE_PORTAL } from '../../external/JSXElementType';
|
||||
import { DomText, DomPortal, Fragment } from '../vnode/VNodeTags';
|
||||
import {updateVNode, createVNode, createVNodeFromElement, updateVNodePath} from '../vnode/VNodeCreator';
|
||||
import {
|
||||
isSameType,
|
||||
createRef,
|
||||
getIteratorFn,
|
||||
isTextType,
|
||||
isArrayType,
|
||||
|
@ -60,7 +59,7 @@ function checkCanReuseNode(oldNode: VNode | null, newChild: any): boolean {
|
|||
if (isArrayType(newChild) || isIteratorType(newChild)) {
|
||||
return oldKey === null;
|
||||
}
|
||||
if (newChild.vtype === TYPE_ELEMENT || newChild.vtype === TYPE_PORTAL) {
|
||||
if (newChild.vtype === TYPE_COMMON_ELEMENT || newChild.vtype === TYPE_PORTAL) {
|
||||
return oldKey === newChild.key;
|
||||
}
|
||||
}
|
||||
|
@ -79,7 +78,7 @@ function getNodeType(newChild: any): string {
|
|||
if (isArrayType(newChild) || isIteratorType(newChild)) {
|
||||
return DiffCategory.ARR_NODE;
|
||||
}
|
||||
if (newChild.vtype === TYPE_ELEMENT || newChild.vtype === TYPE_PORTAL) {
|
||||
if (newChild.vtype === TYPE_COMMON_ELEMENT || newChild.vtype === TYPE_PORTAL) {
|
||||
return DiffCategory.OBJECT_NODE;
|
||||
}
|
||||
}
|
||||
|
@ -129,7 +128,7 @@ function getNewNode(parentNode: VNode, newChild: any, oldNode: VNode | null) {
|
|||
break;
|
||||
}
|
||||
case DiffCategory.OBJECT_NODE: {
|
||||
if (newChild.vtype === TYPE_ELEMENT) {
|
||||
if (newChild.vtype === TYPE_COMMON_ELEMENT) {
|
||||
if (newChild.type === TYPE_FRAGMENT) {
|
||||
if (oldNode === null || oldNode.tag !== Fragment) {
|
||||
const key = oldNode !== null ? oldNode.key : newChild.key;
|
||||
|
@ -142,10 +141,12 @@ function getNewNode(parentNode: VNode, newChild: any, oldNode: VNode | null) {
|
|||
|
||||
if (oldNode === null || !isSameType(oldNode, newChild)) {
|
||||
resultNode = createVNodeFromElement(newChild);
|
||||
resultNode.ref = createRef(newChild);
|
||||
resultNode.ref = newChild.ref;
|
||||
resultNode.belongClassVNode = newChild.belongClassVNode;
|
||||
} else {
|
||||
resultNode = updateVNode(oldNode, newChild.props);
|
||||
resultNode.ref = createRef(newChild);
|
||||
resultNode.ref = newChild.ref;
|
||||
resultNode.belongClassVNode = newChild.belongClassVNode;
|
||||
}
|
||||
break;
|
||||
} else if (newChild.vtype === TYPE_PORTAL) {
|
||||
|
@ -200,7 +201,7 @@ function getOldNodeFromMap(nodeMap: Map<string | number, VNode>, newIdx: number,
|
|||
if (isArrayType(newChild) || isIteratorType(newChild)) {
|
||||
return nodeMap.get(newIdx) || null;
|
||||
}
|
||||
if (newChild.vtype === TYPE_ELEMENT || newChild.vtype === TYPE_PORTAL) {
|
||||
if (newChild.vtype === TYPE_COMMON_ELEMENT || newChild.vtype === TYPE_PORTAL) {
|
||||
return nodeMap.get(newChild.key === null ? newIdx : newChild.key) || null;
|
||||
}
|
||||
}
|
||||
|
@ -354,9 +355,9 @@ function diffArrayNodes(
|
|||
// 把剩下的currentVNode转成Map
|
||||
const leftChildrenMap = transLeftChildrenToMap(oldNode, rightEndOldNode);
|
||||
// 通过贪心算法+二分法获取最长递增子序列
|
||||
const eIndexes = []; // 记录 eIndex 值
|
||||
const result = []; // 记录最长子序列在eIndexes中的 index 值
|
||||
const preIndex = []; // 贪心算法在替换的过程中会使得数组不正确,通过记录preIndex找到正确值
|
||||
const eIndexes: Array<number> = []; // 记录 eIndex 值
|
||||
const result: Array<number> = []; // 记录最长子序列在eIndexes中的 index 值
|
||||
const preIndex: Array<number> = []; // 贪心算法在替换的过程中会使得数组不正确,通过记录preIndex找到正确值
|
||||
const reuseNodes = []; // 记录复用的 VNode
|
||||
let i = 0;
|
||||
for (; leftIdx < rightIdx; leftIdx++) {
|
||||
|
@ -367,6 +368,7 @@ function diffArrayNodes(
|
|||
// 从Map删除,后面不会deleteVNode
|
||||
leftChildrenMap.delete(newNode.key || leftIdx);
|
||||
}
|
||||
|
||||
if (oldNodeFromMap !== null) {
|
||||
let eIndex = newNode.eIndex;
|
||||
eIndexes.push(eIndex);
|
||||
|
@ -380,7 +382,7 @@ function diffArrayNodes(
|
|||
let middle;
|
||||
// 二分法找到需要替换的值
|
||||
while (start < end) {
|
||||
middle = Math.floor((start + end) / 2)
|
||||
middle = Math.floor((start + end) / 2);
|
||||
if (eIndexes[result[middle]] > eIndex) {
|
||||
end = middle;
|
||||
} else {
|
||||
|
@ -403,6 +405,7 @@ function diffArrayNodes(
|
|||
appendNode(newNode);
|
||||
}
|
||||
}
|
||||
|
||||
if (isComparing) {
|
||||
// 向前回溯找到正确的结果
|
||||
let length = result.length;
|
||||
|
@ -411,9 +414,9 @@ function diffArrayNodes(
|
|||
result[length] = prev;
|
||||
prev = preIndex[result[length]];
|
||||
}
|
||||
result.forEach(i => {
|
||||
result.forEach(idx => {
|
||||
// 把需要复用的节点从 restNodes 中清理掉,因为不需要打 add 标记,直接复用 dom 节点
|
||||
reuseNodes[i] = null;
|
||||
reuseNodes[idx] = null;
|
||||
});
|
||||
reuseNodes.forEach(node => {
|
||||
if (node !== null) {
|
||||
|
@ -490,7 +493,7 @@ function diffStringNodeHandler(
|
|||
firstChildVNode: VNode,
|
||||
isComparing: boolean
|
||||
) {
|
||||
let newTextNode = null;
|
||||
let newTextNode: VNode | null = null;
|
||||
|
||||
// 第一个vNode是Text,则复用
|
||||
if (firstChildVNode !== null && firstChildVNode.tag === DomText) {
|
||||
|
@ -520,7 +523,7 @@ function diffObjectNodeHandler(
|
|||
firstChildVNode: VNode,
|
||||
isComparing: boolean
|
||||
) {
|
||||
let canReuseNode = null;
|
||||
let canReuseNode: VNode | null = null;
|
||||
|
||||
// 通过key比对是否有可以reuse
|
||||
const newKey = newChild.key;
|
||||
|
@ -535,9 +538,9 @@ function diffObjectNodeHandler(
|
|||
}
|
||||
}
|
||||
|
||||
let resultNode = null;
|
||||
let resultNode: VNode | null = null;
|
||||
let startDelVNode = firstChildVNode;
|
||||
if (newChild.vtype === TYPE_ELEMENT) {
|
||||
if (newChild.vtype === TYPE_COMMON_ELEMENT) {
|
||||
if (canReuseNode) {
|
||||
// 可以复用
|
||||
if (canReuseNode.tag === Fragment && newChild.type === TYPE_FRAGMENT) {
|
||||
|
@ -546,7 +549,8 @@ function diffObjectNodeHandler(
|
|||
resultNode.next = null;
|
||||
} else if (isSameType(canReuseNode, newChild)) {
|
||||
resultNode = updateVNode(canReuseNode, newChild.props);
|
||||
resultNode.ref = createRef(newChild);
|
||||
resultNode.ref = newChild.ref;
|
||||
resultNode.belongClassVNode = newChild.belongClassVNode;
|
||||
startDelVNode = getSiblingVNode(resultNode);
|
||||
resultNode.next = null;
|
||||
}
|
||||
|
@ -558,7 +562,8 @@ function diffObjectNodeHandler(
|
|||
resultNode = createVNode(Fragment, newChild.key, newChild.props.children);
|
||||
} else {
|
||||
resultNode = createVNodeFromElement(newChild);
|
||||
resultNode.ref = createRef(newChild);
|
||||
resultNode.ref = newChild.ref;
|
||||
resultNode.belongClassVNode = newChild.belongClassVNode;
|
||||
}
|
||||
}
|
||||
} else if (newChild.vtype === TYPE_PORTAL) {
|
||||
|
|
|
@ -4,7 +4,7 @@ export enum HookStage {
|
|||
Update = 2,
|
||||
}
|
||||
|
||||
let hookStage: HookStage = null;
|
||||
let hookStage: HookStage | null = null;
|
||||
|
||||
export function getHookStage() {
|
||||
return hookStage;
|
||||
|
|
|
@ -31,9 +31,9 @@ function useEffect(
|
|||
}
|
||||
|
||||
if (stage === HookStage.Init) {
|
||||
return useEffectForInit(effectFunc, deps, effectType);
|
||||
useEffectForInit(effectFunc, deps, effectType);
|
||||
} else if (stage === HookStage.Update) {
|
||||
return useEffectForUpdate(effectFunc, deps, effectType);
|
||||
useEffectForUpdate(effectFunc, deps, effectType);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,32 @@ import { createChildrenByDiff } from '../diff/nodeDiffComparator';
|
|||
import {onlyUpdateChildVNodes} from '../vnode/VNodeCreator';
|
||||
import componentRenders from './index';
|
||||
|
||||
// 复用vNode时,也需对stack进行处理
|
||||
function handlerContext(processing: VNode) {
|
||||
switch (processing.tag) {
|
||||
case TreeRoot:
|
||||
setNamespaceCtx(processing, processing.outerDom);
|
||||
break;
|
||||
case DomComponent:
|
||||
setNamespaceCtx(processing);
|
||||
break;
|
||||
case ClassComponent: {
|
||||
const isOldCxtExist = isOldProvider(processing.type);
|
||||
cacheOldCtx(processing, isOldCxtExist);
|
||||
break;
|
||||
}
|
||||
case DomPortal:
|
||||
setNamespaceCtx(processing, processing.outerDom);
|
||||
break;
|
||||
case ContextProvider: {
|
||||
const newValue = processing.props.value;
|
||||
setContextCtx(processing, newValue);
|
||||
break;
|
||||
}
|
||||
// No Default
|
||||
}
|
||||
}
|
||||
|
||||
export function captureVNode(processing: VNode): VNode | null {
|
||||
const component = componentRenders[processing.tag];
|
||||
|
||||
|
@ -38,31 +64,6 @@ export function captureVNode(processing: VNode): VNode | null {
|
|||
return component.captureRender(processing, shouldUpdate);
|
||||
}
|
||||
|
||||
// 复用vNode时,也需对stack进行处理
|
||||
function handlerContext(processing: VNode) {
|
||||
switch (processing.tag) {
|
||||
case TreeRoot:
|
||||
setNamespaceCtx(processing, processing.outerDom);
|
||||
break;
|
||||
case DomComponent:
|
||||
setNamespaceCtx(processing);
|
||||
break;
|
||||
case ClassComponent: {
|
||||
const isOldCxtExist = isOldProvider(processing.type);
|
||||
cacheOldCtx(processing, isOldCxtExist);
|
||||
break;
|
||||
}
|
||||
case DomPortal:
|
||||
setNamespaceCtx(processing, processing.outerDom);
|
||||
break;
|
||||
case ContextProvider: {
|
||||
const newValue = processing.props.value;
|
||||
setContextCtx(processing, newValue);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 创建孩子节点
|
||||
export function createVNodeChildren(processing: VNode, nextChildren: any) {
|
||||
const isComparing = !processing.isCreated;
|
||||
|
|
|
@ -20,33 +20,88 @@ import {
|
|||
markComponentDidUpdate,
|
||||
markGetSnapshotBeforeUpdate,
|
||||
} from './class/ClassLifeCycleProcessor';
|
||||
import {FlagUtils} from '../vnode/VNodeFlags';
|
||||
import { FlagUtils } from '../vnode/VNodeFlags';
|
||||
import { createVNodeChildren, markRef } from './BaseComponent';
|
||||
import {
|
||||
createUpdateArray,
|
||||
processUpdates,
|
||||
} from '../UpdateHandler';
|
||||
import { getContextChangeCtx, setContextChangeCtx } from '../ContextSaver';
|
||||
import ProcessingVNode from '../vnode/ProcessingVNode';
|
||||
import {onlyUpdateChildVNodes} from '../vnode/VNodeCreator';
|
||||
import { setProcessingClassVNode } from '../GlobalVar';
|
||||
import { onlyUpdateChildVNodes } from '../vnode/VNodeCreator';
|
||||
|
||||
export function captureRender(processing: VNode): VNode | null {
|
||||
const clazz = processing.type;
|
||||
const props = processing.props;
|
||||
const nextProps = processing.isLazyComponent ? mergeDefaultProps(clazz, props) : props;
|
||||
return captureClassComponent(processing, clazz, nextProps);
|
||||
// 获取当前节点的context
|
||||
export function getCurrentContext(clazz, processing: VNode) {
|
||||
const context = clazz.contextType;
|
||||
return typeof context === 'object' && context !== null
|
||||
? getNewContext(processing, context)
|
||||
: getOldContext(processing, clazz, true);
|
||||
}
|
||||
|
||||
export function bubbleRender(processing: VNode) {
|
||||
if (isOldProvider(processing.type)) {
|
||||
resetOldCtx(processing);
|
||||
// 挂载实例
|
||||
function mountInstance(clazz, processing: VNode, nextProps: object) {
|
||||
if (!processing.isCreated) {
|
||||
processing.isCreated = true;
|
||||
FlagUtils.markAddition(processing);
|
||||
}
|
||||
|
||||
// 构造实例
|
||||
callConstructor(processing, clazz, nextProps);
|
||||
|
||||
const inst = processing.realNode;
|
||||
inst.props = nextProps;
|
||||
inst.state = processing.state;
|
||||
inst.context = getCurrentContext(clazz, processing);
|
||||
inst.refs = {};
|
||||
|
||||
createUpdateArray(processing);
|
||||
processUpdates(processing, inst, nextProps);
|
||||
inst.state = processing.state;
|
||||
|
||||
// 在调用类组建的渲染方法之前调用 并且在初始挂载及后续更新时都会被调用
|
||||
callDerivedStateFromProps(processing, clazz.getDerivedStateFromProps, nextProps);
|
||||
callComponentWillMount(processing, inst, nextProps);
|
||||
|
||||
markComponentDidMount(processing);
|
||||
}
|
||||
|
||||
// 构建子节点
|
||||
function createChildren(clazz: any, processing: VNode) {
|
||||
markRef(processing);
|
||||
|
||||
setProcessingClassVNode(processing);
|
||||
processing.state = processing.realNode.state;
|
||||
|
||||
const inst = processing.realNode;
|
||||
const isCatchError = processing.flags.DidCapture;
|
||||
|
||||
// 按照已有规格,如果捕获了错误却没有定义getDerivedStateFromError函数,返回的child为null
|
||||
const newElements = isCatchError && typeof clazz.getDerivedStateFromError !== 'function'
|
||||
? null
|
||||
: inst.render();
|
||||
|
||||
processing.child = createVNodeChildren(processing, newElements);
|
||||
return processing.child;
|
||||
}
|
||||
|
||||
// 根据isUpdateComponent,执行不同的生命周期
|
||||
function callUpdateLifeCycle(processing: VNode, nextProps: object, clazz) {
|
||||
const inst = processing.realNode;
|
||||
const newContext = getCurrentContext(clazz, processing);
|
||||
if (processing.isCreated) {
|
||||
callComponentWillMount(processing, inst);
|
||||
} else {
|
||||
callComponentWillUpdate(inst, nextProps, processing.state, newContext);
|
||||
}
|
||||
}
|
||||
|
||||
// 用于未完成的类组件
|
||||
export function getIncompleteClassComponent(clazz, processing: VNode, nextProps: object):VNode | null {
|
||||
mountInstance(clazz, processing, nextProps);
|
||||
return createChildren(clazz, processing);
|
||||
function markLifeCycle(processing: VNode, nextProps: object, shouldUpdate: Boolean) {
|
||||
if (processing.isCreated) {
|
||||
markComponentDidMount(processing);
|
||||
} else if (processing.state !== processing.oldState || shouldUpdate) {
|
||||
markComponentDidUpdate(processing);
|
||||
markGetSnapshotBeforeUpdate(processing);
|
||||
}
|
||||
}
|
||||
|
||||
// 用于类组件
|
||||
|
@ -127,76 +182,21 @@ export function captureClassComponent(processing: VNode, clazz: any, nextProps:
|
|||
}
|
||||
}
|
||||
|
||||
// 挂载实例
|
||||
function mountInstance(clazz, processing: VNode, nextProps: object) {
|
||||
if (!processing.isCreated) {
|
||||
processing.isCreated = true;
|
||||
FlagUtils.markAddition(processing);
|
||||
}
|
||||
|
||||
// 构造实例
|
||||
callConstructor(processing, clazz, nextProps);
|
||||
|
||||
const inst = processing.realNode;
|
||||
inst.props = nextProps;
|
||||
inst.state = processing.state;
|
||||
inst.context = getCurrentContext(clazz, processing);
|
||||
inst.refs = {};
|
||||
|
||||
createUpdateArray(processing);
|
||||
processUpdates(processing, inst, nextProps);
|
||||
inst.state = processing.state;
|
||||
|
||||
// 在调用类组建的渲染方法之前调用 并且在初始挂载及后续更新时都会被调用
|
||||
callDerivedStateFromProps(processing, clazz.getDerivedStateFromProps, nextProps);
|
||||
callComponentWillMount(processing, inst, nextProps);
|
||||
|
||||
markComponentDidMount(processing);
|
||||
export function captureRender(processing: VNode): VNode | null {
|
||||
const clazz = processing.type;
|
||||
const props = processing.props;
|
||||
const nextProps = processing.isLazyComponent ? mergeDefaultProps(clazz, props) : props;
|
||||
return captureClassComponent(processing, clazz, nextProps);
|
||||
}
|
||||
|
||||
// 构建子节点
|
||||
function createChildren(clazz: any, processing: VNode) {
|
||||
markRef(processing);
|
||||
|
||||
ProcessingVNode.val = processing;
|
||||
processing.state = processing.realNode.state;
|
||||
|
||||
const inst = processing.realNode;
|
||||
const isCatchError = processing.flags.DidCapture;
|
||||
|
||||
// 按照已有规格,如果捕获了错误却没有定义getDerivedStateFromError函数,返回的child为null
|
||||
const newElements = (isCatchError && typeof clazz.getDerivedStateFromError !== 'function')
|
||||
? null
|
||||
: inst.render();
|
||||
|
||||
processing.child = createVNodeChildren(processing, newElements);
|
||||
return processing.child;
|
||||
}
|
||||
|
||||
// 获取当前节点的context
|
||||
export function getCurrentContext(clazz, processing: VNode) {
|
||||
const context = clazz.contextType;
|
||||
return typeof context === 'object' && context !== null
|
||||
? getNewContext(processing, context)
|
||||
: getOldContext(processing, clazz, true);
|
||||
}
|
||||
|
||||
// 根据isUpdateComponent,执行不同的生命周期
|
||||
function callUpdateLifeCycle(processing: VNode, nextProps: object, clazz) {
|
||||
const inst = processing.realNode;
|
||||
const newContext = getCurrentContext(clazz, processing);
|
||||
if (processing.isCreated) {
|
||||
callComponentWillMount(processing, inst);
|
||||
} else {
|
||||
callComponentWillUpdate(inst, nextProps, processing.state, newContext);
|
||||
export function bubbleRender(processing: VNode) {
|
||||
if (isOldProvider(processing.type)) {
|
||||
resetOldCtx(processing);
|
||||
}
|
||||
}
|
||||
|
||||
function markLifeCycle(processing: VNode, nextProps: object, shouldUpdate: Boolean) {
|
||||
if (processing.isCreated) {
|
||||
markComponentDidMount(processing);
|
||||
} else if (processing.state !== processing.oldState || shouldUpdate) {
|
||||
markComponentDidUpdate(processing);
|
||||
markGetSnapshotBeforeUpdate(processing);
|
||||
}
|
||||
// 用于未完成的类组件
|
||||
export function getIncompleteClassComponent(clazz, processing: VNode, nextProps: object): VNode | null {
|
||||
mountInstance(clazz, processing, nextProps);
|
||||
return createChildren(clazz, processing);
|
||||
}
|
||||
|
|
|
@ -7,14 +7,8 @@ import {FlagUtils} from '../vnode/VNodeFlags';
|
|||
import {exeFunctionHook} from '../hooks/HookMain';
|
||||
import {createVNodeChildren} from './BaseComponent';
|
||||
|
||||
export function captureRender(processing: VNode): VNode | null {
|
||||
return captureIndeterminateComponent(processing);
|
||||
}
|
||||
|
||||
export function bubbleRender() {}
|
||||
|
||||
function captureIndeterminateComponent(
|
||||
processing: VNode | null,
|
||||
processing: VNode,
|
||||
): VNode | null {
|
||||
const funcComp = processing.type;
|
||||
|
||||
|
@ -34,3 +28,9 @@ function captureIndeterminateComponent(
|
|||
processing.child = createVNodeChildren(processing, newElements);
|
||||
return processing.child;
|
||||
}
|
||||
|
||||
export function captureRender(processing: VNode): VNode | null {
|
||||
return captureIndeterminateComponent(processing);
|
||||
}
|
||||
|
||||
export function bubbleRender() {}
|
||||
|
|
|
@ -3,12 +3,6 @@ import type {VNode, ContextType} from '../Types';
|
|||
import {resetDepContexts, getNewContext} from '../components/context/Context';
|
||||
import {createVNodeChildren} from './BaseComponent';
|
||||
|
||||
export function captureRender(processing: VNode): VNode | null {
|
||||
return captureContextConsumer(processing);
|
||||
}
|
||||
|
||||
export function bubbleRender() {}
|
||||
|
||||
function captureContextConsumer(processing: VNode) {
|
||||
const context: ContextType<any> = processing.type;
|
||||
const props = processing.props;
|
||||
|
@ -21,3 +15,10 @@ function captureContextConsumer(processing: VNode) {
|
|||
processing.child = createVNodeChildren(processing, newChildren);
|
||||
return processing.child;
|
||||
}
|
||||
|
||||
export function captureRender(processing: VNode): VNode | null {
|
||||
return captureContextConsumer(processing);
|
||||
}
|
||||
|
||||
export function bubbleRender() {}
|
||||
|
||||
|
|
|
@ -14,46 +14,6 @@ import {launchUpdateFromVNode} from '../TreeBuilder';
|
|||
import {onlyUpdateChildVNodes} from '../vnode/VNodeCreator';
|
||||
import {setParentsChildShouldUpdate} from '../vnode/VNodeShouldUpdate';
|
||||
|
||||
export function captureRender(processing: VNode): VNode | null {
|
||||
return captureContextProvider(processing);
|
||||
}
|
||||
|
||||
export function bubbleRender(processing: VNode) {
|
||||
resetContextCtx(processing);
|
||||
}
|
||||
|
||||
function captureContextProvider(processing: VNode): VNode | null {
|
||||
const providerType: ProviderType<any> = processing.type;
|
||||
const contextType: ContextType<any> = providerType._context;
|
||||
|
||||
const newProps = processing.props;
|
||||
const oldProps = !processing.isCreated ? processing.oldProps : null;
|
||||
|
||||
// 获取provider设置的context,即provider组件设置的value
|
||||
const newCtx = newProps.value;
|
||||
|
||||
// 更新processing的context值为newProps.value
|
||||
setContextCtx(processing, newCtx);
|
||||
|
||||
if (oldProps !== null) {
|
||||
const oldCtx = oldProps.value;
|
||||
const isSameContext = isSame(oldCtx, newCtx);
|
||||
if (isSameContext) {
|
||||
// context没有改变,复用
|
||||
if (oldProps.children === newProps.children && !getContextChangeCtx()) {
|
||||
return onlyUpdateChildVNodes(processing);
|
||||
}
|
||||
} else {
|
||||
// context更改,更新所有依赖的组件
|
||||
handleContextChange(processing, contextType);
|
||||
}
|
||||
}
|
||||
|
||||
const newElements = newProps.children;
|
||||
processing.child = createVNodeChildren(processing, newElements);
|
||||
return processing.child;
|
||||
}
|
||||
|
||||
// 从依赖中找到匹配context的VNode
|
||||
function matchDependencies(depContexts, context, vNode): boolean {
|
||||
for (let i = 0; i < depContexts.length; i++) {
|
||||
|
@ -88,18 +48,59 @@ function handleContextChange(processing: VNode, context: ContextType<any>): void
|
|||
let isMatch = false;
|
||||
|
||||
// 从vNode开始遍历
|
||||
travelVNodeTree(vNode, (node) => {
|
||||
travelVNodeTree(vNode, node => {
|
||||
const depContexts = node.depContexts;
|
||||
if (depContexts.length) {
|
||||
isMatch = matchDependencies(depContexts, context, node) ?? isMatch;
|
||||
}
|
||||
}, (node) => {
|
||||
}, node =>
|
||||
// 如果这是匹配的provider,则不要更深入地扫描
|
||||
return node.tag === ContextProvider && node.type === processing.type;
|
||||
}, processing);
|
||||
node.tag === ContextProvider && node.type === processing.type
|
||||
, processing);
|
||||
|
||||
// 找到了依赖context的子节点,触发一次更新
|
||||
if (isMatch) {
|
||||
launchUpdateFromVNode(processing);
|
||||
}
|
||||
}
|
||||
|
||||
function captureContextProvider(processing: VNode): VNode | null {
|
||||
const providerType: ProviderType<any> = processing.type;
|
||||
const contextType: ContextType<any> = providerType._context;
|
||||
|
||||
const newProps = processing.props;
|
||||
const oldProps = !processing.isCreated ? processing.oldProps : null;
|
||||
|
||||
// 获取provider设置的context,即provider组件设置的value
|
||||
const newCtx = newProps.value;
|
||||
|
||||
// 更新processing的context值为newProps.value
|
||||
setContextCtx(processing, newCtx);
|
||||
|
||||
if (oldProps !== null) {
|
||||
const oldCtx = oldProps.value;
|
||||
const isSameContext = isSame(oldCtx, newCtx);
|
||||
if (isSameContext) {
|
||||
// context没有改变,复用
|
||||
if (oldProps.children === newProps.children && !getContextChangeCtx()) {
|
||||
return onlyUpdateChildVNodes(processing);
|
||||
}
|
||||
} else {
|
||||
// context更改,更新所有依赖的组件
|
||||
handleContextChange(processing, contextType);
|
||||
}
|
||||
}
|
||||
|
||||
const newElements = newProps.children;
|
||||
processing.child = createVNodeChildren(processing, newElements);
|
||||
return processing.child;
|
||||
}
|
||||
|
||||
export function captureRender(processing: VNode): VNode | null {
|
||||
return captureContextProvider(processing);
|
||||
}
|
||||
|
||||
export function bubbleRender(processing: VNode) {
|
||||
resetContextCtx(processing);
|
||||
}
|
||||
|
||||
|
|
|
@ -17,8 +17,55 @@ import {createVNodeChildren, markRef} from './BaseComponent';
|
|||
import {DomComponent, DomPortal, DomText} from '../vnode/VNodeTags';
|
||||
import {getFirstChild, travelVNodeTree} from '../vnode/VNodeUtils';
|
||||
|
||||
export function captureRender(processing: VNode): VNode | null {
|
||||
return captureDomComponent(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;
|
||||
|
||||
// 输入类型的直接标记更新
|
||||
if (type === 'input' || type === 'textarea' || type === 'select' || type === 'option') {
|
||||
FlagUtils.markUpdate(processing);
|
||||
} else {
|
||||
// 其它的类型,数据有变化才标记更新
|
||||
if (changeList.length) {
|
||||
FlagUtils.markUpdate(processing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 把dom类型的子节点append到parent dom中
|
||||
function appendAllChildren(parent: Element, processing: VNode) {
|
||||
const vNode = processing.child;
|
||||
if (vNode === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 向下递归它的子节点,查找所有终端节点。
|
||||
travelVNodeTree(vNode, node => {
|
||||
if (node.tag === DomComponent || node.tag === DomText) {
|
||||
appendChildElement(parent, node.realNode);
|
||||
}
|
||||
}, node =>
|
||||
// 已经append到父节点,或者是DomPortal都不需要处理child了
|
||||
node.tag === DomComponent || node.tag === DomText || node.tag === DomPortal
|
||||
, processing);
|
||||
}
|
||||
|
||||
export function bubbleRender(processing: VNode) {
|
||||
|
@ -86,53 +133,6 @@ function captureDomComponent(processing: VNode): VNode | null {
|
|||
return processing.child;
|
||||
}
|
||||
|
||||
// 把dom类型的子节点append到parent dom中
|
||||
function appendAllChildren(parent: Element, processing: VNode) {
|
||||
const vNode = processing.child;
|
||||
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;
|
||||
|
||||
// 输入类型的直接标记更新
|
||||
if (type === 'input' || type === 'textarea' || type === 'select' || type === 'option') {
|
||||
FlagUtils.markUpdate(processing);
|
||||
} else {
|
||||
// 其它的类型,数据有变化才标记更新
|
||||
if (changeList.length) {
|
||||
FlagUtils.markUpdate(processing);
|
||||
}
|
||||
}
|
||||
export function captureRender(processing: VNode): VNode | null {
|
||||
return captureDomComponent(processing);
|
||||
}
|
||||
|
|
|
@ -4,10 +4,6 @@ import { createChildrenByDiff } from '../diff/nodeDiffComparator';
|
|||
import { createVNodeChildren } from './BaseComponent';
|
||||
import { prePortal } from '../../dom/DOMOperator';
|
||||
|
||||
export function captureRender(processing: VNode): VNode | null {
|
||||
return capturePortalComponent(processing);
|
||||
}
|
||||
|
||||
export function bubbleRender(processing: VNode) {
|
||||
resetNamespaceCtx(processing);
|
||||
|
||||
|
@ -27,3 +23,7 @@ function capturePortalComponent(processing: VNode) {
|
|||
}
|
||||
return processing.child;
|
||||
}
|
||||
|
||||
export function captureRender(processing: VNode): VNode | null {
|
||||
return capturePortalComponent(processing);
|
||||
}
|
||||
|
|
|
@ -13,23 +13,40 @@ import {onlyUpdateChildVNodes} from '../vnode/VNodeCreator';
|
|||
// 在useState, useReducer的时候,会触发state变化
|
||||
let stateChange = false;
|
||||
|
||||
export function captureRender(processing: VNode, shouldUpdate?: boolean): VNode | null {
|
||||
const Component = processing.type;
|
||||
const unresolvedProps = processing.props;
|
||||
const resolvedProps =
|
||||
processing.isLazyComponent
|
||||
? mergeDefaultProps(Component, unresolvedProps)
|
||||
: unresolvedProps;
|
||||
export function bubbleRender() {}
|
||||
|
||||
return captureFunctionComponent(
|
||||
processing,
|
||||
Component,
|
||||
resolvedProps,
|
||||
shouldUpdate
|
||||
);
|
||||
// 判断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 bubbleRender() {}
|
||||
export function setStateChange(isUpdate) {
|
||||
stateChange = isUpdate;
|
||||
}
|
||||
|
||||
export function isStateChange() {
|
||||
return stateChange;
|
||||
}
|
||||
|
||||
export function captureFunctionComponent(
|
||||
processing: VNode,
|
||||
|
@ -66,35 +83,19 @@ export function captureFunctionComponent(
|
|||
return processing.child;
|
||||
}
|
||||
|
||||
// 判断children是否可以复用
|
||||
function checkIfCanReuseChildren(processing: VNode, shouldUpdate?: boolean) {
|
||||
let isCanReuse = true;
|
||||
export function captureRender(processing: VNode, shouldUpdate?: boolean): VNode | null {
|
||||
const Component = processing.type;
|
||||
const unresolvedProps = processing.props;
|
||||
const resolvedProps =
|
||||
processing.isLazyComponent
|
||||
? mergeDefaultProps(Component, unresolvedProps)
|
||||
: unresolvedProps;
|
||||
|
||||
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;
|
||||
return captureFunctionComponent(
|
||||
processing,
|
||||
Component,
|
||||
resolvedProps,
|
||||
shouldUpdate
|
||||
);
|
||||
}
|
||||
|
||||
export function setStateChange(isUpdate) {
|
||||
stateChange = isUpdate;
|
||||
}
|
||||
|
||||
export function isStateChange() {
|
||||
return stateChange;
|
||||
}
|
||||
|
|
|
@ -1,23 +1,23 @@
|
|||
import type {VNode} from '../Types';
|
||||
import type { VNode } from '../Types';
|
||||
|
||||
import {FlagUtils} from '../vnode/VNodeFlags';
|
||||
import {getLazyVNodeTag} from '../vnode/VNodeCreator';
|
||||
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';
|
||||
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() {}
|
||||
export function bubbleRender() { }
|
||||
|
||||
const LazyRendererMap = {
|
||||
[FunctionComponent]: captureFunctionComponent,
|
||||
|
@ -64,12 +64,13 @@ function captureLazyComponent(
|
|||
Component,
|
||||
'',
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function mergeDefaultProps(Component: any, props: object): object {
|
||||
if (Component && Component.defaultProps) {
|
||||
const clonedProps = {...props};
|
||||
const clonedProps = { ...props };
|
||||
const defaultProps = Component.defaultProps;
|
||||
Object.keys(defaultProps).forEach(key => {
|
||||
if (clonedProps[key] === undefined) {
|
||||
|
|
|
@ -7,7 +7,7 @@ import {
|
|||
TYPE_FRAGMENT,
|
||||
TYPE_PROFILER,
|
||||
TYPE_STRICT_MODE,
|
||||
} from '../utils/elementType';
|
||||
} from '../../external/JSXElementType';
|
||||
import {Fragment} from '../vnode/VNodeTags';
|
||||
|
||||
export function captureRender(processing: VNode, shouldUpdate: boolean): VNode | null {
|
||||
|
|
|
@ -70,8 +70,9 @@ function callBeforeSubmitLifeCycles(
|
|||
case TreeRoot: {
|
||||
const root = vNode.realNode;
|
||||
clearContainer(root.outerDom);
|
||||
return;
|
||||
}
|
||||
|
||||
// No Default
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -136,8 +137,9 @@ function callAfterSubmitLifeCycles(
|
|||
vNode.realNode.focus();
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// No Default
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -157,13 +159,19 @@ function hideOrUnhideAllChildren(vNode, isHidden) {
|
|||
|
||||
function attachRef(vNode: VNode) {
|
||||
const ref = vNode.ref;
|
||||
|
||||
if (ref !== null) {
|
||||
const instance = vNode.realNode;
|
||||
|
||||
if (typeof ref === 'function') {
|
||||
let refType = typeof ref;
|
||||
if (refType === 'function') {
|
||||
ref(instance);
|
||||
} else {
|
||||
} else if (refType === 'object') {
|
||||
(<RefType>ref).current = instance;
|
||||
} else {
|
||||
if (vNode.belongClassVNode && vNode.belongClassVNode.realNode) {
|
||||
vNode.belongClassVNode.realNode.refs[String(ref)] = instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -172,25 +180,26 @@ function detachRef(vNode: VNode, isOldRef?: boolean) {
|
|||
let ref = (isOldRef ? vNode.oldRef : vNode.ref);
|
||||
|
||||
if (ref !== null) {
|
||||
if (typeof ref === 'function') {
|
||||
let refType = typeof ref;
|
||||
|
||||
if (refType === 'function') {
|
||||
try {
|
||||
ref(null);
|
||||
} catch (error) {
|
||||
handleSubmitError(vNode, error);
|
||||
}
|
||||
} else {
|
||||
} else if (refType === 'object') {
|
||||
(<RefType>ref).current = null;
|
||||
} else {
|
||||
if (vNode.belongClassVNode && vNode.belongClassVNode.realNode) {
|
||||
vNode.belongClassVNode.realNode.refs[String(ref)] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 卸载一个vNode,不会递归
|
||||
function unmountVNode(vNode: VNode): void {
|
||||
// TODO 暂时用于规避error处理逻辑,后续删除
|
||||
if (vNode.flags.Addition) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (vNode.tag) {
|
||||
case FunctionComponent:
|
||||
case ForwardRef:
|
||||
|
|
|
@ -11,7 +11,7 @@ import {
|
|||
callBeforeSubmitLifeCycles, submitDeletion, submitAddition,
|
||||
submitResetTextContent, submitUpdate, detachRef,
|
||||
} from './LifeCycleHandler';
|
||||
import {tryRenderRoot, setProcessing, getStartVNode} from '../TreeBuilder';
|
||||
import {tryRenderRoot, setProcessing} from '../TreeBuilder';
|
||||
import {
|
||||
BySync,
|
||||
InRender,
|
||||
|
@ -24,6 +24,7 @@ import {
|
|||
isSchedulingEffects,
|
||||
setSchedulingEffects, setHookEffectRoot,
|
||||
} from './HookEffectHandler';
|
||||
import {getStartVNode} from '../GlobalVar';
|
||||
|
||||
let rootThrowError = null;
|
||||
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
import type {VNode} from '../Types';
|
||||
|
||||
// 当前所有者是应拥有当前正在构建的任何组件的组件。
|
||||
const ProcessingVNode: { val: VNode | null } = {
|
||||
val: null,
|
||||
};
|
||||
|
||||
export default ProcessingVNode;
|
|
@ -26,7 +26,7 @@ export class VNode {
|
|||
suspensePromises: any = null; // suspense组件的promise列表
|
||||
changeList: any = null; // DOM的变更列表
|
||||
effectList: any[] = []; // useEffect 的更新数组
|
||||
updates: any[] = null; // TreeRoot和ClassComponent使用的更新数组
|
||||
updates: any[] | null = null; // TreeRoot和ClassComponent使用的更新数组
|
||||
stateCallbacks: any[] = []; // 存放存在setState的第二个参数和HorizonDOM.render的第三个参数所在的node数组
|
||||
isForceUpdate: boolean = false; // 是否使用强制更新
|
||||
|
||||
|
@ -76,6 +76,8 @@ export class VNode {
|
|||
path: Array<number> = []; // 保存从根到本节点的路径
|
||||
toUpdateNodes: Set<VNode> | null = null; // 保存要更新的节点
|
||||
|
||||
belongClassVNode: VNode | null = null; // 记录JSXElement所属class vNode,处理ref的时候使用
|
||||
|
||||
constructor(tag: VNodeTag, props: any, key: null | string, outerDom) {
|
||||
this.tag = tag; // 对应组件的类型,比如ClassComponent等
|
||||
this.key = key;
|
||||
|
|
|
@ -24,9 +24,9 @@ import {
|
|||
TYPE_MEMO, TYPE_PROFILER,
|
||||
TYPE_PROVIDER, TYPE_STRICT_MODE,
|
||||
TYPE_SUSPENSE,
|
||||
} from '../utils/elementType';
|
||||
} from '../../external/JSXElementType';
|
||||
import { VNode } from './VNode';
|
||||
import {HorizonElement} from '../Types';
|
||||
import {JSXElement} from '../Types';
|
||||
|
||||
const typeLazyMap = {
|
||||
[TYPE_FORWARD_REF]: ForwardRef,
|
||||
|
@ -156,7 +156,7 @@ export function updateVNodePath(vNode: VNode) {
|
|||
vNode.path = [...vNode.parent.path, vNode.cIndex];
|
||||
}
|
||||
|
||||
export function createVNodeFromElement(element: HorizonElement): VNode {
|
||||
export function createVNodeFromElement(element: JSXElement): VNode {
|
||||
const type = element.type;
|
||||
const key = element.key;
|
||||
const props = element.props;
|
||||
|
|
|
@ -19,7 +19,7 @@ export const ShouldCapture = 'ShouldCapture';
|
|||
// For suspense
|
||||
export const ForceUpdate = 'ForceUpdate';
|
||||
|
||||
const flagArr = [Addition, Update, Deletion, ResetText, Callback, DidCapture, Ref, Snapshot, Interrupted, ShouldCapture, ForceUpdate];
|
||||
const FlagArr = [Addition, Update, Deletion, ResetText, Callback, DidCapture, Ref, Snapshot, Interrupted, ShouldCapture, ForceUpdate];
|
||||
|
||||
const LifecycleEffectArr = [Update, Callback, Ref, Snapshot];
|
||||
|
||||
|
@ -38,9 +38,10 @@ export class FlagUtils {
|
|||
}
|
||||
static hasAnyFlag(node: VNode) { // 有标志位
|
||||
let keyFlag = false;
|
||||
flagArr.forEach(key => {
|
||||
FlagArr.forEach(key => {
|
||||
if (node.flags[key]) {
|
||||
keyFlag = true;
|
||||
return;
|
||||
}
|
||||
});
|
||||
return keyFlag;
|
||||
|
|
|
@ -13,7 +13,7 @@ export function getSiblingVNode(node) {
|
|||
}
|
||||
|
||||
export function travelChildren(beginVNode: VNode, handleVNode: Function, isFinish?: Function) {
|
||||
let node = beginVNode;
|
||||
let node: VNode | null = beginVNode;
|
||||
|
||||
while (node !== null) {
|
||||
if (isFinish && isFinish(node)) {
|
||||
|
@ -77,10 +77,6 @@ export function travelVNodeTree(
|
|||
|
||||
// 置空vNode
|
||||
export function clearVNode(vNode: VNode) {
|
||||
clearOneVNode(vNode);
|
||||
}
|
||||
|
||||
function clearOneVNode(vNode: VNode) {
|
||||
vNode.child = null;
|
||||
vNode.next = null;
|
||||
vNode.depContexts = [];
|
||||
|
@ -105,6 +101,8 @@ function clearOneVNode(vNode: VNode) {
|
|||
|
||||
vNode.path = [];
|
||||
vNode.toUpdateNodes = null;
|
||||
|
||||
vNode.belongClassVNode = null;
|
||||
}
|
||||
|
||||
// 是dom类型的vNode
|
||||
|
@ -144,6 +142,7 @@ export function findDomVNode(vNode: VNode): VNode | null {
|
|||
if (node.tag === DomComponent || node.tag === DomText) {
|
||||
return node;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -224,7 +223,7 @@ function isSameContainer(
|
|||
}
|
||||
// 注释类型的节点
|
||||
if (isComment(container) && container.parentNode === targetContainer) {
|
||||
return true
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue