前言

1. 不同的元素类型决定不同的任务类型
2. react 的元素类型有 class component , function component ,Dom nodes, portal等等

要理解 fiber 调度算法,首先要了解实现该算法的数据结构:

 jsx -> react element :

 {
$$typeof:Symbol(react.element),
key:'',
props:{},
ref:null,
type:''
}

  react element -> fiber node:

    react element 转换成对应类型的 fiber node ,fiber node 的类型描述了
    要执行了的任务的类型,可以把fiber node看成是一个任务执行单元。 
    在后面的更新中,react 会复用fiber node 仅仅通过对应的react element 上的数据
    更新必要的属性。如果对应的react element在render函数中被删除,与其对应的fiber node
    也会通过key属性来删除。
    react 为每一个element创建了一个fiber node,所以也会得到一个对应的fiber tree。
    以链表的方式通过child(子) sibling(兄弟) return(子节点指向父节点) 等指针连接。

current and work in progress trees (当前和正在进行中的tree)

    fiber tree 反映的是当前的页面的状态。当react 开始更新时,会创建一个 workInProgress 
    tree 反映的是将要被渲染到页面上的状态。每一个 fiber node 会创建一个替换的节点,可以看做是
    workInProgress tree,这个节点用来自对应的react element 上的数据创建。一旦更新过程进行完并且
    所有相关的工作完成,react将会有一颗替换的树准备推向页面。一旦 workInProgress tree被渲染到了页
    面上,它就会变成current tree。

react 的核心原则之一是保持一致性。react 总是一次性更新 workInProgress , 不会出现只更新部分dom的情况。

    workInProgress 可以看成是页面更新的一个原型,对用户并不可见,所以 react 能够首先处理所有的组件,并一次性的把
    这些变更提现在组件上。
    fiber tree 上的节点的 alternate 指向 workInprogress 中对应的节点,反过来也是一样。

副作用

    可以认为 react 组件通过state 和props 来计算UI 表示。除此之外的dom 操作或者生命周期函数可以被认为是副作用。
    因为这些操作会影响其他的组件并且不能在render的时候执行。执行副作用也是一种任务,每个fiber节点 effectTag 来
    关联副作用。因此副作用在fiber中定义了在实例更新之后要处理的任务。对应原生的dom节点来说这个任务由新增,删除和
    更新节点组成。对于类组件,对应的是更新ref 和调用 componentDidMount或者componentDidUpdate生命周期函数。此外
    还有其他的复作用对应了其他的fibre类型。
    副作用任务执行
    为了快速迭代计算,react 构造了一个线性的有副作用的fiber node链表。相比树形结构,同时不需要花时间在没有副作用的节点上。
    链接通过 nextEffect 字段链接下一个节点。
 {
stateNode: new HTMLSpanElement,
type: "span",
alternate: null,
key: "2",
updateQueue: null,
memoizedState: null,
pendingProps: {children: 0},
memoizedProps: {children: 0},
tag: 5,
effectTag: 0,
nextEffect: null
}
   stateNode字段指向组件实例。
    type:react element (组件)或者dom元素。
    tag:定义了fiber 节点的类型 
    updateQueue:状态更新队列
    memoizedState:之前用来创建输出的状态。当正在更新时,它反映了当前在页面上的状态
    memoizedProps:上一次渲染用来创建输出的属性
    pendingProps:从新数据中更新的属性,将要应用到dom元素或者子元素中
    key:在数组中唯一标识该元素,用来帮助react找到变更的元素。

算法:

    更新过程分为两个阶段:render 和 commit
    在render阶段,通过setState或者react.render(初始化),找到要更新的UI上的内容。
    render的阶段的output 是标记了副作用的fiber tree。副作用描述了要在commit阶段
    完成的任务。在commit 阶段,react 会把在render阶段的输出应用到实例,同时会遍历
    副作用列表把dom 更新或者其他的改变反映到页面上。
    在render阶段的任务是可以异步执行的,react 能够根据可用的时间处理一个或者多个
    fiber节点,然后暂停并保存结果来执行一些高优先级的事件,执行完后从暂停的位置继续
    执行。在某些情况下,可能需要丢掉已经做过的工作重新从顶部开始。能够暂停的是因为在
    render 阶段做的工作对用户是不可见的。
    相反,接下来的commit阶段总是同步的。这是因为在这个阶段的任务会导致用户界面的改变,例如dom更新,这是为什么react需要在一次更新中完成所用的任务。
    调用生命周期函数是react 任务中的一种。一些生命周期函数在 render阶段调用,其他的一些方法在commit 阶段调用。
    render 阶段:
        componentWillMount (遗弃)
        componentWillReceiveProps(遗弃)
        getDerivedStateFromProps 
        shouldComponentUpdate
        componentWillUpdate
        render
    render 阶段不会产生副作用,react 可以异步处理组件更新(还可能在多个线程中处理)。然而标记了 unsafe的方法经常误解和误用。开发者可能会把一些有副作用的操作放在这些回调方法中,这可能会导致一些问题在新的一步渲染阶段。
        Commit 阶段:
        getSnapshotBeforeUpdate
        componentDidMount
        componentDidUpdate
        componentWillUnmount
    因为这些方法在同步的commit阶段,这些回调函数可能会包含副作用并且操作dom。
        Render阶段:
        React的调度算法总是从最顶部的HostRoot fiber节点用renderRoot函数。react 会跳过已经处理的fiber节点直到找到未完成的任务。例如,如果调用setState在很深的组件的树中,react 会重顶部重新开始但是会快速跳过父亲节点直到到达调用setState方法的组件。
        任务循环的只要不步骤:
        所有的fiber节点都在任务循环中处理。

function workLoop(isYieldy){
If(!isYieldy){
While(nextUnitOfWork !== null){
nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
}
}else{…}
}
        nextUnitOfWork 保存了指向有任务要做的fiber节点。当react 遍历fiber树时,react 用这个变量来识别有未完成任务的fiber节点。nextUnitWork可能保存了下一个fiber节点或者null。当指向null,react 会退出任务循环并且为提交变更做准备。
        有4个主要的函数用来遍历fiber树或者初始化或者完成任务。
        1.  performUnitWork
        2.  beginWork
        3.  completeUnitOfWork
        4.  completeWork
        performUnitOfWork 从workInProgressTree接受一个fiber节点,调用beginWork函数来开始工作。这个函数会开始所有需要在fiber中执行的活动。beginWork总是返回一个指针指向下一个要处理的子节点或者null。
        如果这里有下一个子节点,会在任务循环中赋值给nextUnitofWork变量。然而,如果没有子节点,react 知道到达了分支的边界,所以会完成当前的节点任务。一旦节点任务完成,react需要执行兄弟节点并且会返回到父亲节点。当workInProgress 节点没有子节点,react会进入这个循环函数。
        在commit阶段,这个阶段以completeRoot这个函数开始。当react进入到这个阶段,react中有2棵树和副作用链表。一棵树代表了当前渲染到页面上的状态。然后在render阶段会创建一棵替换的树,即workInProgress树,代表了需要映射到页面上的状态。workInProgress树和fiber树一样也是通过child 和 sibling指针进行关联。副作用链表是render阶段的结果。整个render阶段的目标是找出需要插入,更新,或者删除的节点,同时找到需要调用生命周期函数的组件。这也是在
        Commit节点需要遍历的节点的集合。
        Commit阶段的步骤:
        1.  在标记了Snapshot的节点上调用getSnapshotBeforeUpdate生命周期函数
        2.  在标记了Deletion副作用的节点上调用componentWillUnmount生命周期函数。
        3.  执行所有节点的插入,更新,删除操作。
        4.  设置finishedWork树作为当前的树。
        5.  在标记了Placement的节点上调用componentDidmount生命周期函数。
        6.  在标记了更新副作用的节点上调用componentDidUpdate生命周期函数。
        在调用完预变更方法getSnapshotBeforeUpdate,react会提交所有的副作用。分为两个阶段,第一个阶段:执行所有的dom更新操作以及ref的卸载,然后react会把任务执行完的到fiberRoot的任务完成的树标记为workInProgress树作为当前的树。在第二阶段,react会调用所有的其他的生命周期函数和ref回调函数。这些方法作为独立的阶段因此替换,更新和删除操作已经被调用。

参考:https://indepth.dev/inside-fiber-in-depth-overview-of-the-new-reconciliation-algorithm-in-react/

    
 

react fiber 的运行机制的更多相关文章

  1. 【react】什么是fiber?fiber解决了什么问题?从源码角度深入了解fiber运行机制与diff执行

    壹 ❀ 引 我在[react] 什么是虚拟dom?虚拟dom比操作原生dom要快吗?虚拟dom是如何转变成真实dom并渲染到页面的?一文中,介绍了虚拟dom的概念,以及react中虚拟dom的使用场景 ...

  2. 微信小程序剖析【下】:运行机制

    在上一篇<微信小程序「官方示例代码」浅析[上]>中,我们只是简单的罗列了一下代码,这一篇,让我们来玩点刺激的——就是看看IDE的代码,了解它是怎么运行的. 还好微信的开发团队在软件工程的实 ...

  3. Omi架构与React Fiber

    原文链接-https://github.com/AlloyTeam/omi/tree/master/tutorial 写在前面 Omi框架在架构设计的时候就决定把update的控制权交给了开发者,视灵 ...

  4. React Fiber源码分析 (介绍)

    写了分析源码的文章后, 总觉得缺少了什么, 在这里补一个整体的总结,输出个人的理解~ 文章的系列标题为Fiber源码分析, 那么什么是Fiber,官方给出的解释是: React Fiber是对核心算法 ...

  5. React: React的组件状态机制

    一.简介 在React中,有两个核心的默认属性,分别是state和props.state会记录组件的状态,React根据状态的变化,会对界面做相应的调整或渲染.props则是数据流向属性,React通 ...

  6. 瞧一瞧React Fiber

    啥是React Fiber? React Fiber,简单来说就是一个从React v16开始引入的新协调引擎,用来实现Virtual DOM的增量渲染. 说人话:就是一种能让React视图更新过程变 ...

  7. (十三)Maven插件解析运行机制

    这里给大家详细说一下Maven的运行机制,让大家不仅知其然,更知其所以然. 1.插件保存在哪里? 与我们所依赖的构件一样,插件也是基于坐标保存在我们的Maven仓库当中的.在用到插件的时候会先从本地仓 ...

  8. 深入理解JavaScript运行机制

    深入理解JavaScript运行机制 前言 本文是写作在给团队新人培训之际,所以其实本文的受众是对JavaScript的运行机制不了解或了解起来有困难的小伙伴.也就是说,其实真正的原理和本文阐述的并不 ...

  9. javascript运行机制

    太久没更新博客了,Javascript运行机制 Record it 1.代码块 JavaScript中的代码块是指由<script>标签分割的代码段.例如: <script type ...

  10. ASP.NET MVC的运行机制--url的全局分析

    全局 首先我们来看一副图片       首先,用户通过Web浏览器向服务器发送一条url请求,这里请求的url不再是xxx.aspx格式,而是http://HostName/ControllerNam ...

随机推荐

  1. 【C++ 泛型编程01:模板】函数模板与类模板

    [模板] 除了OOP外,C++另一种编程思想称为 泛型编程 ,主要利用的技术就是模板 C++提供两种模板机制:函数模板和类模板 函数模板 函数模板作用 建立一个通用函数,其函数返回值类型和形参类型可以 ...

  2. 搭个ChatGPT算法模型,离Java程序员有多远?

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 最近 ChatGPT 很火,火到了各行各业.记得去年更多的还是码农最新体验后拿它搜代码,现在各 ...

  3. 线程基础知识10-volatile

    1 简介 Volatile保证了可见性和有序性,没有保证原子性. 1.1 保证可见性简介 可见性就是指当一个线程修改了共享变量的值时,其他线程能够立即得知这个修改.volatile变量做到了这一点. ...

  4. Java Day 2

    标识符 凡是变量中需要自己命名的地方都是标识符,例如:包名.类名.变量名等等 标识符的命名规则 1.Java的标识符只能用26个英文字母大小写,0-9的数字,下划线(_),和美元的符号($). 2.标 ...

  5. .NET 中的并发编程

    今天我们购买的每台电脑都有一个多核心的 CPU,允许它并行执行多个指令.操作系统通过将进程调度到不同的内核来发挥这个结构的优点.然而,还可以通过异步 I/O 操作和并行处理来帮助我们提高单个应用程序的 ...

  6. 模拟实现strlen的三种方法

    一.strlen()的工作原理 二.模拟实现strlen的三种方法 计数器方法 指针-指针 递归的方法 三.库函数实现strlen的思路 四.库函数的strlen同上面模拟实现strlen的区别 一. ...

  7. Class path contains multiple SLF4J bindings解决

    1.根据控制台查看冲突的日志依赖 本工程Maven依赖 <dependencies> <dependency> <groupId>org.slf4j</gro ...

  8. STM32F4跳转函数

    JMP2APP void JMP2APP(void) { pFunction Jump_To_Application; uint32_t JumpAddress; if (((*(__IO uint3 ...

  9. Eureka 注册中心和服务提供者

    什么是Eureka组件 spring cloud Eureka ,提供服务注册和服务发现的功能. 一:spring cloud Eureka Eureka Server 服务端 Eureka Clie ...

  10. 手写一个audio播放器,实现歌曲切换,列表歌曲循环,音量调节等 vue组件

    1 <template> 2 <div class="wrapper"> 3 <svg 4 t="1673833915638" 5 ...