开始之前,先讲一下该文章能帮你解决哪些问题?
开始之前,先讲一下该文章能帮你解决哪些问题?

  • facebook为什么要使用重构React
  • React Fiber是什么
  • React Fiber的核心算法 - react是如何中断重启任务的
  • react fiber部分源码简化版

前言

该文章涉及的源码部分基于React v17.0.2

why React Fiber

  • 浏览器渲染过程

从浏览器的运行机制谈起。大家都知道,浏览器是多进程多线程的,多进程包括主进程,渲染进程,插件进程,GPU进程等,作为前端开发者,我们主要关注其中的渲染进程,这里是页面渲染,HTML解析,css解析,js执行所在的地方。在渲染进程中包括多个线程,此次核心关注页面渲染的两个线程,GUI线程和JS线程。

GUI线程负责浏览器界面的渲染,包括解析HTML,CSS,布局绘制等;js线程包含我们通常编写的js代码的解析引擎,最有名的就是google的V8。需要注意的一点是,js引擎和GUI渲染是互斥的,因为JS可能会更改HTML或者CSS样式,如果同时执行会导致页面渲染混乱,所以当JS引擎执行时,GUI渲染线程会被挂起,等JS引擎执行完立即执行。

  • GPU渲染

我们通常看到的动画,视频本质上是通过一张张图片快速的闪过,欺骗人类的双眼,让人以为是连续的动画,每秒内包含的图片越多动画越流畅,正常60张图片可以让人眼感觉是流畅的动画,所以当前大部分设备的FPS是60,即,每秒60帧。所以Chrome要在16ms的时间内执行完下图的渲染任务,才能让用户感觉不到掉帧。

所以,如果JS执行时间过长,基本上超过10ms之后,用户会感觉到明显的卡顿,很影响用户体验(下文中js执行都以16ms为分界点,不计算后续的渲染,实际的可执行时间肯定小于16ms)。而React执行是要进行两棵树的diff,虽然React根据html的特性对diff算法做了优化,但是如果两棵树比对的层级较深,依旧会远远超过16ms。

相关参考视频讲解:进入学习

React Fiber

基于此,那如何解决问题呢?在上图中,React作为js,所有的同步操作执行在最开始,在React执行完成后,后续的html解析,布局渲染等操作才会执行。最容易想到的就是,优化JS的执行速度,把React占用线程的时间缩短到16ms以内。在React执行中,最耗时的就是diff算法,React针对html这种场景下做了优化,业界已经没有更好的算法可以缩短diff算法的时间,所以当树的层次很深时,执行时间依旧很长。

那还有什么办法呢,我们依旧可以看上图,在现代浏览器中,浏览器为了让开发者知道浏览器执行完当前帧所有的操作后,还有多长时间可以使用,提供了requestIdleCallback这么一个方法,据此我们可以知道当前还有多长时间可以执行。

requestIdleCallback
requestIdleCallback((deadline) => {while ((deadline.timeRemaining() > 0 || deadline.didTimeout) && nextComponent) {nextComponent = performWork(nextComponent);}
});

题外话:

有兴趣可以在控制台执行输出一下requestIdleCallback回调参数的requestIdleCallback((deadline) ,在不同的网页上得到的时间可能不同。甚至可能会超过16ms(在React官网就显示49.9ms)因为requestIdleCallback的一些限制原因,React源码中未使用requestIdleCallback,而是自己实现了一套类似的机制。

使用此方法我们知道每帧的剩余时间之后,这样就可以在剩余时间内进行工作,如果当前帧时间不够,就把剩余的工作放到下一帧的requestIdleCallback中执行。这就是React所说的时间切片(time slicing)。

所以要使用此方法,需要把基于js内置栈调用的同步递归遍历的diff算法改为异步增量更新。按照React负责人的说法就是

如果你仅仅依赖js内置的调用栈,它会一直执行直到栈为空…,如果我们可以任意的中断并且手动的操作调用栈,不是更完美吗?这就是React Fiber的目的。Fiber是针对React Component的栈的重新实现。你可以认为一个Fiber就是一个虚拟的栈中的一项任务。

说人话,就是原来树的递归是深度递归遍历,现在需要把递归算法重新实现,以便于我不依赖于栈的调用,可以对react组件一个一个节点的遍历,中途任意时间可以中断和从当前开始。

stack Reconciliation vs Fiber Reconciliation

stack Reconciliation

假如我们有如下一个html结构

转化成类React组件的js对象如下

const a1 = {name: 'a1'};
const b1 = {name: 'b1'};
const b2 = {name: 'b2'};
const b3 = {name: 'b3'};
const c1 = {name: 'c1'};
const c2 = {name: 'c2'};
const d1 = {name: 'd1'};
const d2 = {name: 'd2'};a1.render = () => [b1, b2, b3];
b1.render = () => [];
b2.render = () => [c1];
b3.render = () => [c2];
c1.render = () => [d1, d2];
c2.render = () => [];
d1.render = () => [];
d2.render = () => [];

正常情况,我们会使用像下面这种方式递归来遍历这棵"树",在React最早的版本就是基于此来递归遍历dom树

function walk(instance) {console.log(instance.name);let children = instance.render();children.forEach((child) => {walk(child);});
}
walk(a1);

可以看到,这种方式,是可以遍历完整棵树,可是它没办法做到我们之前所说的中断递归,如果你中途中断了递归这棵树,下次你要重新从根节点整个遍历。这显然是不行的,它只能不断递归遍历,直到stack调用栈为空。那React Fiber是如何中断重启任务呢?

答案是单链表树遍历算法。简单来说就是把原来树本身的嵌套结构,改为单链表形式的树。

Fiber Reconciliation

React具体是如何使用链表遍历树呢?为了实现这种算法,首先先看下我们需要的数据结构

  • child,指向该节点第一个子节点
  • sibling,指向该节点的下一个兄弟节点
  • return,指向该节点的父节点

还是之前的dom树结构,现在变成了这样

构建Fiber树的过程就不描述了,我们直接看遍历算法(父节点优先,深度优先)

let root = fiber;
let node = fiber;
while (true) {if (node.child) {node = node.child;continue;}if (node === root) {return;}while (!node.sibling) {if (!node.return || node.return === root) {return;}node = node.return;}node = node.sibling;
}

可以看到,拿到根节点后,不断遍历子节点,直到最深节点,然后从最深的子节点开始遍历兄弟节点,如果没有兄弟节点就返回该节点父节点,如果有兄弟节点,把每个兄弟节点的子节点遍历完,直到最后一个子节点,然后返回父节点。这样不断遍历,直到返回根节点。

下面是在React源码中Fiber的数据对象。其实说到底,Fiber就是一个对象。他相对于之前React createElement生成的element对象,多了一层数据结构来支撑上述的单链表遍历算法。

Fiber数据结构

下面是React源码中的Fiber对象的属性,具体可以直接看注释。

function FiberNode(tag: WorkTag,  pendingProps: mixed,  key: null | string,  mode: TypeOfMode
) {// Instancethis.tag = tag; //Fiber标记是什么类型的Fiber/component,WorkTag 0-24this.key = key; // 唯一标识this.elementType = null;this.type = null;this.stateNode = null; //stateNode:class div// Fiber 数据结构this.return = null; // 父节点this.child = null; // 第一个子节点this.sibling = null; // 兄弟节点this.index = 0; //this.ref = null;this.pendingProps = pendingProps; //newpropsthis.memoizedProps = null; // oldProps 上次的props// updateQueue数据结构://  {//   baseState: fiber.memoizedState,//   firstBaseUpdate: null,//   lastBaseUpdate: null,//   shared: {//     pending: null,//     interleaved: null,//     lanes: NoLanes,//   },//   effects: null,// };this.updateQueue = null; // 批处理队列this.memoizedState = null; //oldStatethis.dependencies = null;this.mode = mode;// Effectsthis.flags = NoFlags; // 标记该fiber变更方式this.subtreeFlags = NoFlags;this.deletions = null;// 优先级调度this.lanes = NoLanes; this.childLanes = NoLanes;this.alternate = null; //work-in-progress current互为alternate
}

fiber带来的效果提升

  1. 可以通过看下重构前后的对比Demo,体会一下带来的体验提升
  2. 为后续React Concurrent模式做了基础

Fiber流转过程

画了一个简单的流程图说明Fiber的流转流程。

图示说明:

react在performUnitOfWork和completeUnitOfWork两个方法中,处理上述Fiber遍历算法的逻辑,在beginwork和completeWork中完成处理组件的逻辑。
在beginwork中会处理state的更新,此阶段相应生命周期的调用,reconcile的过程(给Fiber节点打上新增,删除,移动等标记的过程。在completeWork阶段,会把所有flags的标记,冒泡到父节点。以便于在commit阶段更新。

我记得Dan Abramov对effect list有过一个形象的比喻,可以写一下(大致意思是这样)

你可以把react fiber看做一棵圣诞树,effect list就是这颗圣诞树上悬挂的装饰灯

React源码 —太长不看系列

下面是React中关于Fiber的一些核心源码—已删除了很多跟此次文章无关的代码,大家可以自行选择是否服用。

包含代码注释,及代码在React仓库中的所在位置。大家可以直接看代码注释,不作具体解读了。

// https://github.com/facebook/react/blob/v17.0.2/packages/react-reconciler/src/ReactFiberWorkLoop.old.js#L1635
function workLoopConcurrent() {// Perform work until Scheduler asks us to yieldwhile (workInProgress !== null && !shouldYield()) {performUnitOfWork(workInProgress);}
}

performUnitOfWork

// https://github.com/facebook/react/blob/v17.0.2/packages/react-reconciler/src/ReactFiberWorkLoop.old.js#L1642
function performUnitOfWork(unitOfWork: Fiber): void {const current = unitOfWork.alternate;let next;// 一直返回unitOfWork.child,不会处理siblingnext = beginWork(current, unitOfWork, subtreeRenderLanes);unitOfWork.memoizedProps = unitOfWork.pendingProps;// 该fiber需要做的处理完成,返回下一个待处理的fiberif (next === null) {// 到达该链路的最底层的叶子节点,在该函数中处理sibling节点completeUnitOfWork(unitOfWork);} else {workInProgress = next;}
}

beginWork

// https://github.com/facebook/react/blob/v17.0.2/packages/react-reconciler/src/ReactFiberBeginWork.old.js#L3083
function beginWork(current: Fiber | null,  workInProgress: Fiber,  renderLanes: Lanes
): Fiber | null {let updateLanes = workInProgress.lanes;// tag有很多,这里只保留了常用的FunctionComponent和ClassComponent,后续只看updateClassComponentswitch (workInProgress.tag) {case FunctionComponent: {const Component = workInProgress.type;const unresolvedProps = workInProgress.pendingProps;const resolvedProps =workInProgress.elementType === Component? unresolvedProps: resolveDefaultProps(Component, unresolvedProps);return updateFunctionComponent(current,workInProgress,Component,resolvedProps,renderLanes);}case ClassComponent: {const Component = workInProgress.type;const unresolvedProps = workInProgress.pendingProps;const resolvedProps =workInProgress.elementType === Component? unresolvedProps: resolveDefaultProps(Component, unresolvedProps);// 返回值为workInProgress.child,可以在finishClassComponent中看到return updateClassComponent(current,workInProgress,Component,resolvedProps,renderLanes);}}
}
function updateClassComponent(current: Fiber | null,  workInProgress: Fiber,  Component: any,  nextProps: any,  renderLanes: Lanes
) {const instance = workInProgress.stateNode;let shouldUpdate;// 在此阶段处理更新生命周期和批处理的更新,if (instance === null) {if (current !== null) {// A class component without an instance only mounts if it suspended// inside a non-concurrent tree, in an inconsistent state. We want to// treat 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 effectworkInProgress.flags |= Placement;}// In the initial pass we might need to construct the instance.constructClassInstance(workInProgress, Component, nextProps);mountClassInstance(workInProgress, Component, nextProps, renderLanes);shouldUpdate = true;} else if (current === null) {// In a resume, we'll already have an instance we can reuse.复用之前未完成shouldUpdate = resumeMountClassInstance(workInProgress,Component,nextProps,renderLanes);} else {// 在此阶段处理生命周期和批处理的更新shouldUpdate = updateClassInstance(current,workInProgress,Component,nextProps,renderLanes);}const nextUnitOfWork = finishClassComponent(current,workInProgress,Component,shouldUpdate,hasContext,renderLanes);return nextUnitOfWork;
}function finishClassComponent(current: Fiber | null,  workInProgress: Fiber,  Component: any,  shouldUpdate: boolean,  hasContext: boolean,  renderLanes: Lanes
) {const instance = workInProgress.stateNode;// RerenderReactCurrentOwner.current = workInProgress;let nextChildren;nextChildren = instance.render();//初始化或者执行dom diff   //ReactChildFiber.old.jsreconcileChildren(current, workInProgress, nextChildren, renderLanes);  //childreturn workInProgress.child;
}

completeUnitOfWork

// https://github.com/facebook/react/blob/v17.0.2/packages/react-reconciler/src/ReactFiberWorkLoop.old.js#L1670
function completeUnitOfWork(unitOfWork: Fiber): void {// Attempt to complete the current unit of work, then move to the next// sibling. If there are no more siblings, return to the parent fiber.let completedWork = unitOfWork;do {const current = completedWork.alternate;const returnFiber = completedWork.return;let next;// 返回值一直为nullnext = completeWork(current, completedWork, subtreeRenderLanes);const siblingFiber = completedWork.sibling;if (siblingFiber !== null) {// If there is more work to do in this returnFiber, do that next.workInProgress = siblingFiber;return;}// Otherwise, return to the parentcompletedWork = returnFiber;// Update the next thing we're working on in case something throws.workInProgress = completedWork;} while (completedWork !== null);
}// https://github.com/facebook/react/blob/v17.0.2/packages/react-reconciler/src/ReactFiberCompleteWork.old.js#L645
function completeWork(current: Fiber | null,  workInProgress: Fiber,  renderLanes: Lanes,
): Fiber | null {const newProps = workInProgress.pendingProps;switch (workInProgress.tag) {case FunctionComponent:bubbleProperties(workInProgress);return null;case ClassComponent: {const Component = workInProgress.type;if (isLegacyContextProvider(Component)) {popLegacyContext(workInProgress);}bubbleProperties(workInProgress);return null;}}
  • facebook为什么要使用重构React
  • React Fiber是什么
  • React Fiber的核心算法 - react是如何中断重启任务的
  • react fiber部分源码简化版

前言

该文章涉及的源码部分基于React v17.0.2

why React Fiber

  • 浏览器渲染过程

从浏览器的运行机制谈起。大家都知道,浏览器是多进程多线程的,多进程包括主进程,渲染进程,插件进程,GPU进程等,作为前端开发者,我们主要关注其中的渲染进程,这里是页面渲染,HTML解析,css解析,js执行所在的地方。在渲染进程中包括多个线程,此次核心关注页面渲染的两个线程,GUI线程和JS线程。

GUI线程负责浏览器界面的渲染,包括解析HTML,CSS,布局绘制等;js线程包含我们通常编写的js代码的解析引擎,最有名的就是google的V8。需要注意的一点是,js引擎和GUI渲染是互斥的,因为JS可能会更改HTML或者CSS样式,如果同时执行会导致页面渲染混乱,所以当JS引擎执行时,GUI渲染线程会被挂起,等JS引擎执行完立即执行。

  • GPU渲染

我们通常看到的动画,视频本质上是通过一张张图片快速的闪过,欺骗人类的双眼,让人以为是连续的动画,每秒内包含的图片越多动画越流畅,正常60张图片可以让人眼感觉是流畅的动画,所以当前大部分设备的FPS是60,即,每秒60帧。所以Chrome要在16ms的时间内执行完下图的渲染任务,才能让用户感觉不到掉帧。

所以,如果JS执行时间过长,基本上超过10ms之后,用户会感觉到明显的卡顿,很影响用户体验(下文中js执行都以16ms为分界点,不计算后续的渲染,实际的可执行时间肯定小于16ms)。而React执行是要进行两棵树的diff,虽然React根据html的特性对diff算法做了优化,但是如果两棵树比对的层级较深,依旧会远远超过16ms。

相关参考视频讲解:进入学习

React Fiber

基于此,那如何解决问题呢?在上图中,React作为js,所有的同步操作执行在最开始,在React执行完成后,后续的html解析,布局渲染等操作才会执行。最容易想到的就是,优化JS的执行速度,把React占用线程的时间缩短到16ms以内。在React执行中,最耗时的就是diff算法,React针对html这种场景下做了优化,业界已经没有更好的算法可以缩短diff算法的时间,所以当树的层次很深时,执行时间依旧很长。

那还有什么办法呢,我们依旧可以看上图,在现代浏览器中,浏览器为了让开发者知道浏览器执行完当前帧所有的操作后,还有多长时间可以使用,提供了requestIdleCallback这么一个方法,据此我们可以知道当前还有多长时间可以执行。

requestIdleCallback
requestIdleCallback((deadline) => {while ((deadline.timeRemaining() > 0 || deadline.didTimeout) && nextComponent) {nextComponent = performWork(nextComponent);}
});

题外话:

有兴趣可以在控制台执行输出一下requestIdleCallback回调参数的requestIdleCallback((deadline) ,在不同的网页上得到的时间可能不同。甚至可能会超过16ms(在React官网就显示49.9ms)因为requestIdleCallback的一些限制原因,React源码中未使用requestIdleCallback,而是自己实现了一套类似的机制。

使用此方法我们知道每帧的剩余时间之后,这样就可以在剩余时间内进行工作,如果当前帧时间不够,就把剩余的工作放到下一帧的requestIdleCallback中执行。这就是React所说的时间切片(time slicing)。

所以要使用此方法,需要把基于js内置栈调用的同步递归遍历的diff算法改为异步增量更新。按照React负责人的说法就是

如果你仅仅依赖js内置的调用栈,它会一直执行直到栈为空…,如果我们可以任意的中断并且手动的操作调用栈,不是更完美吗?这就是React Fiber的目的。Fiber是针对React Component的栈的重新实现。你可以认为一个Fiber就是一个虚拟的栈中的一项任务。

说人话,就是原来树的递归是深度递归遍历,现在需要把递归算法重新实现,以便于我不依赖于栈的调用,可以对react组件一个一个节点的遍历,中途任意时间可以中断和从当前开始。

stack Reconciliation vs Fiber Reconciliation

stack Reconciliation

假如我们有如下一个html结构

转化成类React组件的js对象如下

const a1 = {name: 'a1'};
const b1 = {name: 'b1'};
const b2 = {name: 'b2'};
const b3 = {name: 'b3'};
const c1 = {name: 'c1'};
const c2 = {name: 'c2'};
const d1 = {name: 'd1'};
const d2 = {name: 'd2'};a1.render = () => [b1, b2, b3];
b1.render = () => [];
b2.render = () => [c1];
b3.render = () => [c2];
c1.render = () => [d1, d2];
c2.render = () => [];
d1.render = () => [];
d2.render = () => [];

正常情况,我们会使用像下面这种方式递归来遍历这棵"树",在React最早的版本就是基于此来递归遍历dom树

function walk(instance) {console.log(instance.name);let children = instance.render();children.forEach((child) => {walk(child);});
}
walk(a1);

可以看到,这种方式,是可以遍历完整棵树,可是它没办法做到我们之前所说的中断递归,如果你中途中断了递归这棵树,下次你要重新从根节点整个遍历。这显然是不行的,它只能不断递归遍历,直到stack调用栈为空。那React Fiber是如何中断重启任务呢?

答案是单链表树遍历算法。简单来说就是把原来树本身的嵌套结构,改为单链表形式的树。

Fiber Reconciliation

React具体是如何使用链表遍历树呢?为了实现这种算法,首先先看下我们需要的数据结构

  • child,指向该节点第一个子节点
  • sibling,指向该节点的下一个兄弟节点
  • return,指向该节点的父节点

还是之前的dom树结构,现在变成了这样

构建Fiber树的过程就不描述了,我们直接看遍历算法(父节点优先,深度优先)

let root = fiber;
let node = fiber;
while (true) {if (node.child) {node = node.child;continue;}if (node === root) {return;}while (!node.sibling) {if (!node.return || node.return === root) {return;}node = node.return;}node = node.sibling;
}

可以看到,拿到根节点后,不断遍历子节点,直到最深节点,然后从最深的子节点开始遍历兄弟节点,如果没有兄弟节点就返回该节点父节点,如果有兄弟节点,把每个兄弟节点的子节点遍历完,直到最后一个子节点,然后返回父节点。这样不断遍历,直到返回根节点。

下面是在React源码中Fiber的数据对象。其实说到底,Fiber就是一个对象。他相对于之前React createElement生成的element对象,多了一层数据结构来支撑上述的单链表遍历算法。

Fiber数据结构

下面是React源码中的Fiber对象的属性,具体可以直接看注释。

function FiberNode(tag: WorkTag,  pendingProps: mixed,  key: null | string,  mode: TypeOfMode
) {// Instancethis.tag = tag; //Fiber标记是什么类型的Fiber/component,WorkTag 0-24this.key = key; // 唯一标识this.elementType = null;this.type = null;this.stateNode = null; //stateNode:class div// Fiber 数据结构this.return = null; // 父节点this.child = null; // 第一个子节点this.sibling = null; // 兄弟节点this.index = 0; //this.ref = null;this.pendingProps = pendingProps; //newpropsthis.memoizedProps = null; // oldProps 上次的props// updateQueue数据结构://  {//   baseState: fiber.memoizedState,//   firstBaseUpdate: null,//   lastBaseUpdate: null,//   shared: {//     pending: null,//     interleaved: null,//     lanes: NoLanes,//   },//   effects: null,// };this.updateQueue = null; // 批处理队列this.memoizedState = null; //oldStatethis.dependencies = null;this.mode = mode;// Effectsthis.flags = NoFlags; // 标记该fiber变更方式this.subtreeFlags = NoFlags;this.deletions = null;// 优先级调度this.lanes = NoLanes; this.childLanes = NoLanes;this.alternate = null; //work-in-progress current互为alternate
}

fiber带来的效果提升

  1. 可以通过看下重构前后的对比Demo,体会一下带来的体验提升
  2. 为后续React Concurrent模式做了基础

Fiber流转过程

画了一个简单的流程图说明Fiber的流转流程。

图示说明:

react在performUnitOfWork和completeUnitOfWork两个方法中,处理上述Fiber遍历算法的逻辑,在beginwork和completeWork中完成处理组件的逻辑。
在beginwork中会处理state的更新,此阶段相应生命周期的调用,reconcile的过程(给Fiber节点打上新增,删除,移动等标记的过程。在completeWork阶段,会把所有flags的标记,冒泡到父节点。以便于在commit阶段更新。

我记得Dan Abramov对effect list有过一个形象的比喻,可以写一下(大致意思是这样)

你可以把react fiber看做一棵圣诞树,effect list就是这颗圣诞树上悬挂的装饰灯

React源码 —太长不看系列

下面是React中关于Fiber的一些核心源码—已删除了很多跟此次文章无关的代码,大家可以自行选择是否服用。

包含代码注释,及代码在React仓库中的所在位置。大家可以直接看代码注释,不作具体解读了。

// https://github.com/facebook/react/blob/v17.0.2/packages/react-reconciler/src/ReactFiberWorkLoop.old.js#L1635
function workLoopConcurrent() {// Perform work until Scheduler asks us to yieldwhile (workInProgress !== null && !shouldYield()) {performUnitOfWork(workInProgress);}
}

performUnitOfWork

// https://github.com/facebook/react/blob/v17.0.2/packages/react-reconciler/src/ReactFiberWorkLoop.old.js#L1642
function performUnitOfWork(unitOfWork: Fiber): void {const current = unitOfWork.alternate;let next;// 一直返回unitOfWork.child,不会处理siblingnext = beginWork(current, unitOfWork, subtreeRenderLanes);unitOfWork.memoizedProps = unitOfWork.pendingProps;// 该fiber需要做的处理完成,返回下一个待处理的fiberif (next === null) {// 到达该链路的最底层的叶子节点,在该函数中处理sibling节点completeUnitOfWork(unitOfWork);} else {workInProgress = next;}
}

beginWork

// https://github.com/facebook/react/blob/v17.0.2/packages/react-reconciler/src/ReactFiberBeginWork.old.js#L3083
function beginWork(current: Fiber | null,  workInProgress: Fiber,  renderLanes: Lanes
): Fiber | null {let updateLanes = workInProgress.lanes;// tag有很多,这里只保留了常用的FunctionComponent和ClassComponent,后续只看updateClassComponentswitch (workInProgress.tag) {case FunctionComponent: {const Component = workInProgress.type;const unresolvedProps = workInProgress.pendingProps;const resolvedProps =workInProgress.elementType === Component? unresolvedProps: resolveDefaultProps(Component, unresolvedProps);return updateFunctionComponent(current,workInProgress,Component,resolvedProps,renderLanes);}case ClassComponent: {const Component = workInProgress.type;const unresolvedProps = workInProgress.pendingProps;const resolvedProps =workInProgress.elementType === Component? unresolvedProps: resolveDefaultProps(Component, unresolvedProps);// 返回值为workInProgress.child,可以在finishClassComponent中看到return updateClassComponent(current,workInProgress,Component,resolvedProps,renderLanes);}}
}
function updateClassComponent(current: Fiber | null,  workInProgress: Fiber,  Component: any,  nextProps: any,  renderLanes: Lanes
) {const instance = workInProgress.stateNode;let shouldUpdate;// 在此阶段处理更新生命周期和批处理的更新,if (instance === null) {if (current !== null) {// A class component without an instance only mounts if it suspended// inside a non-concurrent tree, in an inconsistent state. We want to// treat 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 effectworkInProgress.flags |= Placement;}// In the initial pass we might need to construct the instance.constructClassInstance(workInProgress, Component, nextProps);mountClassInstance(workInProgress, Component, nextProps, renderLanes);shouldUpdate = true;} else if (current === null) {// In a resume, we'll already have an instance we can reuse.复用之前未完成shouldUpdate = resumeMountClassInstance(workInProgress,Component,nextProps,renderLanes);} else {// 在此阶段处理生命周期和批处理的更新shouldUpdate = updateClassInstance(current,workInProgress,Component,nextProps,renderLanes);}const nextUnitOfWork = finishClassComponent(current,workInProgress,Component,shouldUpdate,hasContext,renderLanes);return nextUnitOfWork;
}function finishClassComponent(current: Fiber | null,  workInProgress: Fiber,  Component: any,  shouldUpdate: boolean,  hasContext: boolean,  renderLanes: Lanes
) {const instance = workInProgress.stateNode;// RerenderReactCurrentOwner.current = workInProgress;let nextChildren;nextChildren = instance.render();//初始化或者执行dom diff   //ReactChildFiber.old.jsreconcileChildren(current, workInProgress, nextChildren, renderLanes);  //childreturn workInProgress.child;
}

completeUnitOfWork

// https://github.com/facebook/react/blob/v17.0.2/packages/react-reconciler/src/ReactFiberWorkLoop.old.js#L1670
function completeUnitOfWork(unitOfWork: Fiber): void {// Attempt to complete the current unit of work, then move to the next// sibling. If there are no more siblings, return to the parent fiber.let completedWork = unitOfWork;do {const current = completedWork.alternate;const returnFiber = completedWork.return;let next;// 返回值一直为nullnext = completeWork(current, completedWork, subtreeRenderLanes);const siblingFiber = completedWork.sibling;if (siblingFiber !== null) {// If there is more work to do in this returnFiber, do that next.workInProgress = siblingFiber;return;}// Otherwise, return to the parentcompletedWork = returnFiber;// Update the next thing we're working on in case something throws.workInProgress = completedWork;} while (completedWork !== null);
}// https://github.com/facebook/react/blob/v17.0.2/packages/react-reconciler/src/ReactFiberCompleteWork.old.js#L645
function completeWork(current: Fiber | null,  workInProgress: Fiber,  renderLanes: Lanes,
): Fiber | null {const newProps = workInProgress.pendingProps;switch (workInProgress.tag) {case FunctionComponent:bubbleProperties(workInProgress);return null;case ClassComponent: {const Component = workInProgress.type;if (isLegacyContextProvider(Component)) {popLegacyContext(workInProgress);}bubbleProperties(workInProgress);return null;}}

React源码解读之React Fiber相关推荐

  1. react源码解读 {createClass}

    对一个框架源码的解读,既有利于更深入地了解框架,使用上更得心应手,又可以学习到其中代码组织的思路,吸收其精华简洁的写法以便于日常工作上使用.下面我就挑选近年大热门react(15.3.1),从中剖析框 ...

  2. python组件的react实现_【React源码解读】- 组件的实现

    前言 react使用也有一段时间了,大家对这个框架褒奖有加,但是它究竟好在哪里呢? 让我们结合它的源码,探究一二!(当前源码为react16,读者要对react有一定的了解) 回到最初 根据react ...

  3. React源码解读之更新的创建

    React 的鲜活生命起源于 ReactDOM.render ,这个过程会为它的一生储备好很多必需品,我们顺着这个线索,一探婴儿般 React 应用诞生之初的悦然. 更新创建的操作我们总结为以下两种场 ...

  4. React源码分析7 — React合成事件系统

    1 React合成事件特点 React自己实现了一套高效的事件注册,存储,分发和重用逻辑,在DOM事件体系基础上做了很大改进,减少了内存消耗,简化了事件逻辑,并最大化的解决了IE等浏览器的不兼容问题. ...

  5. webuploader 怎么在react中_另辟蹊径搭建阅读React源码调试环境支持所有React版本细分文件断点调试...

    引言(为什么写这篇文章) 若要高效阅读和理解React源码,搭建调试环境是必不可少的一步.而常规方法:使用react.development.js和react-dom.development.js调试 ...

  6. react源码中的fiber架构

    先看一下FiberNode在源码中的样子 FiberNode // packages/react-reconciler/src/ReactFiber.old.js function FiberNode ...

  7. # React源码解析之fiber的初次渲染与更新(下)

    React源码解析之fiber的初次渲染与更新(下) 经历一个月的学习整理,站在前人的肩膀上,对React有了一些浅薄的理解,希望记录自己的学习过程的同时也可以给大家带来一点小帮助.如果此系列文章对您 ...

  8. React 之 Refs 的使用和 forwardRef 的源码解读

    三种使用方式 React 提供了 Refs,帮助我们访问 DOM 节点或在 render 方法中创建的 React 元素. React 提供了三种使用 Ref 的方式: 1. String Refs ...

  9. react源码解析之stack reconciler

    关于源码解读的系列文章,可以关注我的github的这个仓库, 现在才刚刚写,后续有空就写点.争取把react源码剖析透学习透.有不正确的地方希望大家帮忙指正.大家互相学习,共同进步. 本篇文章是官方文 ...

最新文章

  1. 信息检索的评价指标(Precision、Recall、F-score、MAP、ROC、AUC)
  2. “速卖通”店铺3天销量破零运营技巧
  3. firefox 39 linux,Mozilla Firefox 39.0 Beta 4 发布下载
  4. 基于GRU和am-softmax的句子相似度模型 | 附代码实现
  5. 联想电脑如何下载matlab,lenovo utility是什么软件?
  6. Java 8 –按值对HashMap进行升序和降序排序
  7. Hihocoder #1631 : Cats and Fish 模拟
  8. python3中实现tar_Python3实现将文件树中所有文件和子目录归档到tar压缩文件的方法...
  9. .idea文件夹是做什么的_33 个 IDEA 最牛配置,写代码太爽了!
  10. linux 抓包工具_03-Python爬虫工程师-抓包工具
  11. java 解析数据包_java - 如何在Java中正确解析TCP数据包? - 堆栈内存溢出
  12. Ps 初学者教程,如何在图片中创造双色效果?
  13. 存储过程别忘了写最后一句话,别忘了类型转换
  14. 企业微信邮箱登录入口在哪里?
  15. 制作自己的 Cydia 源
  16. Matlab实现求解乘法逆元实验
  17. 字节校园精选 66 道高频经典笔面试题(含多种思路)(上)
  18. 量化资源--awesome quant中文版发布
  19. 地图叠加图切片:使用配准点使叠加图片精确匹配到地图
  20. 《CISP》(十)软件安全开发

热门文章

  1. 共享存储集群规范化部署
  2. 爱奇艺世界大会|中国影视行业该走出“作坊”了
  3. HTML如何添加重置标签,HTML CSS标签类型转换、样式重置 、前段规范的详细介绍...
  4. 分享一款超高颜值的Idea插件——Material Theme UI !!!快来看看
  5. android 点击爱心变色,小程序学习(一):点击爱心变色最简单的事件实现
  6. linux中根目录包含哪些目录,Linux根目录的主要目录功能介绍
  7. 几行Python代码实现人像动漫化, 让头像独一无二
  8. 计算机主板用户可以自己安装的是,如何选择计算机主板? DIY主板安装指南
  9. C语言软件版本号组成和定义
  10. 设置 Sidecar