著有《React 源码》《React 用到的一些算法》《javascript地月星》等多个专栏。欢迎关注。
文章不好写,要是有帮助别忘了点赞,收藏,评论 ~你的鼓励是我继续挖干货的动力🔥。
另外,本文为原创内容,商业转载请联系作者获得授权,非商业转载需注明出处,感谢理解~
前言
这一篇介绍Fiber树 App之前的“头部”节点 FiberRootNode HostRootFiber的构建过程,然后梳理名称很像的FiberRootNode HostRootFiber HostRoot和功能很像(其实完全不一样)的stateNode containerInfo。
总流程
创建FiberRootNode和HostRootFiber->创建workInProgress->创建App和后续节点。
阶段一:遍历Fiber树前
createRoot()->createFiberRoot(),创建FiberRootNode和HostRootFiber。
createRoot(document.getElementById('root'))
//这个函数里面创建new FiberRootNode和创建HostRootFiber(createHostRootFiber)
function createFiberRoot(containerInfo, tag, hydrate, initialChildren, hydrationCallbacks, isStrictMode, concurrentUpdatesByDefaultOverride,
identifierPrefix, onRecoverableError, transitionCallbacks) {
//1.创建FiberRootNode
var root = new FiberRootNode(containerInfo, tag, hydrate, identifierPrefix, onRecoverableError);
//2.创建HostRootFiber
var uninitializedFiber = createHostRootFiber(tag, isStrictMode);
//3.FiberRootNode.current = HostRootFiber
root.current = uninitializedFiber;
//4.HostRootFiber.stateNode = FiberRootNode
uninitializedFiber.stateNode = root;
{
var _initialState = {
element: initialChildren,
isDehydrated: hydrate,
cache: null,
// not enabled yet
transitions: null,
pendingSuspenseBoundaries: null
};
uninitializedFiber.memoizedState = _initialState;
}
initializeUpdateQueue(uninitializedFiber);
return root;
}
createRoot().render()->prepareFreshStack()->createWorkInProgress()创建第一个workInProgress。
createRoot(document.getElementById('root')).render(<App />)
function prepareFreshStack(root, lanes) {
...
// root是FiberRootNode root.current是HostRootFiber
var rootWorkInProgress = createWorkInProgress(root.current, null);
...
}
function createWorkInProgress(current, pendingProps) {
// 1.此时是null
var workInProgress = current.alternate;
if (workInProgress === null) {
// 2.创建workInprogress
workInProgress = createFiber(current.tag, pendingProps, current.key, current.mode);
workInProgress.elementType = current.elementType;
workInProgress.type = current.type;
//3. current.sateNode 是HostRootFiber.stateNode是FiberRootNode
workInProgress.stateNode = current.stateNode;
{
// DEV-only fields
workInProgress._debugSource = current._debugSource;
workInProgress._debugOwner = current._debugOwner;
workInProgress._debugHookTypes = current._debugHookTypes;
}
// 4. cur和wip相互指向
workInProgress.alternate = current;
current.alternate = workInProgress;
} else {
...
}
...
}
return workInProgress;
}
阶段二:正式遍历Fiber树
prepareFreshStack后,遍历Fiber树,创建App节点以及后续的节点。
- updateHostRoot的参数current和workInProgress是上面创建的HostRootFiber。
- reconcileChildren,创建HostRootFiber的子节点App。
- 首次挂载的时候,App节点还没有current。但是HostRootFiber有current和workInProgress。
在源码中经常使用if(current === null){}来判断是挂载节点还是更新节点。
function renderRootSync(){
...
if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {
...
// root是fiberRootNode, tag=1
prepareFreshStack(root, lanes)//创建HostRootFiber的wip,cur和wip相互指向
}
...
// prepareFreshStack后马上进入到do-while循环遍历fiber树
do {
try {
workLoopSync(); //这一步创建tag=3的子fiber,因为进入到beginWork-updateHostRoot-reconcileChildren
break;
} catch (thrownValue) {
handleError(root, thrownValue);
}
} while (true);
...
}
function updateHostRoot(current, workInProgress, renderLanes) {
...
reconcileChildren(current, workInProgress, nextChildren, renderLanes);
}
function reconcileChildren(current, workInProgress, nextChildren, renderLanes) {
if (current === null) {
workInProgress.child = mountChildFibers(workInProgress, null, nextChildren, renderLanes);
} else {
// current是HostRootFiber,workInProgress是HostRootFiber,创建App
workInProgress.child = reconcileChildFibers(workInProgress, current.child, nextChildren, renderLanes);
}
}
梳理FiberRootNode HostRootFiber HostRoot
- FiberRootNode不是Fiber节点,tag有两种类型:
var LegacyRoot = 0; //传统的同步渲染模式 React 16 之前的版本中唯一的模式 更新都是同步的、不可中断的
var ConcurrentRoot = 1; //并发渲染模式 React 18 引入的全新模式
Fiber树切换的时候,切换的是FiberRootNode的current,Fiber树切换:
function commitRootImpl(root, recoverableErrors, transitions, renderPriorityLevel) {
...
if (subtreeHasEffects || rootHasEffect) {
...
root.current = finishedWork;
}else{
root.current = finishedWork;
...
}
...
}
- HostRootFiber是一个Fiber节点,是HostRoot的Fiber节点。
- HostRoot是一个tag,表示Fiber节点的类型(tag)。
HostRootFiber是一种Fiber节点,tag有:
var FunctionComponent = 0;
var ClassComponent = 1;
var IndeterminateComponent = 2; // Before we know whether it is function or class
var HostRoot = 3; // Root of a host tree. Could be nested inside another node.
var HostPortal = 4; // A subtree. Could be an entry point to a different renderer.
...
HostRootFiber.tag = HostRoot = 3。
结语
Fiber树不是“一体成型”的,是由头部 + App及以下节点拼接而成。真正的遍历Fiber树是从App开始的。
FiberRootNode HostRootFiber HostRoot有点复杂的,不熟悉的话容易搞混。
除此之外,还有stateNode和containerInfo也容易搞乱,下篇继续梳理stateNode和containerInfo。
相关阅读:React DOM树的构建原理和算法