React16源码: React中Fiber对象的源码实现

2024-01-09 01:28

本文主要是介绍React16源码: React中Fiber对象的源码实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

关于 Fiber 对象

  • FiberRoot里面,它也会为我们去创建的一个对象叫做 Fiber
  • 在 React16 之后, 非常核心,非常重要的一个东西
  • A. 每个 ReactElement 都会对应一个 Fiber 对象
  • B. 它会记录节点的各种状态
    • 比如,class component,它的state和props
    • 这些东西是记录在 Fiber 对象上面的
    • 在 Fiber 更新之后才会更新到 class component 上面的 this.state, this.props 里面
    • 并不是通过 class component 自己去调理的这个过程
    • 这些过程都是在 Fiber 上面进行操作的,只是在更新了这个节点之后
    • 它才会把这个属性放到this上面,因此也给了react实现 hooks 一个方便
    • 因为hooks是用在function component里面的,里面没有this
    • 我们本身记录state还有props都不是在class component的对象上面,而是在 Fiber 上面
    • 我们有能力记录这个状态之后,也可以让function component更新的过程当中去拿到这个更新之后的state
    • 所以Fiber有一个非常重要的作用,就是记录节点的各种状态
  • C. 串联整个应用
    • 在 ReactElement 中,通过 props.children 属性把整个应用串联起来
    • 每一个节点都可以通过一个属性去拿到它的子节点
    • 在 Fiber 里面,它也是有这么一个能力来帮助我们记录整个应用的一个树形结构的一个状态,把每一个节点给它串联起来
    • 在 Fiber 里面它是如何进行一个串联的呢?需要先回顾一下 ReactElement 的串联方式, 结合前文中的举例,如下
                  App|render() return|div/ \/   \children[0]  children[1]/       \/         \/           \Input          List|               \
      render() return     render() return |                 \input        (span  span  span  button)
      
    • 上面就是 ReactElement 基于 render() 函数 return 了子节点,由此构建的树形结构
    • Fiber 里面也有这个能力记录整个应用的树形结构和状态,把每个节点串联起来
                -----current-----> FiberRoot                     RootFiber<---stateNode-----    |   |child  |↓App|child|↓|div/ \/   \child   child/       \/         \/           \Input-sibling-> List|               \child            child|                \↓                 \input               span --sibling--> span --sibling--> span --sibling--> button
      
    • 被首先创建的 FiberRoot 有一个 current 属性,指向一个 Fiber对象,我们叫做 RootFiber
    • 因为 FiberRoot 在 ReactDOM.render 时,接受了 一个参数是 App
    • App 这个 ReactElement 它对应的Fiber对象就是 RootFiber 的 child
    • App 的 child 就是 div 节点,因为它的 render 函数渲染出来之后就是一个 div 的唯一子节点
    • 这个 div 接收到的 props.children 是一个数组,这个数组的第一个节点会作为它的 child, 其指向 Input 组件对应的 Fiber对象
    • List 也是作为 div的 props.children 的一项,它是 Input 对应Fiber对象的 sibling 兄弟节点
    • 也就是说,每个节点只会存储它的第一个子节点,不会把它的子节点都存放在一起,通过上述单项的指向来进行存储
    • 对于 Input 组件来说,它的 child 就是其 render 函数 return 的一个 input 原生标签
    • 对于 List 组件来说,它返回的是一个数组,它的第一个子节点就是 span, 剩下的兄弟节点通过 sibling 来连接
    • 而每个子节点都会有一个 return 属性指向自己的父节点,上述图示中并没有标示出来
    • 我们进行节点的查找就会非常方便,只需要通过 child 一路向下查找,直接到叶子节点
    • 基于这种方式来遍历整个应用,即通过react的这种数据结构,把整个 Fiber树给它串联起来,提供给我们一个方便高效的遍历方式
    • 现在看下 Fiber 的属性,源码在: https://github.com/facebook/react/blob/v16.6.0/packages/react-reconciler/src/ReactFiber.js
      // A Fiber is work on a Component that needs to be done or was done. There can
      // be more than one per component.
      export type Fiber = {|// These first fields are conceptually members of an Instance. This used to// be split into a separate type and intersected with the other Fiber fields,// but until Flow fixes its intersection bugs, we've merged them into a// single type.// An Instance is shared between all versions of a component. We can easily// break this out into a separate object to avoid copying so much to the// alternate versions of the tree. We put this on a single object for now to// minimize the number of objects created during the initial render.// Tag identifying the type of fiber.tag: WorkTag, // 标记不同的组件类型,有原生dom节点的组件, 有 class component, function component, 各种各样的组件都有对应的类型// Unique identifier of this child.key: null | string, // ReactElement 里面的key// The value of element.type which is used to preserve the identity during// reconciliation of this child.elementType: any, // ReactElement.type, 就是调用 createElement 的第一个参数// The resolved function/class/ associated with this fiber.type: any, // 异步组件 resolved 之后返回的内容, 一般是 function 或 class// The local state associated with this fiber.stateNode: any, // 跟当前Fiber相关的本地状态,在浏览器环境中就是DOM节点,对应节点实际的实例,比如 class component 对应 class 的实例, dom组件对应dom节点的实例,function component 没有实例,就没有 stateNode, 有了它在应用更新完成后,就可以把 最新的 state,props 等放到节点上// Conceptual aliases// parent : Instance -> return The parent happens to be the same as the// return fiber since we've merged the fiber and instance.// Remaining fields belong to Fiber// The Fiber to return to after finishing processing this one.// This is effectively the parent, but there can be multiple parents (two)// so this is only the parent of the thing we're currently processing.// It is conceptually the same as the return address of a stack frame.return: Fiber | null,// Singly Linked List Tree Structure.child: Fiber | null,sibling: Fiber | null,index: number,// The ref last used to attach this node.// I'll avoid adding an owner field for prod and model that as functions.ref: null | (((handle: mixed) => void) & {_stringRef: ?string}) | RefObject, // ref 属性// Input is the data coming into process this fiber. Arguments. Props.pendingProps: any, // This type will be more specific once we overload the tag. // 新的变动,带来的新的 props,就存在 pendingProps 属性上memoizedProps: any, // The props used to create the output. // 上一次更新结束之后的 props// A queue of state updates and callbacks.updateQueue: UpdateQueue<any> | null, // 该 Fiber 对应的组件产生的 update 会存放在这个队列里面// The state used to create the outputmemoizedState: any, // 上次渲染完成之后的 state,比如我们执行setState,会算出一个新的state,算出是要经过 updateQueue计算的, 计算完之后覆盖 memoizedState 值// A linked-list of contexts that this fiber depends onfirstContextDependency: ContextDependency<mixed> | null, // 一个列表,存放这个Fiber依赖的context// Bitfield that describes properties about the fiber and its subtree. E.g.// the ConcurrentMode flag indicates whether the subtree should be async-by-// default. When a fiber is created, it inherits the mode of its// parent. Additional flags can be set at creation time, but after that the// value should remain unchanged throughout the fiber's lifetime, particularly// before its child fibers are created.mode: TypeOfMode, // 用于描述当前Fiber和它子树的Bitfield, 共存的模式表示这个子树是否默认是异步渲染的, Fiber 被创建的时候会继承父Fiber, 其他的标识也可以在创建的时候被设置,但在创建之后不应该再被修改,特别是他的子Fiber创建之前, 对应比如: ConcurrentMode, StrictMode,// EffecteffectTag: SideEffectTag, // 副作用,用来记录 Side Effect// Singly linked list fast path to the next fiber with side-effects.nextEffect: Fiber | null, // 副作用, 单链表用来快速查找下一个 side effect// The first and last fiber with side-effect within this subtree. This allows// us to reuse a slice of the linked list when we reuse the work done within// this fiber.firstEffect: Fiber | null, // 副作用,子树中第一个 side effectlastEffect: Fiber | null, // 副作用,子树中最后一个 side effect// Represents a time in the future by which this work should be completed.// Does not include work found in its subtree.expirationTime: ExpirationTime, // 当前节点产生的更新任务的过期时间, 代表任务在未来的哪个时间点应该被完成,不包括他的子树产生的任务// This is used to quickly determine if a subtree has no pending changes.childExpirationTime: ExpirationTime, // 它的子节点如果产生更新,记录子节点的过期时间, 快速确定子树中是否有不在等待的变化// This is a pooled version of a Fiber. Every fiber that gets updated will// eventually have a pair. There are cases when we can clean up pairs to save// memory if we need to.alternate: Fiber | null, // 在Fiber树更新的过程中,每个Fiber都会有一个跟其对应的Fiber,我们称它为 current <==> workInProgress的对应关系, 在渲染完成之后他们会交换位置,在更新过程中,会根据整个Fiber对象,创建一个 workInProgress, current是当前的, workInProgress 是要进行一个更新的, 在更新完成后,workInProgress 状态会更新成一个新的,current就变成老的,最终节点渲染到 dom 上面,workInProgress 会变成current,再下一次更新到来的时候,会保持着两个对象都存在,这个在 react 中叫做 double buffer, 是用来提高性能的// Time spent rendering this Fiber and its descendants for the current update.// This tells us how well the tree makes use of sCU for memoization.// It is reset to 0 each time we render and only updated when we don't bailout.// This field is only set when the enableProfilerTimer flag is enabled.actualDuration?: number,// If the Fiber is currently active in the "render" phase,// This marks the time at which the work began.// This field is only set when the enableProfilerTimer flag is enabled.actualStartTime?: number,// Duration of the most recent render time for this Fiber.// This value is not updated when we bailout for memoization purposes.// This field is only set when the enableProfilerTimer flag is enabled.selfBaseDuration?: number,// Sum of base times for all descedents of this Fiber.// This value bubbles up during the "complete" phase.// This field is only set when the enableProfilerTimer flag is enabled.treeBaseDuration?: number,// Conceptual aliases// workInProgress : Fiber ->  alternate The alternate used for reuse happens// to be the same as work in progress.// __DEV__ only_debugID?: number,_debugSource?: Source | null,_debugOwner?: Fiber | null,_debugIsCurrentlyTiming?: boolean,
      |};
      
    • Fiber里面三个重要的属性: return, child, sibling 可以帮助把应用中的所有节点一一串联
      • return: 指向它在Fiber节点树中的 parent, 用来处理完这个节点之后,向上返回
      • child: 单链表树结构, 指向自己的第一个子节点
      • sibling: 指向自己的兄弟结构,兄弟节点的return指向同一个父节点
    • 上述 Effect 相关的属性,都是副作用,是用来标记 Dom 节点要进行那些更新的工具,以及标记是否要执行组件生命周期的内容
    • 上述 Duration 相关属性,是渲染时间相关的,和 React DEV TOOL 有相关关系
    • 其他属性,都在代码中备注

这篇关于React16源码: React中Fiber对象的源码实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/585494

相关文章

Oracle查询优化之高效实现仅查询前10条记录的方法与实践

《Oracle查询优化之高效实现仅查询前10条记录的方法与实践》:本文主要介绍Oracle查询优化之高效实现仅查询前10条记录的相关资料,包括使用ROWNUM、ROW_NUMBER()函数、FET... 目录1. 使用 ROWNUM 查询2. 使用 ROW_NUMBER() 函数3. 使用 FETCH FI

Python脚本实现自动删除C盘临时文件夹

《Python脚本实现自动删除C盘临时文件夹》在日常使用电脑的过程中,临时文件夹往往会积累大量的无用数据,占用宝贵的磁盘空间,下面我们就来看看Python如何通过脚本实现自动删除C盘临时文件夹吧... 目录一、准备工作二、python脚本编写三、脚本解析四、运行脚本五、案例演示六、注意事项七、总结在日常使用

Java实现Excel与HTML互转

《Java实现Excel与HTML互转》Excel是一种电子表格格式,而HTM则是一种用于创建网页的标记语言,虽然两者在用途上存在差异,但有时我们需要将数据从一种格式转换为另一种格式,下面我们就来看看... Excel是一种电子表格格式,广泛用于数据处理和分析,而HTM则是一种用于创建网页的标记语言。虽然两

Java中Springboot集成Kafka实现消息发送和接收功能

《Java中Springboot集成Kafka实现消息发送和接收功能》Kafka是一个高吞吐量的分布式发布-订阅消息系统,主要用于处理大规模数据流,它由生产者、消费者、主题、分区和代理等组件构成,Ka... 目录一、Kafka 简介二、Kafka 功能三、POM依赖四、配置文件五、生产者六、消费者一、Kaf

使用Python实现在Word中添加或删除超链接

《使用Python实现在Word中添加或删除超链接》在Word文档中,超链接是一种将文本或图像连接到其他文档、网页或同一文档中不同部分的功能,本文将为大家介绍一下Python如何实现在Word中添加或... 在Word文档中,超链接是一种将文本或图像连接到其他文档、网页或同一文档中不同部分的功能。通过添加超

windos server2022里的DFS配置的实现

《windosserver2022里的DFS配置的实现》DFS是WindowsServer操作系统提供的一种功能,用于在多台服务器上集中管理共享文件夹和文件的分布式存储解决方案,本文就来介绍一下wi... 目录什么是DFS?优势:应用场景:DFS配置步骤什么是DFS?DFS指的是分布式文件系统(Distr

NFS实现多服务器文件的共享的方法步骤

《NFS实现多服务器文件的共享的方法步骤》NFS允许网络中的计算机之间共享资源,客户端可以透明地读写远端NFS服务器上的文件,本文就来介绍一下NFS实现多服务器文件的共享的方法步骤,感兴趣的可以了解一... 目录一、简介二、部署1、准备1、服务端和客户端:安装nfs-utils2、服务端:创建共享目录3、服

C#使用yield关键字实现提升迭代性能与效率

《C#使用yield关键字实现提升迭代性能与效率》yield关键字在C#中简化了数据迭代的方式,实现了按需生成数据,自动维护迭代状态,本文主要来聊聊如何使用yield关键字实现提升迭代性能与效率,感兴... 目录前言传统迭代和yield迭代方式对比yield延迟加载按需获取数据yield break显式示迭

Python实现高效地读写大型文件

《Python实现高效地读写大型文件》Python如何读写的是大型文件,有没有什么方法来提高效率呢,这篇文章就来和大家聊聊如何在Python中高效地读写大型文件,需要的可以了解下... 目录一、逐行读取大型文件二、分块读取大型文件三、使用 mmap 模块进行内存映射文件操作(适用于大文件)四、使用 pand

python实现pdf转word和excel的示例代码

《python实现pdf转word和excel的示例代码》本文主要介绍了python实现pdf转word和excel的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价... 目录一、引言二、python编程1,PDF转Word2,PDF转Excel三、前端页面效果展示总结一