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

相关文章

Nginx实现高并发的项目实践

《Nginx实现高并发的项目实践》本文主要介绍了Nginx实现高并发的项目实践,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录使用最新稳定版本的Nginx合理配置工作进程(workers)配置工作进程连接数(worker_co

python中列表list切分的实现

《python中列表list切分的实现》列表是Python中最常用的数据结构之一,经常需要对列表进行切分操作,本文主要介绍了python中列表list切分的实现,文中通过示例代码介绍的非常详细,对大家... 目录一、列表切片的基本用法1.1 基本切片操作1.2 切片的负索引1.3 切片的省略二、列表切分的高

基于Python实现一个PDF特殊字体提取工具

《基于Python实现一个PDF特殊字体提取工具》在PDF文档处理场景中,我们常常需要针对特定格式的文本内容进行提取分析,本文介绍的PDF特殊字体提取器是一款基于Python开发的桌面应用程序感兴趣的... 目录一、应用背景与功能概述二、技术架构与核心组件2.1 技术选型2.2 系统架构三、核心功能实现解析

使用Python实现表格字段智能去重

《使用Python实现表格字段智能去重》在数据分析和处理过程中,数据清洗是一个至关重要的步骤,其中字段去重是一个常见且关键的任务,下面我们看看如何使用Python进行表格字段智能去重吧... 目录一、引言二、数据重复问题的常见场景与影响三、python在数据清洗中的优势四、基于Python的表格字段智能去重

Spring AI集成DeepSeek实现流式输出的操作方法

《SpringAI集成DeepSeek实现流式输出的操作方法》本文介绍了如何在SpringBoot中使用Sse(Server-SentEvents)技术实现流式输出,后端使用SpringMVC中的S... 目录一、后端代码二、前端代码三、运行项目小天有话说题外话参考资料前面一篇文章我们实现了《Spring

Nginx中location实现多条件匹配的方法详解

《Nginx中location实现多条件匹配的方法详解》在Nginx中,location指令用于匹配请求的URI,虽然location本身是基于单一匹配规则的,但可以通过多种方式实现多个条件的匹配逻辑... 目录1. 概述2. 实现多条件匹配的方式2.1 使用多个 location 块2.2 使用正则表达式

使用Apache POI在Java中实现Excel单元格的合并

《使用ApachePOI在Java中实现Excel单元格的合并》在日常工作中,Excel是一个不可或缺的工具,尤其是在处理大量数据时,本文将介绍如何使用ApachePOI库在Java中实现Excel... 目录工具类介绍工具类代码调用示例依赖配置总结在日常工作中,Excel 是一个不可或缺的工http://

SpringBoot实现导出复杂对象到Excel文件

《SpringBoot实现导出复杂对象到Excel文件》这篇文章主要为大家详细介绍了如何使用Hutool和EasyExcel两种方式来实现在SpringBoot项目中导出复杂对象到Excel文件,需要... 在Spring Boot项目中导出复杂对象到Excel文件,可以利用Hutool或EasyExcel

前端bug调试的方法技巧及常见错误

《前端bug调试的方法技巧及常见错误》:本文主要介绍编程中常见的报错和Bug,以及调试的重要性,调试的基本流程是通过缩小范围来定位问题,并给出了推测法、删除代码法、console调试和debugg... 目录调试基本流程调试方法排查bug的两大技巧如何看控制台报错前端常见错误取值调用报错资源引入错误解析错误

Python如何实现读取csv文件时忽略文件的编码格式

《Python如何实现读取csv文件时忽略文件的编码格式》我们再日常读取csv文件的时候经常会发现csv文件的格式有多种,所以这篇文章为大家介绍了Python如何实现读取csv文件时忽略文件的编码格式... 目录1、背景介绍2、库的安装3、核心代码4、完整代码1、背景介绍我们再日常读取csv文件的时候经常