一、前言
在之前的一篇文章中react fiber概念及原理,介绍了react v16中fiber扮演了什么样的一个角色,以及它的工作原理。概念和原理只能让你有一个初步的认识,想要深入的了解fiber,那么必须要从源码入手,看看fiber具体的实现是怎样的。接下来的内容将结合思维导图和代码描述react fiber在代码中如何生成。
二、react三个阶段
首先,react的设计理念是把整个应用看成是一个视图,当首屏渲染或者更新时,就会根据一系列过程生成新的视图,并渲染。这一系列过程伴随fiber的出现而发生了一些改变,react v16之前,react主要有两个阶段,分别是
1.协调(Reconciler),根据状态的变化,找出变化的组件,并打上标记
2.渲染(Renderder),根据被打上标记的组件,更新视图
react v16及之后,react在原有两个阶段的基础上增加了一个调度过程(Scheduler),主要负责任务的调度(此文不涉及)
而我们关心的fiber生成就是发生在在协调阶段(Reconciler)过程中,刚才我们说到,react在首屏渲染和更新时都会经历协调阶段,所以我们分首屏渲染和更新两种情况来看fiber生成过程。
三、首屏渲染时的fiber
根据react文档,reactDom创建应用有三种模式,分别是
1. legacy -- ReactDOM.render(, rootNode)
2. blocking -- ReactDOM.createBlockingRoot(rootNode).render()
3. concurrent -- ReactDOM.createRoot(rootNode).render()
此次我们讨论是的legacy模式,也是目前react默认的模式,我先根据react官方文档运行demo,看一下首屏渲染时的调用栈
function legacyRenderSubtreeIntoContainer(parentComponent, children, container, forceHydrate, callback) {
var root = container._reactRootContainer;
var fiberRoot;
if (!root) {
// Initial mount
root = container._reactRootContainer = legacyCreateRootFromDOMContainer(container, forceHydrate);
fiberRoot = root._internalRoot;
unbatchedUpdates(function () {
updateContainer(children, fiberRoot, parentComponent, callback);
});
} else {
//...do something
}
return getPublicRootInstance(fiberRoot);
}
在legacyRenderSubtreeIntoContainer中,因为是首次渲染,所以会创建rootFiberNode和rootFiber,然后将rootFiber作为此次渲染的workInProgress fiber,也就是一下renderRootSync函数中的第一个参数
renderRootSync也就是开始创建fiber的入口函数
function renderRootSync(root, lanes) {
//...do something
do {
try {
workLoopSync();
break;
} catch (thrownValue) {
handleError(root, thrownValue);
}
} while (true);
//...do something
return workInProgressRootExitStatus;
}
function workLoopSync() {
while (workInProgress !== null) {
performUnitOfWork(workInProgress);
}
}
function performUnitOfWork(unitOfWork) {
var current = unitOfWork.alternate;
setCurrentFiber(unitOfWork);
var next;
if ( (unitOfWork.mode & ProfileMode) !== NoMode) {
startProfilerTimer(unitOfWork);
next = beginWork$1(current, unitOfWork, subtreeRenderLanes);
stopProfilerTimerIfRunningAndRecordDelta(unitOfWork, true);
} else {
next = beginWork$1(current, unitOfWork, subtreeRenderLanes);
}
resetCurrentFiber();
unitOfWork.memoizedProps = unitOfWork.pendingProps;
if (next === null) {
completeUnitOfWork(unitOfWork);
} else {
workInProgress = next;
}
ReactCurrentOwner$2.current = null;
}
从代码上看react会执行一次循环,最终会运行performUnitOfWork这个函数,上面代码看着比较难懂,这里结合图来理解一下。我们知道fiber是类似于链表的数据结构,那么reactDom其实是通过递归的方式生成fiber,递阶段就是beginWork(最终调用createFiberFromElement返回一个fiber节点),归阶段就是completeWork(生成dom并挂载在父节点,更新处理props以及生成副作用链表),我们用图解的方式来表示一下整个递归的过程,假设我们的jsx是这样的结构
<div>
<header>
<span>test</span>
这是一个头
</header>
<p>这也是一段话</p>
</div>
那么它对应的fiber数据就是
所以整个过程就是:
- 递阶段(beginWork)依次生成div、header、span的fiber,span没有child,所以进行2流程
- 归阶段(completeWork)处理span、header,生成dom,赋值为stateNode属性,并挂载到父节点的dom上,header有一个sibling节点,所以进行3流程
- 递阶段(beginWork)生成p的fiber,p没有child,所以进行4流程
- 归阶段(completeWork)处理p、div,rootFiber生成dom,赋值为stateNode属性,并挂载到父节点的dom上
如此可以得到最终的一个fiber数据结构,且应用的根节点其中rootFiber已经有了一个完整的dom树,然后进行渲染(Renderer)
四、更新时的fiber
上面说到react的设计理念是将整个应用看做是一个视图,所以reactDom在开始Reconciler时都是以rootFiber为起点。在更新时,当rooFiber的alternate不存在时,会克隆一个current fiber作为此次的workInProgress fiber,当rootFiber的alternate存在时,就将其作为此次更新的workInProgress。然后同样的进行上面的描述的流程。不同的是这次递归会在递阶段根据需要更新组件的jsx和workInProgress fiber进行对比生成新的fiber,也就是大名鼎鼎的diff算法。(alternate在此文中介绍react fiber概念及原理)
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!