function createFiberRoot(
containerInfo: any,
isConcurrent: boolean,
hydrate: boolean,
): FiberRoot {
// Cyclic construction. This cheats the type system right now because
// stateNode is any.
const uninitializedFiber = createHostRootFiber(isConcurrent);
// ....
}
function createHostRootFiber(isConcurrent: boolean): Fiber {
let mode = isConcurrent ? ConcurrentMode | StrictMode : NoContext;
if (enableProfilerTimer && isDevToolsPresent) {
// Always collect profile timings when DevTools are present.
// This enables DevTools to start capturing timing at any point–
// Without some nodes in the tree having empty base times.
mode |= ProfileMode;
}
function createFiberFromTypeAndProps(
type: any, // React$ElementType
key: null | string,
pendingProps: any,
owner: null | Fiber,
mode: TypeOfMode,
expirationTime: ExpirationTime,
): Fiber {
let fiber;
let fiberTag = IndeterminateComponent;
// The resolved type is set if we know what the final type will be. I.e. it's not lazy.
let resolvedType = type;
if (typeof type === 'function') {
if (shouldConstruct(type)) {
// component 组件
fiberTag = ClassComponent;
}
// 原生 dom 标签
} else if (typeof type === 'string') {
fiberTag = HostComponent;
} else {
getTag: switch (type) {
case REACT_FRAGMENT_TYPE:
return createFiberFromFragment(
pendingProps.children,
mode,
expirationTime,
key,
);
case REACT_CONCURRENT_MODE_TYPE:
return createFiberFromMode(
pendingProps,
mode | ConcurrentMode | StrictMode,
expirationTime,
key,
);
case REACT_STRICT_MODE_TYPE:
return createFiberFromMode(
pendingProps,
mode | StrictMode,
expirationTime,
key,
);
case REACT_PROFILER_TYPE:
return createFiberFromProfiler(pendingProps, mode, expirationTime, key);
case REACT_SUSPENSE_TYPE:
return createFiberFromSuspense(pendingProps, mode, expirationTime, key);
default: {
if (typeof type === 'object' && type !== null) {
switch (type.$$typeof) {
case REACT_PROVIDER_TYPE:
fiberTag = ContextProvider;
break getTag;
case REACT_CONTEXT_TYPE:
// This is a consumer
fiberTag = ContextConsumer;
break getTag;
case REACT_FORWARD_REF_TYPE:
fiberTag = ForwardRef;
break getTag;
case REACT_MEMO_TYPE:
fiberTag = MemoComponent;
break getTag;
case REACT_LAZY_TYPE:
fiberTag = LazyComponent;
resolvedType = null;
break getTag;
}
}
// ...
}
}
}
fiber = createFiber(fiberTag, pendingProps, key, mode);
fiber.elementType = type;
fiber.type = resolvedType;
fiber.expirationTime = expirationTime;
return fiber;
}
在 mountIndeterminateComponent 中确定 function 组件类型
case IndeterminateComponent: {
const elementType = workInProgress.elementType;
return mountIndeterminateComponent(
current,
workInProgress,
elementType,
renderExpirationTime,
);
}
function mountIndeterminateComponent(
_current,
workInProgress,
Component,
renderExpirationTime,
) {
// 只有在第一次渲染的时候,才会有 mountIndeterminateComponent 这种情况,非首次渲染,已经发现 mountIndeterminateComponent 的具体的类型
// 如果在第一次渲染中,current 存在,可能是中途抛出 error 或者 promise的情况,就是 suspended 组件的情况,把 current 和 workInprogress 的联系去除,需要重新创建 workInprogress,重新进行一次初次渲染的流程
if (_current !== null) {
// An indeterminate component only mounts if it suspended inside a non-
// concurrent tree, in an inconsistent state. We want to tree it like
// a new mount, even though an empty version of it already committed.
// Disconnect the alternate pointers.
_current.alternate = null;
workInProgress.alternate = null;
// Since this is conceptually a new fiber, schedule a Placement effect
workInProgress.effectTag |= Placement;
}
// context 相关
const props = workInProgress.pendingProps;
const unmaskedContext = getUnmaskedContext(workInProgress, Component, false);
const context = getMaskedContext(workInProgress, unmaskedContext);
prepareToReadContext(workInProgress, renderExpirationTime);
let value;
if (__DEV__) {
// ...
} else {
// 调用方法
value = Component(props, context);
}
// React DevTools reads this flag.
workInProgress.effectTag |= PerformedWork;
if (
typeof value === 'object' &&
value !== null &&
// 符合 class 组件条件
// 这里如果函数里返回一个具有 render Function 属性的对象,也会被当作 class 组件调用
typeof value.render === 'function' &&
value.$$typeof === undefined
) {
// Proceed under the assumption that this is a class instance
workInProgress.tag = ClassComponent;
// Push context providers early to prevent context stack mismatches.
// During mounting we don't know the child context yet as the instance doesn't exist.
// We will invalidate the child context in finishClassComponent() right after rendering.
let hasContext = false;
if (isLegacyContextProvider(Component)) {
hasContext = true;
pushLegacyContextProvider(workInProgress);
} else {
hasContext = false;
}
workInProgress.memoizedState =
value.state !== null && value.state !== undefined ? value.state : null;
const getDerivedStateFromProps = Component.getDerivedStateFromProps;
if (typeof getDerivedStateFromProps === 'function') {
applyDerivedStateFromProps(
workInProgress,
Component,
getDerivedStateFromProps,
props,
);
}
adoptClassInstance(workInProgress, value);
mountClassInstance(workInProgress, Component, props, renderExpirationTime);
return finishClassComponent(
null,
workInProgress,
Component,
true,
hasContext,
renderExpirationTime,
);
} else {
// 否则标记为 FunctionComponent,直接调和子节点
// Proceed under the assumption that this is a function component
workInProgress.tag = FunctionComponent;
if (__DEV__) {
// ...
}
// value 即子节点
reconcileChildren(null, workInProgress, value, renderExpirationTime);
return workInProgress.child;
}
}
HostComponent
dom 原生标签对应的 fiber 节点
没有 state 概念
根据标签 type 和 props 判断标签内容是不是纯文本,如果是纯文本则没有新子节点;如果不是并且 prevProps 是纯文本那么标记 effectTag
最后调和创建子节点
function updateHostComponent(current, workInProgress, renderExpirationTime) {
pushHostContext(workInProgress);
if (current === null) {
// 服务端渲染,复用节点
tryToClaimNextHydratableInstance(workInProgress);
}
const type = workInProgress.type;
const nextProps = workInProgress.pendingProps;
const prevProps = current !== null ? current.memoizedProps : null;
let nextChildren = nextProps.children;
// 判断是否为纯文本节点的 children
const isDirectTextChild = shouldSetTextContent(type, nextProps);
if (isDirectTextChild) {
// 纯文本节点,则没有children
nextChildren = null;
} else if (prevProps !== null && shouldSetTextContent(type, prevProps)) {
// If we're switching from a direct text child to a normal child, or to
// empty, we need to schedule the text content to be reset.
// 原来的 props 值是纯文本,新的 props 是节点,标记替换
workInProgress.effectTag |= ContentReset;
}
// 标记 ref,仅有在 classComponent 和 HostComponent (原生标签)中标记ref
markRef(current, workInProgress);
// Check the host config to see if the children are offscreen/hidden.
if (
renderExpirationTime !== Never &&
workInProgress.mode & ConcurrentMode && // ConcurrentMode
shouldDeprioritizeSubtree(type, nextProps) // !!props.hidden;
) {
// 异步渲染模式下,具有 hidden 属性的组件永远不被更新
// Schedule this fiber to re-render at offscreen priority. Then bailout.
workInProgress.expirationTime = Never;
return null;
}
// 调和创建子节点
reconcileChildren(
current,
workInProgress,
nextChildren,
renderExpirationTime,
);
return workInProgress.child;
}
// shouldSetTextContent
// 只能渲染文本的标签 textarea、option、noscript
// children 为 string、number直接渲染为文本
// 使用了 dangerouslySetInnerHTML 属性的
function shouldSetTextContent(type: string, props: Props): boolean {
return (
type === 'textarea' ||
type === 'option' ||
type === 'noscript' ||
typeof props.children === 'string' ||
typeof props.children === 'number' ||
(typeof props.dangerouslySetInnerHTML === 'object' &&
props.dangerouslySetInnerHTML !== null &&
props.dangerouslySetInnerHTML.__html != null)
);
}
HostText
纯文本节点,没有子节点
在 complete 阶段才插入到 dom 中
function updateHostText(current, workInProgress) {
if (current === null) {
// 服务端渲染
tryToClaimNextHydratableInstance(workInProgress);
}
// Nothing to do here. This is terminal. We'll do the completion step
// immediately after.
return null;
}
IndeterminateComponent
Component
创建时期
ReactDOM.render App
组件时会创建FiberRoot -> createHostRootFiber
,标记这个fiber
的tag
为HostRoot
function createHostRootFiber(isConcurrent: boolean): Fiber { let mode = isConcurrent ? ConcurrentMode | StrictMode : NoContext;
if (enableProfilerTimer && isDevToolsPresent) { // Always collect profile timings when DevTools are present. // This enables DevTools to start capturing timing at any point– // Without some nodes in the tree having empty base times. mode |= ProfileMode; }
return createFiber(HostRoot, null, null, mode); }
在 mountIndeterminateComponent 中确定 function 组件类型
HostComponent
dom
原生标签对应的fiber
节点state
概念type
和props
判断标签内容是不是纯文本,如果是纯文本则没有新子节点;如果不是并且prevProps
是纯文本那么标记effectTag
HostText
complete
阶段才插入到dom
中