此章节会通过两个 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. Java 基础 JRE和JDK的区别

    JRE(Java Runtime Environment,Java运行时环境)是Java程序运行所需的软件环境,包含Java虚拟机和Java基础类库. JDK(Java Development Kit ...

  2. softmax in pytorch

    背景 在分类中,最常见的设置是一个输入,输出是类数目大小的向量.预测的输入类将是在最后一个网络层中具有最大条目的相应类.在分类任务中,交叉熵损失(交叉熵)是训练这类网络最常见的损失函数.交叉熵损失可以 ...

  3. VMware vsphere client报错问题

    今天出现一种情况,搞了很久,重装client都不行,原来很简单,防止再犯.     异常信息:"VirtualInfrastructure.Utils.ClientsXml"的类型 ...

  4. Anveshak: Placing Edge Servers In The Wild

    Anveshak:在野外放置边缘服务器 本文为SIGCOMM 2018 Workshop (Mobile Edge Communications, MECOMM)论文. 笔者翻译了该论文.由于时间仓促 ...

  5. Spring Boot 实现 RabbitMQ 延迟消费和延迟重试队列

    本文主要摘录自:详细介绍Spring Boot + RabbitMQ实现延迟队列 并增加了自己的一些理解,记录下来,以便日后查阅. 项目源码: spring-boot-rabbitmq-delay-q ...

  6. Redis线程模型

    Redis 基于 Reactor 模式开发了自己的网络事件处理器: 这个处理器被称为文件事件处理器(file event handler): 文件事件处理器使用 I/O 多路复用(multiplexi ...

  7. MYSQL数据库数据拆分之分库分表总结

    数据存储演进思路一:单库单表 单库单表是最常见的数据库设计,例如,有一张用户(user)表放在数据库db中,所有的用户都可以在db库中的user表中查到. 数据存储演进思路二:单库多表 随着用户数量的 ...

  8. [Swift]LeetCode210. 课程表 II | Course Schedule II

    There are a total of n courses you have to take, labeled from 0 to n-1. Some courses may have prereq ...

  9. [Swift]LeetCode963. 最小面积矩形 II | Minimum Area Rectangle II

    Given a set of points in the xy-plane, determine the minimum area of any rectangle formed from these ...

  10. [Swift]LeetCode988. 从叶结点开始的最小字符串 | Smallest String Starting From Leaf

    Given the root of a binary tree, each node has a value from 0 to 25 representing the letters 'a' to  ...