Match-id-9419c4670068e525646a01fdb6a0a0169680b14c

This commit is contained in:
* 2021-12-22 20:30:01 +08:00 committed by *
parent 1cc73acdd0
commit d6e5d6285c
6 changed files with 598 additions and 0 deletions

View File

@ -0,0 +1,79 @@
import type { VNode } from '../Types';
import {cacheOldCtx, isOldProvider} from '../components/context/CompatibleContext';
import {
ClassComponent,
ContextProvider,
DomComponent,
DomPortal,
TreeRoot,
SuspenseComponent,
} from '../vnode/VNodeTags';
import { getContextChangeCtx, setContextCtx, setNamespaceCtx } from '../ContextSaver';
import { FlagUtils } from '../vnode/VNodeFlags';
import { createChildrenByDiff } from '../diff/nodeDiffComparator';
import {onlyUpdateChildVNodes} from '../vnode/VNodeCreator';
import componentRenders from './index';
export function captureVNode(processing: VNode): Array<VNode> | VNode | null {
const component = componentRenders[processing.tag];
if (processing.tag !== SuspenseComponent) {
// 该vNode没有变化不用进入capture直接复用。
if (
!processing.isCreated &&
processing.oldProps === processing.props &&
!getContextChangeCtx() &&
!processing.shouldUpdate
) {
// 复用还需对stack进行处理
handlerContext(processing);
return onlyUpdateChildVNodes(processing);
}
}
const shouldUpdate = processing.shouldUpdate;
processing.shouldUpdate = false;
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 currentChildren = !processing.isCreated ? (processing.children ?? []) : [];
const isComparing = !processing.isCreated;
return createChildrenByDiff(processing, currentChildren, nextChildren, isComparing);
}
export function markRef(processing: VNode) {
const ref = processing.ref;
if ((processing.isCreated && ref !== null) || (!processing.isCreated && processing.oldRef !== ref)) {
FlagUtils.markRef(processing);
}
}

View File

@ -0,0 +1,202 @@
import type { VNode } from '../Types';
import { mergeDefaultProps } from './LazyComponent';
import { getNewContext, resetDepContexts } from '../components/context/Context';
import {
cacheOldCtx,
getOldContext,
isOldProvider,
resetOldCtx,
updateOldContext,
} from '../components/context/CompatibleContext';
import {
callComponentWillMount,
callComponentWillReceiveProps,
callComponentWillUpdate,
callConstructor,
callDerivedStateFromProps,
callShouldComponentUpdate,
markComponentDidMount,
markComponentDidUpdate,
markGetSnapshotBeforeUpdate,
} from './class/ClassLifeCycleProcessor';
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';
export function captureRender(processing: VNode): Array<VNode> | null {
const clazz = processing.type;
const props = processing.props;
const nextProps = processing.isLazyComponent ? mergeDefaultProps(clazz, props) : props;
return captureClassComponent(processing, clazz, nextProps);
}
export function bubbleRender(processing: VNode) {
if (isOldProvider(processing.type)) {
resetOldCtx(processing);
}
}
// 用于未完成的类组件
export function getIncompleteClassComponent(clazz, processing: VNode, nextProps: object): Array<VNode> | null {
mountInstance(clazz, processing, nextProps);
return createChildren(clazz, processing);
}
// 用于类组件
export function captureClassComponent(processing: VNode, clazz: any, nextProps: object): Array<VNode> | null {
const isOldCxtExist = isOldProvider(clazz);
cacheOldCtx(processing, isOldCxtExist);
resetDepContexts(processing);
// 通过 shouldUpdate 判断是否要复用 children该值和props,state,context的变化shouldComponentUpdate,forceUpdate api的调用结果有关
let shouldUpdate;
const inst = processing.realNode;
if (inst === null) {
// 挂载新组件,一定会更新
mountInstance(clazz, processing, nextProps);
shouldUpdate = true;
} else { // 更新
const newContext = getCurrentContext(clazz, processing);
// 子节点抛出异常时如果本class是个捕获异常的处理节点这时候oldProps是null所以需要使用props
let oldProps = processing.flags.DidCapture ? processing.props : processing.oldProps;
if (processing.isLazyComponent) {
oldProps = mergeDefaultProps(processing.type, oldProps);
}
inst.props = oldProps;
if (oldProps !== processing.props || inst.context !== newContext) {
// 在已挂载的组件接收新的 props 之前被调用
callComponentWillReceiveProps(inst, nextProps, newContext);
}
inst.state = processing.state;
processUpdates(processing, inst, nextProps);
// 如果 props, state, context 都没有变化且 isForceUpdate 为 false则不需要更新
shouldUpdate = oldProps !== processing.props ||
inst.state !== processing.state ||
getContextChangeCtx() ||
processing.isForceUpdate;
if (shouldUpdate) {
// derivedStateFromProps会修改nextState因此需要调用
callDerivedStateFromProps(processing, clazz.getDerivedStateFromProps, nextProps);
if (!processing.isForceUpdate) {
// 业务可以通过 shouldComponentUpdate 函数进行优化阻止更新
shouldUpdate = callShouldComponentUpdate(processing, oldProps, nextProps, processing.state, newContext);
}
if (shouldUpdate) {
callUpdateLifeCycle(processing, nextProps, clazz);
}
inst.state = processing.state;
inst.context = newContext;
}
markLifeCycle(processing, nextProps, shouldUpdate);
// 不管有没有更新props都必须更新
inst.props = nextProps;
}
// 如果捕获了 error必须更新
const isCatchError = processing.flags.DidCapture;
shouldUpdate = isCatchError || shouldUpdate;
// 更新ref
markRef(processing);
// 不复用
if (shouldUpdate) {
// 更新context
if (isOldCxtExist) {
updateOldContext(processing);
}
return createChildren(clazz, processing);
} else {
if (isOldCxtExist) {
setContextChangeCtx(processing, false);
}
return onlyUpdateChildVNodes(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);
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.children = createVNodeChildren(processing, newElements);
return processing.children;
}
// 获取当前节点的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);
}
}
function markLifeCycle(processing: VNode, nextProps: object, shouldUpdate: Boolean) {
if (processing.isCreated) {
markComponentDidMount(processing);
} else if (processing.state !== processing.oldState || shouldUpdate) {
markComponentDidUpdate(processing);
markGetSnapshotBeforeUpdate(processing);
}
}

View File

@ -0,0 +1,36 @@
import type {VNode} from '../Types';
import {FunctionComponent} from '../vnode/VNodeTags';
import {resetDepContexts} from '../components/context/Context';
import {getOldContext} from '../components/context/CompatibleContext';
import {FlagUtils} from '../vnode/VNodeFlags';
import {exeFunctionHook} from '../hooks/HookMain';
import {createVNodeChildren} from './BaseComponent';
export function captureRender(processing: VNode): Array<VNode> | null {
return captureIndeterminateComponent(processing);
}
export function bubbleRender() {}
function captureIndeterminateComponent(
processing: VNode | null,
): Array<VNode> | null {
const funcComp = processing.type;
if (!processing.isCreated) {
processing.isCreated = true;
FlagUtils.markAddition(processing);
}
const props = processing.props;
const context = getOldContext(processing, funcComp, false);
resetDepContexts(processing);
const newElements = exeFunctionHook(funcComp, props, context, processing);
processing.tag = FunctionComponent;
processing.children = createVNodeChildren(processing, newElements);
return processing.children;
}

View File

@ -0,0 +1,23 @@
import type {VNode, ContextType} from '../Types';
import {resetDepContexts, getNewContext} from '../components/context/Context';
import {createVNodeChildren} from './BaseComponent';
export function captureRender(processing: VNode): Array<VNode> | null {
return captureContextConsumer(processing);
}
export function bubbleRender() {}
function captureContextConsumer(processing: VNode) {
const context: ContextType<any> = processing.type;
const props = processing.props;
const renderFunc = props.children;
resetDepContexts(processing);
const contextVal = getNewContext(processing, context);
const newChildren = renderFunc(contextVal);
processing.children = createVNodeChildren(processing, newChildren);
return processing.children;
}

View File

@ -0,0 +1,105 @@
import type {VNode, ContextType, ProviderType} from '../Types';
import {createVNodeChildren} from './BaseComponent';
import {isSame} from '../utils/compare';
import {ClassComponent, ContextProvider} from '../vnode/VNodeTags';
import {pushForceUpdate} from '../UpdateHandler';
import {
getContextChangeCtx,
resetContextCtx,
setContextCtx,
} from '../ContextSaver';
import {getFirstChild, travelVNodeTree} from '../vnode/VNodeUtils';
import {launchUpdateFromVNode} from '../TreeBuilder';
import {onlyUpdateChildVNodes} from '../vnode/VNodeCreator';
import {setParentsChildShouldUpdate} from '../vnode/VNodeShouldUpdate';
export function captureRender(processing: VNode): Array<VNode> | null {
return captureContextProvider(processing);
}
export function bubbleRender(processing: VNode) {
resetContextCtx(processing);
}
function captureContextProvider(processing: VNode): Array<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.children = createVNodeChildren(processing, newElements);
return processing.children;
}
// 从依赖中找到匹配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 = getFirstChild(processing);
if (vNode === null) {
return;
}
let isMatch = false;
// 从vNode开始遍历
travelVNodeTree(vNode, (node) => {
const depContexts = node.depContexts;
if (depContexts.length) {
isMatch = matchDependencies(depContexts, context, node) ?? isMatch;
}
}, (node) => {
// 如果这是匹配的provider则不要更深入地扫描
return node.tag === ContextProvider && node.type === processing.type;
}, processing);
// 找到了依赖context的子节点触发一次更新
if (isMatch) {
launchUpdateFromVNode(processing);
}
}

View File

@ -0,0 +1,153 @@
import type { VNode } from '../../Types';
import type { Callback } from '../../UpdateHandler';
import {shallowCompare} from '../../utils/compare';
import {
pushUpdate,
newUpdate,
UpdateState,
processUpdates,
} from '../../UpdateHandler';
import { launchUpdateFromVNode } from '../../TreeBuilder';
import { FlagUtils } from '../../vnode/VNodeFlags';
import { getCurrentContext } from '../ClassComponent';
import { PureComponent } from '../../components/BaseClassComponent';
export function callDerivedStateFromProps(
processing: VNode,
getDerivedStateFromProps: (props: object, state: object) => object,
nextProps: object,
) {
if (typeof getDerivedStateFromProps === 'function') {
const oldState = processing.state;
// 调用class组件的getDerivedStateFromProps函数
const newState = getDerivedStateFromProps(nextProps, oldState);
// 组件未返回state,需要返回旧的preState
processing.state = newState === null || newState === undefined
? oldState
: { ...oldState, ...newState };
}
}
function changeStateContent(type: UpdateState, content: object, callback: Callback) {
// @ts-ignore
const vNode = this._vNode;
const update = newUpdate();
update.type = type;
if (type === UpdateState.Update || type === UpdateState.Override) {
update.content = content;
}
if (callback !== undefined && callback !== null) {
update.callback = callback;
}
pushUpdate(vNode, update);
launchUpdateFromVNode(vNode);
}
export function callShouldComponentUpdate(
processing: VNode,
oldProps: object,
newProps: object,
newState: object,
newContext: object,
) {
const inst = processing.realNode;
if (typeof inst.shouldComponentUpdate === 'function') {
return inst.shouldComponentUpdate(newProps, newState, newContext);
}
if (inst instanceof PureComponent) {
return !shallowCompare(oldProps, newProps) || !shallowCompare(inst.state, newState);
}
return true;
}
function setStateAndForceUpdateImpl(inst): void {
inst.setState = changeStateContent.bind(inst, UpdateState.Update);
inst.forceUpdate = changeStateContent.bind(inst, UpdateState.ForceUpdate, null);
}
export function callConstructor(processing: VNode, ctor: any, props: any): any {
const context = getCurrentContext(ctor, processing);
const inst = new ctor(props, context);
if (inst.state !== null && inst.state !== undefined) {
processing.state = inst.state;
}
setStateAndForceUpdateImpl(inst);
// 双向绑定processing和inst
processing.realNode = inst;
inst._vNode = processing;
return inst;
}
export function callComponentWillMount(processing, inst, newProps?) {
const oldState = inst.state;
if (typeof inst.componentWillMount === 'function') {
inst.componentWillMount();
}
if (typeof inst.UNSAFE_componentWillMount === 'function') {
inst.UNSAFE_componentWillMount();
}
if (oldState !== inst.state) {
changeStateContent.call(inst, UpdateState.Override, inst.state, null);
}
// 处理componentWillMount中可能存在的state更新行为
processUpdates(processing, inst, newProps);
inst.state = processing.state;
}
export function callComponentWillUpdate(inst, newProps, newState, nextContext) {
if (typeof inst.componentWillUpdate === 'function') {
inst.componentWillUpdate(newProps, newState, nextContext);
}
if (typeof inst.UNSAFE_componentWillUpdate === 'function') {
inst.UNSAFE_componentWillUpdate(newProps, newState, nextContext);
}
}
export function callComponentWillReceiveProps(inst, newProps: object, newContext: object) {
const oldState = inst.state;
if (typeof inst.componentWillReceiveProps === 'function') {
inst.componentWillReceiveProps(newProps, newContext);
}
if (typeof inst.UNSAFE_componentWillReceiveProps === 'function') {
inst.UNSAFE_componentWillReceiveProps(newProps, newContext);
}
if (inst.state !== oldState) {
changeStateContent.call(inst, UpdateState.Override, inst.state, null);
}
}
export function markComponentDidMount(processing: VNode) {
const inst = processing.realNode;
if (typeof inst.componentDidMount === 'function') {
FlagUtils.markUpdate(processing);
}
}
export function markGetSnapshotBeforeUpdate(processing: VNode) {
const inst = processing.realNode;
if (typeof inst.getSnapshotBeforeUpdate === 'function') {
FlagUtils.markSnapshot(processing);
}
}
export function markComponentDidUpdate(processing: VNode) {
const inst = processing.realNode;
if (typeof inst.componentDidUpdate === 'function') {
FlagUtils.markUpdate(processing);
}
}