Match-id-1973f30585c9bb87995fad4913f5c677e0601e84

This commit is contained in:
* 2022-01-21 14:57:21 +08:00 committed by *
commit b1458fea04
40 changed files with 762 additions and 752 deletions

View File

@ -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 节点

View File

@ -73,6 +73,7 @@ function isInDocument(dom) {
if (dom && dom.ownerDocument) {
return isNodeContainsByTargetNode(dom.ownerDocument.documentElement, dom);
}
return false;
}
// 判断一个标签是否有设置选择范围的能力

View File

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

View File

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

View File

@ -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';

View File

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

View File

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

View File

@ -5,7 +5,7 @@ export const InRender = 'IN_RENDER';
type RenderMode = typeof ByAsync | typeof BySync | typeof InRender;
// 当前执行阶段标记
// 当前执行模式标记
let executeMode = {
[ByAsync]: false,
[BySync]: false,

View File

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

View File

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

View File

@ -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中调用setStateparent可能是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中调用setStateparent可能是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);
}
}

View File

@ -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> = {

View File

@ -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

View File

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

View File

@ -1,4 +1,4 @@
import {TYPE_FORWARD_REF} from '../utils/elementType';
import {TYPE_FORWARD_REF} from '../../external/JSXElementType';
export function forwardRef(render: Function) {
return {

View File

@ -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改变:

View File

@ -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 {

View File

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

View File

@ -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> = {

View File

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

View File

@ -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) {

View File

@ -4,7 +4,7 @@ export enum HookStage {
Update = 2,
}
let hookStage: HookStage = null;
let hookStage: HookStage | null = null;
export function getHookStage() {
return hookStage;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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) {

View File

@ -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 {

View File

@ -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:

View File

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

View File

@ -1,8 +0,0 @@
import type {VNode} from '../Types';
// 当前所有者是应拥有当前正在构建的任何组件的组件。
const ProcessingVNode: { val: VNode | null } = {
val: null,
};
export default ProcessingVNode;

View File

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

View File

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

View File

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

View File

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