整体流程:
react的核心可以用ui=fn(state)来表示,更详细可以用:
const state = reconcile(update);
const UI = commit(state);
上面的fn可以分为如下一个部分:
- Scheduler(调度器): 调度任务,排序优先级,让优先级高的任务先进行reconcile
- Reconciler(协调器): 生成Fiber对象,找出哪些节点发生了改变,并打上不同的Flags(旧版本 react叫Tag),著名的diff算法也是在这个阶段中执行的;
- Renderer(渲染器): 将Reconciler中打好标签的节点渲染到视图上
调度器
在react16之前,采用的是stack架构,所有任务只能同步进行,无法被打断,导致浏览器出现丢帧情况,表现出卡顿,react为了解决这个问题,从16之后进行了两大更新:
- 引入Fiber
- 新增了Scheduler
Scheduler在浏览器原生API中实际是有类似API的,reqestIleCallback虽然每一帧的绘制时间约为16.66ms,但是若屏幕没有刷新,那么浏览器会安排长度为50ms左右的空闲时间。
为什么是50ms?
据研究,100ms以内用户感觉都是瞬时发生的,不会感受到延迟感,因此将空闲时间设置为50,浏览器仍然还剩余50ms。
后期react打算重新开发这个Scheduler,这意味这调度器不仅仅是在react中使用,凡是有任务调度的项目都可以使用Scheduler。
协调器
协调器是render阶段的第二个阶段,类组件或者函数组件本身就是在这个阶段被调用的。
根据Scheduler调度结果的不同,协调器起点可能是不同的;
performSyncWorkOnRoot(同步更新流程)
会执行workLoopSync这个方法
performConcurrentWorkOnRoot(并发更新流程)
function workLoopSync (){while(workProgress != null){performUnitOfWork(workProgress)
}
}
function workLoopConcurrentSync (){while(workProgress != null && shouldYield()){performConcurrentWorkOnRoot(workProgress)}
}
新的架构使用Fiber对象来描述dom结构,最终需要形成一棵Fiber tree,不过这棵Fiber树是通过链表形式串联在一起的。
workInProgress代表的是当前的FiberNode
performUnitOfWork方法创建下一个FiberNode,并且还会将已经创建的FiberNode连接起来(child、return、sibling),从而形成一棵链表结构的Fiber tree。
若workInProgress为空,则表示没有下一个FiberNode,也就说整颗Fiber Tree已经构建完毕。
上面两个方法的区别是是否调用了shouldYield方法,该方法表明了是否可以中断。
performUnitOfWork在创建下一个FiberNode 时候,整体分为以下两大块:
- 递阶段
- 归阶段
递阶段
从HostRootFiber开始按照深度优先(先找到最底层)原则进行遍历,遍历到的每一个FiberNode执行beginWork方法,该方法会根据传入的FiberNode创建下一级的FiberNode,此时可能存在两种情况:
- 下一级只有一个元素,beginWork方法会创建对应的FiberNode,并且与workInProgress连接
- 下一级有多个元素,beginWork方法会依次创建所有的子FiberNode并且通过与subling连接到一起,每个FiberNode也会和workInProgress连接。
<ul>
<li></li>
<li></li>
<li></li>
</ul>
此时会创建3个li对应的FiberNode,连接情况如下:
所有的子Fiber依次连接
Li0Fiber.sibling = Li1Fiber
Li1Fiber.sibling = Li2Fiber
子Fiber还要和父Fiber连接
Li0Fiber.return = UlFiber
Li1Fiber.return = UlFiber
Li2Fiber.return = UlFiber
由于采用的是深度优先原则,因此无法再往下走的时候,会走入归阶段。
归阶段
归阶段会调用completeWork方法来处理FiberNode,做副作用的收集。
当某个FiberNode执行完了completeWork,若存在兄弟元素,就会进入到兄弟元素的递阶段,若不存在兄弟元素,就会进入父FiberNode的归阶段。
我们来看一张图:
渲染器
Renderer工作阶段称为commit阶段,该阶段会将各种副作用commit到宿主环境的UI中。
相较于之前的render阶段可以被打断,commit阶段一旦开始就会同步执行直到完成渲染工作。
整个渲染过程可以分为三个子阶段:
beforeMutation
Mutation
Layout