Match-id-fa22e32139c3b08f8277784897028d35166c0e95

This commit is contained in:
* 2022-01-19 10:25:41 +08:00 committed by *
parent bd50434e32
commit 4759e65160
4 changed files with 238 additions and 229 deletions

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

View File

@ -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的所有效果附加到父树的效果列表中子项的完成顺序会影响副作用顺序。

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

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; // 是否使用强制更新