Match-id-fa22e32139c3b08f8277784897028d35166c0e95
This commit is contained in:
parent
bd50434e32
commit
4759e65160
|
@ -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;
|
||||
|
@ -38,10 +38,9 @@ export function startUpdate(
|
|||
}
|
||||
|
||||
export function getFirstCustomDom(treeRoot: VNode): Element | Text | null {
|
||||
if (!treeRoot.child) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (treeRoot?.child) {
|
||||
return treeRoot.child.realNode;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
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 { findDomParent, getSiblingVNode } from './vnode/VNodeUtils';
|
||||
import {
|
||||
ByAsync,
|
||||
BySync,
|
||||
|
@ -21,7 +21,7 @@ import {
|
|||
isExecuting,
|
||||
setExecuteMode
|
||||
} from './ExecuteMode';
|
||||
import {recoverParentsContextCtx, resetNamespaceCtx, setNamespaceCtx} from './ContextSaver';
|
||||
import { recoverParentsContextCtx, resetNamespaceCtx, setNamespaceCtx } from './ContextSaver';
|
||||
import {
|
||||
updateChildShouldUpdate,
|
||||
updateParentsChildShouldUpdate,
|
||||
|
@ -61,132 +61,6 @@ 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,6 +69,71 @@ function resetProcessingVariables(startUpdateVNode: VNode) {
|
|||
unrecoverableErrorDuringBuild = null;
|
||||
}
|
||||
|
||||
// ============================== 向上递归 ==============================
|
||||
|
||||
// 尝试完成当前工作单元,然后移动到下一个兄弟工作单元。如果没有更多的同级,请返回父vNode。
|
||||
function bubbleVNode(vNode: VNode): void {
|
||||
let node = vNode;
|
||||
|
||||
do {
|
||||
const parent = node.parent;
|
||||
|
||||
if (!node.flags.Interrupted) { // vNode没有抛出异常
|
||||
componentRenders[node.tag].bubbleRender(node);
|
||||
|
||||
// 设置node的childShouldUpdate属性
|
||||
updateChildShouldUpdate(node);
|
||||
|
||||
if (parent !== null && node !== getStartVNode() && !parent.flags.Interrupted) {
|
||||
collectDirtyNodes(node, parent);
|
||||
}
|
||||
}
|
||||
|
||||
// 回到了开始遍历的节点
|
||||
if (node === getStartVNode()) {
|
||||
if (node.tag !== TreeRoot) {
|
||||
// 设置父node的childShouldUpdate属性
|
||||
updateParentsChildShouldUpdate(node);
|
||||
}
|
||||
|
||||
processing = null;
|
||||
break;
|
||||
}
|
||||
|
||||
const siblingVNode = getSiblingVNode(node);
|
||||
if (siblingVNode !== null) { // 有兄弟vNode
|
||||
processing = siblingVNode;
|
||||
return;
|
||||
}
|
||||
|
||||
// 继续遍历parent
|
||||
node = parent;
|
||||
// 更新processing,抛出异常时可以使用
|
||||
processing = node;
|
||||
} while (node !== null);
|
||||
|
||||
// 修改结果
|
||||
if (getBuildResult() === BuildInComplete) {
|
||||
setBuildResult(BuildCompleted);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// ============================== 深度遍历 ==============================
|
||||
function buildVNodeTree(treeRoot: VNode) {
|
||||
const preMode = copyExecuteMode();
|
||||
|
@ -251,20 +190,130 @@ function buildVNodeTree(treeRoot: VNode) {
|
|||
setExecuteMode(preMode);
|
||||
}
|
||||
|
||||
function handleError(root, error): void {
|
||||
if (processing === null || processing.parent === null) {
|
||||
// 这是一个致命的错误,因为没有祖先可以处理它
|
||||
setBuildResult(BuildFatalErrored);
|
||||
unrecoverableErrorDuringBuild = error;
|
||||
// 总体任务入口
|
||||
function renderFromRoot(treeRoot) {
|
||||
runAsyncEffects();
|
||||
|
||||
processing = null;
|
||||
return;
|
||||
// 1. 构建vNode树
|
||||
buildVNodeTree(treeRoot);
|
||||
|
||||
// 致命错误直接抛出
|
||||
if (getBuildResult() === BuildFatalErrored) {
|
||||
throw unrecoverableErrorDuringBuild;
|
||||
}
|
||||
|
||||
// 处理capture和bubble阶段抛出的错误
|
||||
handleRenderThrowError(processing, error);
|
||||
// 2. 提交变更
|
||||
submitToRender(treeRoot);
|
||||
|
||||
bubbleVNode(processing);
|
||||
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): null | void {
|
||||
// 检查循环调用
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 判断数组中节点的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 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;
|
||||
}
|
||||
|
||||
export function setBuildResultError() {
|
||||
|
@ -273,55 +322,6 @@ export function setBuildResultError() {
|
|||
}
|
||||
}
|
||||
|
||||
// ============================== 向上递归 ==============================
|
||||
|
||||
// 尝试完成当前工作单元,然后移动到下一个兄弟工作单元。如果没有更多的同级,请返回父vNode。
|
||||
function bubbleVNode(vNode: VNode): void {
|
||||
let node = vNode;
|
||||
|
||||
do {
|
||||
const parent = node.parent;
|
||||
|
||||
if (!node.flags.Interrupted) { // vNode没有抛出异常
|
||||
componentRenders[node.tag].bubbleRender(node);
|
||||
|
||||
// 设置node的childShouldUpdate属性
|
||||
updateChildShouldUpdate(node);
|
||||
|
||||
if (parent !== null && node !== getStartVNode() && !parent.flags.Interrupted) {
|
||||
collectDirtyNodes(node, parent);
|
||||
}
|
||||
}
|
||||
|
||||
// 回到了开始遍历的节点
|
||||
if (node === getStartVNode()) {
|
||||
if (node.tag !== TreeRoot) {
|
||||
// 设置父node的childShouldUpdate属性
|
||||
updateParentsChildShouldUpdate(node);
|
||||
}
|
||||
|
||||
processing = null;
|
||||
break;
|
||||
}
|
||||
|
||||
const siblingVNode = getSiblingVNode(node);
|
||||
if (siblingVNode !== null) { // 有兄弟vNode
|
||||
processing = siblingVNode;
|
||||
return;
|
||||
}
|
||||
|
||||
// 继续遍历parent
|
||||
node = parent;
|
||||
// 更新processing,抛出异常时可以使用
|
||||
processing = node;
|
||||
} while (node !== null);
|
||||
|
||||
// 修改结果
|
||||
if (getBuildResult() === BuildInComplete) {
|
||||
setBuildResult(BuildCompleted);
|
||||
}
|
||||
}
|
||||
|
||||
// 收集有变化的节点,在submit阶段继续处理
|
||||
function collectDirtyNodes(vNode: VNode, parent: VNode): void {
|
||||
// 将子树和此vNode的所有效果附加到父树的效果列表中,子项的完成顺序会影响副作用顺序。
|
||||
|
|
|
@ -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;
|
||||
|
@ -42,6 +42,18 @@ export function pushUpdate(vNode: VNode, update: Update) {
|
|||
updates.push(update);
|
||||
}
|
||||
|
||||
function getCallback(
|
||||
update: Update,
|
||||
inst: any,
|
||||
oldState: any,
|
||||
props: any,): any {
|
||||
const content = update.content;
|
||||
const newState = typeof content === 'function' ? content.call(inst, oldState, props) : content;
|
||||
return (newState === null || newState === undefined)
|
||||
? oldState
|
||||
: { ...oldState, ...newState };
|
||||
}
|
||||
|
||||
// 根据update获取新的state
|
||||
function calcState(
|
||||
vNode: VNode,
|
||||
|
@ -50,24 +62,22 @@ function calcState(
|
|||
oldState: any,
|
||||
props: any,
|
||||
): any {
|
||||
if (update.type === UpdateState.Override) {
|
||||
switch (update.type) {
|
||||
case UpdateState.Override:
|
||||
const content = update.content;
|
||||
return typeof content === 'function' ? content.call(inst, oldState, props) : content;
|
||||
} else if (update.type === UpdateState.ForceUpdate) {
|
||||
case UpdateState.ForceUpdate:
|
||||
vNode.isForceUpdate = true;
|
||||
return oldState;
|
||||
} else if (update.type === UpdateState.Error || update.type === UpdateState.Update) {
|
||||
if (update.type === UpdateState.Error) {
|
||||
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}
|
||||
}
|
||||
return getCallback(update, inst, oldState, props);
|
||||
case UpdateState.Update:
|
||||
return getCallback(update, inst, oldState, props);
|
||||
default:
|
||||
return oldState;
|
||||
}
|
||||
}
|
||||
|
||||
// 收集callback
|
||||
|
|
|
@ -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; // 是否使用强制更新
|
||||
|
||||
|
|
Loading…
Reference in New Issue