react源码阅读- fiber架构探索(一)

react源码阅读- fiber架构探索(一)

React 团队在 React 的v16版本中重写了 React 的核心算法 - reconciliation,称为fiber reconciler,简称为Fiber。

优势

  • 可中断拆分任务

  • render 函数可以返回多个元素(即可以返回数组)

  • 对执行的任务划分优先级,可复用上一次的工作成果

  • 更加友好的支持 error boundary,异常边界处理

  • 可在父子组件任务之间切换,支持执行过程中的布局刷新

Fiber 给 React 带来了更加先进的调和器,它允许渲进程分段进行,也可以再执行的过程中返回主进程执行其他任务,非常的灵活,而这是通过diff算法,计算部分组件树的变更,并暂停渲染更新,询问主进程是否有更高需求的绘制或者更新任务需要执行,也就是任务之间是有优先级的;这一切的实现是通过引入新的数据结构-fiber对象来实现的,一个组件实例对应一个fiber实例,fiber实例负责管理组件实例的更新,渲染任务及与其他fiber实例的联系。

关键点

Fiber 的主要特点是:

  • 给不同类型的更新赋予优先级

  • 并发方面新的基础能力

  • 把渲染任务拆分成块,匀到多帧(增量渲染)

  • 更新时能够暂停,终止,复用渲染任务

增量渲染用来解决掉帧的问题,渲染任务拆分之后,每次只做一小段,做完一段就把时间控制权交还给主线程,而不像之前长时间占用。这种策略叫做cooperative scheduling(合作式调度),操作系统的3种任务调度策略之一。

Fiber 与组件

Fiber 是如何和组件联系的呢?并且如何实现效果的呢?

  1. React App 中的基础单元是组件,应用以组件树形式组织,渲染组件;

  2. Fiber reconciliation 基础单元则是fiber(调和单元),应用以fiber树形式组织,应用Fiber算法;

  3. 组件树和fiber树结构对应,一个组件实例有一个对应的fiber实例;

  4. Fiber负责整个应用层面的调和,fiber实例负责对应组件的调和;

注意Fiber与fiber的区别,Fiber是指reconciliation算法,fiber则是reconciliation算法组成单元,和组件与应用关系类似,每一个组件实例会有对应的fiber实例负责该组件的调和。

Fiber 数据结构

接下来让看看Fiber的数据结构,数据结构能在一定程度反映其整体工作架构。

一个fiber就是一个javascript对象,已键值对存储一个关联组件的对象,包括组件的props属性,维护的state,最后需要渲染出的内容等。

Fiber 对象


// 一个Fiber对象作用于一个组件

export type Fiber = {|

  // 标记fiber类型tag.

  tag: TypeOfWork,

  // fiber对应的function/class/module类型组件名.

  type: any,

  // fiber所在组件树的根组件FiberRoot对象

  stateNode: any,

  // 处理完当前fiber后返回的fiber,

  // 返回当前fiber所在fiber树的父级fiber实例

  return: Fiber | null,

  // fiber树结构相关链接

  child: Fiber | null,

  sibling: Fiber | null,

  index: number,

  // 当前处理过程中的组件props对象

  pendingProps: any,

  // 缓存的之前组件props对象

  memoizedProps: any, // The props used to create the output.

  // The state used to create the output

  memoizedState: any,

  // 组件状态更新及对应回调函数的存储队列

  updateQueue: UpdateQueue<any> | null,

  // 描述当前fiber实例及其子fiber树的数位,

  // 如,AsyncUpdates特殊字表示默认以异步形式处理子树,

  // 一个fiber实例创建时,此属性继承自父级fiber,在创建时也可以修改值,

  // 但随后将不可修改。

  internalContextTag: TypeOfInternalContext,

  // 更新任务的最晚执行时间

  expirationTime: ExpirationTime,

  // fiber的版本池,即记录fiber更新过程,便于恢复

  alternate: Fiber | null,

  // Conceptual aliases

  // workInProgress : Fiber ->  alternate The alternate used for reuse happens

  // to be the same as work in progress.

|};

  1. type & key:同React元素的值;

  2. type:描述fiber对应的React组件;

    2.1 对于组合组件:值为function或class组件本身;

    2.2 对于原生组件(div等):值为该元素类型字符串;

  3. key:调和阶段,标识fiber,以检测是否可重用该fiber实例;

  4. child & sibling:组件树,对应生成fiber树,类比的关系;

  5. pendingProps & memoizedProps:分别表示组件当前传入的及之前的props;

  6. return:返回当前fiber所在fiber树的父级fiber实例,即当前组件的父组件对应的fiber;

  7. alternate:fiber的版本池,即记录fiber更新过程,便于恢复重用;

  8. workInProgress:正在处理的fiber,概念上叫法,实际上没有此属性;

Fiber类型

Fiber对象中有个tag属性,标记fiber类型,而fiber实例是和组件对应的,所以其类型基本上对应于组件类型,源码见ReactTypeOfWork模块:


export type TypeOfWork = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10;

export const IndeterminateComponent = 0; // 尚不知是类组件还是函数式组件

export const FunctionalComponent = 1; // 函数式组件

export const ClassComponent = 2; // Class类组件

export const HostRoot = 3; // 组件树根组件,可以嵌套

export const HostPortal = 4; // 子树. Could be an entry point to a different renderer.

export const HostComponent = 5; // 标准组件,如地div, span等

export const HostText = 6; // 文本

export const CallComponent = 7; // 组件调用

export const CallHandlerPhase = 8; // 调用组件方法

export const ReturnComponent = 9; // placeholder(占位符)

export const Fragment = 10; // 片段

在调度执行任务的时候会根据不同类型fiber,即fiber.tag值进行不同处理。

FiberRoot对象

FiberRoot对象,主要用来管理组件树组件的更新进程,同时记录组件树挂载的DOM容器相关信息,具体定义见ReactFiberRoot模块:


export type FiberRoot = {

  // fiber节点的容器元素相关信息,通常会直接传入容器元素

  containerInfo: any,

  // 当前fiber树中激活状态(正在处理)的fiber节点,

  current: Fiber,

  // 此节点剩余的任务到期时间

  remainingExpirationTime: ExpirationTime,

  // 更新是否可以提交

  isReadyForCommit: boolean,

  // 准备好提交的已处理完成的work-in-progress

  finishedWork: Fiber | null,

  // 多组件树FirberRoot对象以单链表存储链接,指向下一个需要调度的FiberRoot

  nextScheduledRoot: FiberRoot | null,

};

创建FIBERROOT实例


import {

  ClassComponent,

  HostRoot

} from 'shared/ReactTypeOfWork';

// 创建返回一个初始根组件对应的fiber实例

function createHostRootFiber(): Fiber {

  // 创建fiber

  const fiber = createFiber(HostRoot, null, NoContext);

  return fiber;

}

export function createFiberRoot(

  containerInfo: any,

  hydrate: boolean,

) {

  // 创建初始根组件对应的fiber实例

  const uninitializedFiber = createHostRootFiber();

  // 组件树根组件的FiberRoot对象

  const root = {

    // 根组件对应的fiber实例

    current: uninitializedFiber,

    containerInfo: containerInfo,

    pendingChildren: null,

    remainingExpirationTime: NoWork,

    isReadyForCommit: false,

    finishedWork: null,

    context: null,

    pendingContext: null,

    hydrate,

    nextScheduledRoot: null,

  };

  // 组件树根组件fiber实例的stateNode指向FiberRoot对象

  uninitializedFiber.stateNode = root;

  return root;

}

参考文章:

  1. react

  2. reconciliation

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容