Match-id-d793acba3f91b34b2fcca385f3c3404ab7714454

This commit is contained in:
* 2021-12-22 16:42:36 +08:00 committed by *
commit ef662ef283
2 changed files with 478 additions and 0 deletions

View File

@ -0,0 +1,367 @@
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 componentRenders from './render';
import ProcessingVNode from './vnode/ProcessingVNode';
import {findDomParent, getSiblingVNode} from './vnode/VNodeUtils';
import {
ByAsync,
BySync,
changeMode,
checkMode,
copyExecuteMode,
InRender,
isExecuting,
setExecuteMode
} from './ExecuteMode';
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 = node.children[pathIndex];
}
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
processing = updateVNode(startUpdateVNode, startUpdateVNode?.props);
setBuildResult(BuildInComplete);
unrecoverableErrorDuringBuild = null;
}
// ============================== 深度遍历 ==============================
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);
// @ts-ignore
if (!next || !next.length) {
// 如果没有产生新的,那么就完成当前节点,向上遍历
bubbleVNode(processing);
} else {
processing = next[0];
}
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);
}
}
// ============================== 向上递归 ==============================
// 尝试完成当前工作单元然后移动到下一个兄弟工作单元。如果没有更多的同级请返回父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的所有效果附加到父树的效果列表中子项的完成顺序会影响副作用顺序。
parent.dirtyNodes.push(...vNode.dirtyNodes);
if (FlagUtils.hasAnyFlag(vNode)) {
parent.dirtyNodes.push(vNode);
}
}
// ============================== HorizonDOM使用 ==============================
export function runDiscreteUpdates() {
if (checkMode(ByAsync) || checkMode(InRender)) {
// 已经渲染不能再同步执行待工作的任务有可能是被生命周期或effect触发的事件导致的如el.focus()
return;
}
runAsyncEffects();
}
export function asyncUpdates(fn, ...param) {
const preMode = copyExecuteMode();
changeMode(ByAsync, true);
try {
return fn(...param);
} finally {
setExecuteMode(preMode);
if (!isExecuting()) {
// 同步执行
callRenderQueueImmediate();
}
}
}
export function syncUpdates(fn) {
const preMode = copyExecuteMode();
// 去掉异步状态,添加同步状态
changeMode(ByAsync, false);
changeMode(BySync, true);
try {
return fn();
} finally {
setExecuteMode(preMode);
if (!isExecuting()) {
// 同步执行
callRenderQueueImmediate();
}
}
}

View File

@ -0,0 +1,111 @@
import type {VNode} from './Types';
import {FlagUtils, ShouldCapture} from './vnode/VNodeFlags';
export type Update = {
type: 'Update' | 'Override' | 'ForceUpdate' | 'Error',
content: any,
callback: Callback | null,
};
export type Callback = () => any;
export type Updates = Array<Update> | null;
export enum UpdateState {
Update = 'Update',
Override = 'Override',
ForceUpdate = 'ForceUpdate',
Error = 'Error',
}
// 初始化更新数组
export function createUpdateArray(vNode: VNode): void {
vNode.updates = []; // 新产生的update会加入这个数组
}
// 创建update对象
export function newUpdate(): Update {
return {
type: UpdateState.Update, // 更新的类型
content: null, // ClassComponent的content是setState第一个参数TreeRoot的content是HorizonDOM.render的第一个参数
callback: null, // setState的第二个参数HorizonDOM.render的第三个参数
};
}
// 将update对象加入updates
export function pushUpdate(vNode: VNode, update: Update) {
const updates = vNode.updates;
if (updates === null) {
return;
}
updates.push(update);
}
// 根据update获取新的state
function calcState(
vNode: VNode,
update: Update,
inst: any,
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) {
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 oldState;
}
// 收集callback
function collectCallbacks(vNode: VNode, update: Update) {
if (update.callback !== null) {
FlagUtils.markCallback(vNode);
vNode.stateCallbacks.push(update.callback);
}
}
// 遍历处理updates, 更新vNode的state
function calcUpdates(vNode: VNode, props: any, inst: any, toProcessUpdates: Updates) {
let newState = vNode.state;
toProcessUpdates.forEach(update => {
newState = calcState(vNode, update, inst, newState, props);
collectCallbacks(vNode, update);
});
vNode.shouldUpdate = false;
vNode.state = newState;
}
// 将待更新的队列添加到updates的尾部
export function processUpdates(vNode: VNode, inst: any, props: any): void {
const updates: Updates = vNode.updates;
vNode.isForceUpdate = false;
const toProcessUpdates = [...updates];
updates.length = 0;
if (toProcessUpdates.length) {
calcUpdates(vNode, props, inst, toProcessUpdates);
}
}
export function pushForceUpdate(vNode: VNode) {
const update = newUpdate();
update.type = UpdateState.ForceUpdate;
pushUpdate(vNode, update);
}