Match-id-380f75464e0bb10e9f8ea83e11081332842887f4

This commit is contained in:
* 2022-01-21 11:07:16 +08:00 committed by *
parent 25c3a84991
commit 66ca6c968a
10 changed files with 199 additions and 197 deletions

View File

@ -69,6 +69,16 @@ function resetProcessingVariables(startUpdateVNode: VNode) {
unrecoverableErrorDuringBuild = null;
}
// 收集有变化的节点在submit阶段继续处理
function collectDirtyNodes(vNode: VNode, parent: VNode): void {
// 将子树和此vNode的所有效果附加到父树的效果列表中子项的完成顺序会影响副作用顺序。
parent.dirtyNodes.push(...vNode.dirtyNodes);
if (FlagUtils.hasAnyFlag(vNode)) {
parent.dirtyNodes.push(vNode);
}
}
// ============================== 向上递归 ==============================
// 尝试完成当前工作单元然后移动到下一个兄弟工作单元。如果没有更多的同级请返回父vNode。
@ -134,6 +144,51 @@ function handleError(root, error): void {
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();
@ -271,67 +326,12 @@ function getChildByIndex(vNode: VNode, idx: number) {
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() {
if (getBuildResult() !== BuildCompleted) {
setBuildResult(BuildErrored);
}
}
// 收集有变化的节点在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)) {

View File

@ -5,24 +5,24 @@ type Trigger<A> = (A) => void;
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,
vtype: any;
type: any;
key: any;
ref: any;
props: any;
_vNode: any,
_vNode: any;
};
export type ProviderType<T> = {

View File

@ -4,11 +4,11 @@ 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

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

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

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

@ -14,6 +14,30 @@ import {launchUpdateFromVNode} from '../TreeBuilder';
import {onlyUpdateChildVNodes} from '../vnode/VNodeCreator';
import {setParentsChildShouldUpdate} from '../vnode/VNodeShouldUpdate';
// 从依赖中找到匹配context的VNode
function matchDependencies(depContexts, context, vNode): boolean {
for (let i = 0; i < depContexts.length; i++) {
const contextItem = depContexts[i];
if (contextItem === context) {
// 匹配到了更新的context需要创建update。
if (vNode.tag === ClassComponent) {
pushForceUpdate(vNode);
}
vNode.shouldUpdate = true;
// 找到需要更新的节点所以祖先节点都需要改为shouldUpdate为true
setParentsChildShouldUpdate(vNode.parent);
vNode.isDepContextChange = true;
// 由于我们已经找到匹配项,我们可以停止遍历依赖项列表。
return true;
}
}
return false;
}
// 从当前子节点开始向下遍历找到消费此context的组件并更新
function handleContextChange(processing: VNode, context: ContextType<any>): void {
const vNode = processing.child;
@ -24,15 +48,15 @@ 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) {
@ -80,26 +104,3 @@ export function bubbleRender(processing: VNode) {
resetContextCtx(processing);
}
// 从依赖中找到匹配context的VNode
function matchDependencies(depContexts, context, vNode): boolean {
for (let i = 0; i < depContexts.length; i++) {
const contextItem = depContexts[i];
if (contextItem === context) {
// 匹配到了更新的context需要创建update。
if (vNode.tag === ClassComponent) {
pushForceUpdate(vNode);
}
vNode.shouldUpdate = true;
// 找到需要更新的节点所以祖先节点都需要改为shouldUpdate为true
setParentsChildShouldUpdate(vNode.parent);
vNode.isDepContextChange = true;
// 由于我们已经找到匹配项,我们可以停止遍历依赖项列表。
return true;
}
}
return false;
}

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