【React】React Fiber

React Fiber

自从 react 从15版更新到16版后,虽然使用上差别不会很大也提供了一定的兼容性,但是 react 的底层架构确有了很大的变化。React Fiber横空出世…

在了解 Fiber 之前,我们先了解几个概念,以及为什么需要引入 Fiber。

Reconciliation (协调)

Reconciliation 是 React 的协调算法的高级描述。

Reconciliation 是被普遍理解为“虚拟 DOM”的算法。当你渲染一个 React 应用程序时,一个描述应用程序的节点树被生成并保存在内存中。然后将该树刷新到渲染环境——例如,在浏览器应用程序的情况下,它被转换为一组 DOM 操作。当应用程序更新时(通常通过setState),会生成一棵新树。新树与之前的树进行比较,以计算更新渲染应用程序所需的操作。

Scheduling (调度)

可以理解为:确定什么时候应该执行某段代码。

调度核心理念便是:我们可以随心所欲的控制我们的代码。

React 目前没有很大程度的利用调度,所以才会引入Fiber。

Fiber

我们已经确定 Fiber 的主要目标是使 React 能够利用调度。

具体来说,我们需要能够:

  1. 暂停工作,稍后再回来。
  2. 为不同类型的工作分配优先级。
  3. 重用之前完成的工作。
  4. 如果不再需要,则中止工作。

为了做到这一点,我们首先需要一种将工作分解为单元的方法。从某种意义上说,这就是Fiber。

一个 Fiber 代表一个工作单元。


React15 最大的问题就是 Reconciler(协调)阶段产生产生虚拟DOM是通过深度优先递归的,并且中途不可间断。所以假如虚拟DOM很深的话,由于 JS 线程和浏览器 GUI 线程是互斥的,处理 js 的时间过长,会导致浏览器刷新的时候掉帧,造成卡顿。而 React16 则实现了异步的可中断的更新。

那么 Fiber 到底是个啥?其实 Fiber 怎么说都可以…Fiber可以理解为一种架构,Fiber也可以理解为一种数据结构,也可以是常说的最小的工作单元。

常说的 Fiber 树就是将 Fiber 当作一种架构来看待的,Fiber怎么连接成树呢?三个关键的属性:

// 指向父级Fiber节点
this.return = null;
// 指向子Fiber节点
this.child = null;
// 指向右边第一个兄弟Fiber节点
this.sibling = null;

所以在 react16 里面,一个组件对应的就是一棵 Fiber 树。那更新的时候,Fiber 这棵树有什么作用呢?react16 里面有一个很有意思的技术解答了——“双缓存技术”。

双缓存简单来说就是,在 React 里面最多同时存在两棵 Fiber 树,都在内存中构建,构建完成后直接替换。在源码里面,当前屏幕上显示内容对应的 Fiber 树称为 current fiber 树,正在内存中构建的 Fiber 树称为 workInProgress fiber 树。两棵树之间通过 alternate 属性连接。

currentFiber.alternate === workInProgressFiber;
workInProgressFiber.alternate === currentFiber;

【React】React Fiber_第1张图片
上图中,rootFiber 是 react 应用,footFiberNode 是应用挂在的节点,current 指向的 fiber 是渲染在页面中的 fiber (即出现在屏幕中的视图),我们称它为 current fiber, current fiber 的每一个 fiber 节点都有一个 alternode 指向另一个棵树的相同 fiber 节点,我们称这个 fiber 为 workInProgress fiber。
  我们知道,当 react v16 前的版本更新时,会进行 jsx 和虚拟 dom 树进行 diff 算法,计算结果就是最终需要更新的视图。而在 react v16 diff 算法是将 jsx 和 workInProgress fiber 进行计算,最终得出最终视图,然后将 current 指针指向 workInProgress fiber,渲染新的视图。跟 workInProgress fiber 进行 diff 算法是在内存中进行的,即使被中断也对现有视图不产生影响。

那么 Fiber 和 React 里面重要的一个概念 JSX 有什么区别和联系呢?

简单来说,JSX 就是 html+js,由于和 UI 的本质形式很相似,所以这理解起来也很轻松。而通过 JSX 创建的组件就是 React15 里面的虚拟 DOM,实质上虚拟 DOM 是由 React.createElement 这个函数创建的。其中就要用到 babel 转译器了。

Babel 转译 JSX 的过程简要:

Babel 通过 parse() 函数将 JSX 解析成 AST 抽象语法树,一种树状的形式表现编程语言的语法结构。

依据解析的 AST,再通过 Babel 的 transform() 函数生成 React.createElement() 代码
再来看 React.createElement() 创建返回的 JSX 数据结构。

  return ReactElement(
    type,
    key,
    ref,
    self,
    source,
    ReactCurrentOwner.current,
    props,
  );
}

所以,由此可以看见,一个组件的JSX和Fiber节点的数据结构不同。组件在 mount 时,根据 JSX 来创建对应的 Fiber 节点。

扩展

通过上文我们知道了,react 是通过双缓存技术来实现可中断的更新,那么为什么不用原本的架构(虚拟 dom)来做双缓存呢?

以下两点最为关键:

  1. fiber 是典型以空间换时间,提高运行效率;
  2. fiber 的出现是实现 function component 及 hook(state数据持久化)的关键;

参考文献:

  1. React Fiber 是什么
  2. 浅谈 React 里面的 Fiber
  3. react fiber 概念及原理

你可能感兴趣的:(前端,react.js,javascript,前端)