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

相关文章

使用Java将DOCX文档解析为Markdown文档的代码实现

《使用Java将DOCX文档解析为Markdown文档的代码实现》在现代文档处理中,Markdown(MD)因其简洁的语法和良好的可读性,逐渐成为开发者、技术写作者和内容创作者的首选格式,然而,许多文... 目录引言1. 工具和库介绍2. 安装依赖库3. 使用Apache POI解析DOCX文档4. 将解析

Qt中QGroupBox控件的实现

《Qt中QGroupBox控件的实现》QGroupBox是Qt框架中一个非常有用的控件,它主要用于组织和管理一组相关的控件,本文主要介绍了Qt中QGroupBox控件的实现,具有一定的参考价值,感兴趣... 目录引言一、基本属性二、常用方法2.1 构造函数 2.2 设置标题2.3 设置复选框模式2.4 是否

C++使用printf语句实现进制转换的示例代码

《C++使用printf语句实现进制转换的示例代码》在C语言中,printf函数可以直接实现部分进制转换功能,通过格式说明符(formatspecifier)快速输出不同进制的数值,下面给大家分享C+... 目录一、printf 原生支持的进制转换1. 十进制、八进制、十六进制转换2. 显示进制前缀3. 指

springboot整合阿里云百炼DeepSeek实现sse流式打印的操作方法

《springboot整合阿里云百炼DeepSeek实现sse流式打印的操作方法》:本文主要介绍springboot整合阿里云百炼DeepSeek实现sse流式打印,本文给大家介绍的非常详细,对大... 目录1.开通阿里云百炼,获取到key2.新建SpringBoot项目3.工具类4.启动类5.测试类6.测

pytorch自动求梯度autograd的实现

《pytorch自动求梯度autograd的实现》autograd是一个自动微分引擎,它可以自动计算张量的梯度,本文主要介绍了pytorch自动求梯度autograd的实现,具有一定的参考价值,感兴趣... autograd是pytorch构建神经网络的核心。在 PyTorch 中,结合以下代码例子,当你

Python中判断对象是否为空的方法

《Python中判断对象是否为空的方法》在Python开发中,判断对象是否为“空”是高频操作,但看似简单的需求却暗藏玄机,从None到空容器,从零值到自定义对象的“假值”状态,不同场景下的“空”需要精... 目录一、python中的“空”值体系二、精准判定方法对比三、常见误区解析四、进阶处理技巧五、性能优化

SpringBoot集成Milvus实现数据增删改查功能

《SpringBoot集成Milvus实现数据增删改查功能》milvus支持的语言比较多,支持python,Java,Go,node等开发语言,本文主要介绍如何使用Java语言,采用springboo... 目录1、Milvus基本概念2、添加maven依赖3、配置yml文件4、创建MilvusClient

JS+HTML实现在线图片水印添加工具

《JS+HTML实现在线图片水印添加工具》在社交媒体和内容创作日益频繁的今天,如何保护原创内容、展示品牌身份成了一个不得不面对的问题,本文将实现一个完全基于HTML+CSS构建的现代化图片水印在线工具... 目录概述功能亮点使用方法技术解析延伸思考运行效果项目源码下载总结概述在社交媒体和内容创作日益频繁的

前端CSS Grid 布局示例详解

《前端CSSGrid布局示例详解》CSSGrid是一种二维布局系统,可以同时控制行和列,相比Flex(一维布局),更适合用在整体页面布局或复杂模块结构中,:本文主要介绍前端CSSGri... 目录css Grid 布局详解(通俗易懂版)一、概述二、基础概念三、创建 Grid 容器四、定义网格行和列五、设置行

前端下载文件时如何后端返回的文件流一些常见方法

《前端下载文件时如何后端返回的文件流一些常见方法》:本文主要介绍前端下载文件时如何后端返回的文件流一些常见方法,包括使用Blob和URL.createObjectURL创建下载链接,以及处理带有C... 目录1. 使用 Blob 和 URL.createObjectURL 创建下载链接例子:使用 Blob