此章节会通过两个 demo 来展示 Stack Reconciler 以及 Fiber Reconciler 的数据结构。

个人博客

首先用代码表示上图节点间的关系。比如 a1 节点下有 b1、b2、b3 节点, 就可以把它们间的关系写成 a1.render = () => [b1, b2, b3];

var a1 = { name: 'a1', render = () => [b1, b2, b3] }
var b1 = { name: 'b1', render = () => [c1] }
var b2 = { name: 'b2', render = () => [c2] }
var b3 = { name: 'b3', render = () => [] }
var c1 = { name: 'c1', render = () => [d1] }
var c2 = { name: 'c2', render = () => [] }
var d1 = { name: 'd1', render = () => [d2] }
var d2 = { name: 'd2', render = () => [] }

Stack Reconciler

React 16 之前,节点之间的关系可以用数据结构中树的深度遍历来表示。

如下实现 walk 函数, 将深度遍历的节点打印出来。

walk(a1)

function walk(instance) {
if (!instance) return
console.log(instance.name)
instance.render().map(walk)
}

输出结果为: a1 b1 c1 d1 d2 b2 c2 b3

Fiber Reconciler

React 16 中,节点之间的关系可以用数据结构中的链表来表示。

节点之间的链表有三种情形, 用图表示如下:

  1. 父节点到子节点(红色虚线)
  2. 同层节点(黄色虚线)
  3. 子节点到父节点(蓝色虚线)

父节点指向第一个子节点, 每个子节点都指向父节点,同层节点间是单向链表。

首先, 构建节点的数据结构, 如下所示:

var FiberNode = function(instance) {
this.instance = instance
this.parent = null
this.sibling = null
this.child = null
}

然后创建一个将节点串联起来的 connect 函数:

var connect = function(parent, childList) {
parent.child = childList.reduceRight((prev, current) => {
const fiberNode = new FiberNode(current)
fiberNode.parent = parent
fiberNode.sibling = prev
return fiberNode
}, null) return parent.child
}

在 JavaScript 中实现链表的数据结构可以巧用 reduceRight

connect 函数中实现了上述链表关系。可以像这样使用它:

var parent = new FiberNode(a1)
var childFirst = connect(parent, a1.render())

这样子便完成了 a1 节点指向 b1 节点的链表、b1、b2、b3 节点间的单向链表以及 b1、b2、b3 节点指向 a1 节点的链表。

最后剩下 goWalk 函数将全部节点给遍历完。

// 打印日志以及添加列表
var walk = function(node) {
console.log(node.instance.name)
const childLists = node.instance.render()
let child = null
if (childLists.length > 0) {
child = connect(node, childLists)
}
return child
} var goWalk = function(root) {
let currentNode = root while (true) {
const child = walk(currentNode)
// 如果有子节点
if (child) {
currentNode = child
continue
} // 如果没有相邻节点, 则返回到父节点
while (!currentNode.sibling) {
currentNode = currentNode.parent
if (currentNode === root) {
return
}
} // 相邻节点
currentNode = currentNode.sibling
}
} // 调用
goWalk(new FiberNode(a1))

打印结果为 a1 b1 c1 d1 d2 b2 c2 b3

Fiber Reconciler 的优势

通过分析上述两种数据结构实现的代码,可以得出下面结论:

  • 基于树的深度遍历实现的 Reconciler: 一旦进入调用栈便无法暂停;
  • 基于链表实现的 Reconciler: 在 while(true) {} 的循环中, 可以通过 currentNode 的赋值重新得到需要操作的节点,而在赋值之前便可以'暂停'来执行其它逻辑, 这也是 requestIdleCallback 能得以在 Fiber Reconciler 的原因。

相关链接

React Fiber 数据结构揭秘的更多相关文章

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

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

  2. Omi架构与React Fiber

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

  3. react fiber

    react fiber https://github.com/acdlite/react-fiber-architecture https://github.com/facebook/react/is ...

  4. 瞧一瞧React Fiber

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

  5. React Fiber源码分析 第二篇(同步模式)

    先附上两张流程图 1.scheduleRootUpdate  这个函数主要执行了两个操作  1个是创建更新createUpdate并放到更新队列enqueueUpdate, 1个是执行sheculeW ...

  6. React Fiber源码分析 第一篇

    先附上流程图一张 先由babel编译, 调用reactDOM.render,入参为element, container, callback, 打印出来可以看到element,container,cal ...

  7. 你真的懂redis的数据结构了吗?redis内部数据结构和外部数据结构揭秘

    Redis有哪些数据结构? 字符串String.字典Hash.列表List.集合Set.有序集合SortedSet. 很多人面试时都遇到过这种场景吧? 其实除了上面的几种常见数据结构,还需要加上数据结 ...

  8. React Fiber源码分析 第三篇(异步状态)

    先附上流程图~ 调用setState时, 会调用classComponentUpdater的enqueueSetState方法, 同时将新的state作为payload参数传进 enqueueSetS ...

  9. redis内部数据结构和外部数据结构揭秘

    Redis有哪些数据结构? 字符串String.字典Hash.列表List.集合Set.有序集合SortedSet. 很多人面试时都遇到过这种场景吧? 其实除了上面的几种常见数据结构,还需要加上数据结 ...

随机推荐

  1. javascript 插入DOM节点

    1.使用appendChild,把一个子节点添加到父节点的最后一个子节点,.innerText插入的是内容 HTML <!-- HTML结构 --> <p id="js&q ...

  2. XGBoost原理和公式推导

     本篇文章主要介绍下Xgboost算法的原理和公式推导.关于XGB的一些应用场景在此就不赘述了,感兴趣的同学可以自行google.下面开始: 1.模型构建 构建最优模型的方法一般是最小化训练数据的损失 ...

  3. SpringBoot报错:Table 'database_name.hibernate_sequence' doesn't exist

    引起条件: SpringBoot+JPA插入包含自增字段的对象 @Id @GeneratedValue private Integer id; 解决方法: 给注解添加属性 @Id @Generated ...

  4. ssm框架各自的作用

  5. ubuntu下安装pandas出现 compile failed with error code 1 in /tmp/pip_build_hadoop/pandas

    都是用pip装的,是不是应该用apt-get 装的呀 ubuntu下安装pandas (出现 compile failed with error code 1 in /tmp/pip_build_ha ...

  6. 找一个数组的最大和的连续子数组(时间复杂度 O(n))(二)

    要求: 要求数组从文件读取. 如果输入的数组很大,  并且有很多大的数字,  就会产生比较大的结果 (考虑一下数的溢出), 请保证你的程序能正常输出. 另外, 如果输入文件的参数有错误, 这个程序应该 ...

  7. python枚举类型

    笔记: 一:枚举类型的定义二:枚举类和普通类的区别 1:值不可变 2:具有防止相同标签得功能,不同标签的值可以相同! 三:枚举类型.枚举值.枚举名称 1:VIP.YELLOW.Value 获得标签的值 ...

  8. 产品经理教你如何构建电商电销 CRM 系统

    在电销或网销行业中老板们会经常问到,上个月渠道投放花了多少钱,来了多少量,转化率怎么样,获得了多少新线索,获客成本如何,销售额是多少? 劈天盖地的各种数据需求飞来,没有一个像样的系统该如何是好?这时候 ...

  9. 养成这8个编程习惯,你的Python性能将蹭蹭蹭地往上涨

    Python不以性能见长,但掌握一些技巧,也可尽量提高程序性能,避免不必要的资源浪费. 1. 使用局部变量 尽量使用局部变量代替全局变量:便于维护,提高性能并节省内存. 使用局部变量替换模块名字空间中 ...

  10. 树莓派3B+ HDMI连接显示屏 因供电问题而不能进入系统

    1.config.txt文件中hdmi_force_hotplug=1前面的注释符号"#"一定要去掉. 2.完成上述操作后,树莓派通过HDMI连接屏幕,一直在开机画面循环重复,却不 ...