React Fiber 架构明白

React Fiber 架构明白

援用原文:
React Fiber Architecture

React Fiber is an ongoing reimplementation of React’s core algorithm. It is the culmination of over two years of research by the React team.

The goal of React Fiber is to increase its suitability for areas like animation, layout,and gestures. Its headline feature is incremental rendering: the ability to split rendering work into chunks and spread it out over multiple frames.

Other key features include the ability to pause, abort, or reuse work as new updates come in; the ability to assign priority to different types of updates; and new concurrency primitives.

React Fibre 是 React 中心算法正在举行的从新完成。它是 React 团队两年多的研究成果。

React Fiber 的目的是进步其对动画,规划和手势等范畴的适用性。它的主体特征是增量衬着:能够将衬着事变支解成块,并将其疏散到多个帧中。

其他重要功用包含在举行更新时停息,中缀或从新运用事变的才能,为差别范例的更新分派优先权的才能和新的并发原语。

React16之前组件的衬着逻辑

先来看一下react组件衬着时阅历的性命周期:

挂载阶段:

  • constructor()
  • componentWillMount()
  • render()
  • componentDidMount()

更新阶段:

  • componentWillReceiveProps()
  • shouldComponentUpdate()
  • componentWillUpdate()
  • render()
  • componentDidUpdate

卸载阶段:

  • componentWillUnmount()

在之前的版本中,如果你完成一个很庞杂的深度嵌套的复合组件,会涌现下面的状况:

现有层级关联以下的四个组件:

《React Fiber 架构明白》

组件衬着时挪用的性命周期递次:

《React Fiber 架构明白》

上图展现的是A,B,C,D的挂载阶段挪用的性命周期衬着递次,能够看到从顶层组件最先挪用各性命周期,一向向下,直至挪用完最底层子组件的性命周期。然后再向上挪用。
组件更新阶段同理。

组件挂载以后,如果修正最上层组件的数据(state),组件更新时的挪用栈:

《React Fiber 架构明白》

如果这是一个很大,层级很深的组件,能够想像到,组件在衬着时,挪用栈太长,再加上如果在时代举行了种种庞杂的操纵,就可能致使长时候壅塞主线程,react衬着它须要几十以至几百毫秒,如许的话react就会一向占用浏览器主线程,任何其他的操纵(包含用户的点击,鼠标挪动等操纵)都没法实行,带来非常不好的用户体验。

React Fiber的涌现

React Fiber 就是为了处置惩罚上面的题目而生。

宛如一个潜水员,当它一头扎进水里,就要往最底层一向游,直到找到最底层的组件,然后他再登陆。
在这时候代,岸上发作的任何事,都不能对他举行滋扰,如果有更重要的事变须要他去做(如用户操纵),也必需得等他登陆。

Fiber 本质上是一个假造的客栈帧,新的调理器会依据优先级自在调理这些帧,从而将之前的同步衬着改成了异步衬着,在不影响体验的状况下去分段盘算更新。它让潜水员会每隔一段时候就登陆,看是不是有更重要的事变要做。

《React Fiber 架构明白》

关于怎样区分优先级,React 有自身的一套逻辑。关于动画这类及时性很高的东西,也就是 16 ms 必需衬着一次保证不卡顿的状况下,React 会每 16 ms(之内) 停息一下更新,返返来继承衬着动画。

React Fiber 架构

调理拆分为小使命

浏览器自身也不停进化中,跟着页面由简朴的展现转向WebAPP,它须要一些新才能来承载更多节点的展现与更新。
下面是一些自救步伐:

  • requestAnimationFrame
  • requestIdleCallback
  • web worker
  • IntersectionObserver

react官方采纳的是 requestIdleCallback,为了兼容一切平台,facebook 零丁完成了其功用,作为一个自力的 npm 包运用 react-schedule

其作用是会在浏览器余暇时代顺次挪用函数, 这就能够在主事宜轮回中实行背景或低优先级的使命,而且不会对像动画和用户交互如许耽误触发而且症结的事宜发生影响。函数平常会按先进先挪用的递次实行,除非函数在浏览器挪用它之前就到了它的超时时候。

简化后的大抵流程图以下:

《React Fiber 架构明白》

Fiber Node 及 Fiber Tree

  • 从流程图上看到会有 Fiber Node 节点,这个是在 react 天生的 Virtual Dom 基础上增添的一层数据结构,重要是为了将递归遍历转变成轮回遍历,合营 requestIdleCallback API, 完成使命拆分、中缀与恢复。为了完成轮回遍历,Fiber Node 上携带了更多的信息。
  • 每一个 Fiber Node 节点与 Virtual Dom 一一对应,一切 Fiber Node 连接起来组成 Fiber tree, 是个单链表树结构

两个阶段:reconciliationcommit

关于异步衬着,如今衬着有两个阶段:reconciliationcommit 。前者历程是能够打断的,后者不能停息,会一向更新界面直到完成。

reconciliation 处置惩罚历程

  • 当实行 setState() 或初次 render() 时,进入事变轮回,轮回体中处置惩罚的单元为 Fiber Node, 等于拆分使命的最小单元,从根节点最先,自顶向下逐节点组织 workInProgress tree(构建中的新 Fiber Tree)。
  • 每一个事变处置惩罚单元做的事变,由 beginWork(), completeUnitOfWork() 两部份组成。
  • beginWork()重要做的事变是从顶向下天生一切的 Fiber Node,并标记 Diff, 不包含兄弟节点,每一个 Fiber Node 的处置惩罚历程依据组件范例略有差别,以 ClassComponent 为例:

1 如果当前节点不须要更新,直接把子节点clone过来,要更新的话标记更新范例
2 更新当前节点状况(props, state, context等)
3 挪用shouldComponentUpdate()
4 挪用组件实例要领 render() 取得新的子节点,并为子节点建立 Fiber Node(建立历程会只管复用现有 Fiber Node,子节点增删也发作在这里)
5 如果没有发生 child fiber,进入下一阶段 completeUnitOfWork

  • completeUnitOfWork() 当没有子节点,最先遍历兄弟节点作为下一个处置惩罚单元,处置惩罚完兄弟节点最先向上回溯,直到再次归去根节点为止,将收集向上回溯历程当中的一切 diff,拿到 diff 后最先进入 commit 阶段。
  • 构建 workInProgress tree 的历程就是 diff 的历程,经由过程 requestIdleCallback 来调理实行一组使命,每完成一个使命后返来看看有无插队的(更紧要的),把时候控制权交还给主线程,直到下一次 requestIdleCallback 回调再继承构建workInProgress tree。

两个阶段涉及到的性命周期:

Reconciliation 阶段 (React算法,用来比较2颗树,以肯定哪些部份须要从新衬着

  • componentWillMount
  • componentWillReceiveProps
  • shouldComponentUpdate
  • componentWillUpdate

Commit 阶段 (用于显现React运用的数据变动。通常是setState的效果。终究致使从新衬着。

  • componentDidMount
  • componentDidUpdate
  • componentWillUnmount

因为 reconciliation 阶段是能够被打断的,所以 reconciliation 阶段会实行的性命周期函数就可能会涌现挪用屡次的状况,从而引发 Bug。所以关于 reconciliation 阶段挪用的几个函数,除了 shouldComponentUpdate 之外,其他都应该避免除运用,而且 React16 中也引入了新的 API 来处置惩罚这个题目。

因而官方推出了getDerivedStateFromProps,让你在render设置新state,你重要返回一个新对象,它就主动帮你setState。因为这是一个静态要领,你不能取到 this,固然你也不能操纵instance,这就阻挠了你屡次操纵setState。如许一来,getDerivedStateFromProps的逻辑应该会很简朴,如许就不会失足,不会失足,就不会打断DFS历程。

getDerivedStateFromProps庖代了本来的componentWillMountcomponentWillReceiveProps要领,该函数会在组件 初始化 和 更新 时被挪用

class ExampleComponent extends React.Component {
  // Initialize state in constructor,
  // Or with a property initializer.
  state = {};

  static getDerivedStateFromProps(nextProps, prevState) {
    if (prevState.someMirroredValue !== nextProps.someValue) {
      return {
        derivedData: computeDerivedState(nextProps),
        someMirroredValue: nextProps.someValue
      };
    }

    // Return null to indicate no change to state.
    return null;
  }
}

在进入commi阶段时,组件多了一个新钩子叫getSnapshotBeforeUpdate,它与commit阶段的钩子一样只实行一次。
getSnapshotBeforeUpdate 用于替代 componentWillUpdate ,该函数会在 update 后 DOM 更新前被挪用,用于读取最新的 DOM 数据。

因而全部流程变成如许:(援用大神@司徒正美的图)

《React Fiber 架构明白》

React16 性命周期函数用法发起

连系 React Fiber 架构 发起以下运用react性命周期

class ExampleComponent extends React.Component {
  // 用于初始化 state
  constructor() {}
  // 用于替代 `componentWillReceiveProps` ,该函数会在初始化和 `update` 时被挪用
  // 因为该函数是静态函数,所以取不到 `this`
  // 如果须要对照 `prevProps` 须要零丁在 `state` 中保护
  static getDerivedStateFromProps(nextProps, prevState) {}
  // 推断是不是须要更新组件,多用于组件机能优化
  shouldComponentUpdate(nextProps, nextState) {}
  // 组件挂载后挪用
  // 能够在该函数中举行要求或许定阅
  componentDidMount() {}
  // 用于取得最新的 DOM 数据
  getSnapshotBeforeUpdate() {}
  // 组件行将烧毁
  // 能够在此处移除定阅,定时器等等
  componentWillUnmount() {}
  // 组件烧毁后挪用
  componentDidUnMount() {}
  // 组件更新后挪用
  componentDidUpdate() {}
  // 衬着组件函数
  render() {}
  // 以下函数不发起运用
  UNSAFE_componentWillMount() {}
  UNSAFE_componentWillUpdate(nextProps, nextState) {}
  UNSAFE_componentWillReceiveProps(nextProps) {}
}

16 大版本重要更新还处置惩罚以下痛点:

  • 组件不能返回数组,最见的场所是UL元素下只能运用LI,TR元素下只能运用TD或TH,这时候这里有一个组件轮回天生LI或TD列表时,我们并不想再放一个DIV,这会损坏HTML的语义。
  • 弹窗题目,之前一向运用不稳定的unstable_renderSubtreeIntoContainer。弹窗是依靠本来DOM树的上下文,因而这个API第一个参数是组件实例,经由过程它获得对应假造DOM,然后一级级往上找,获得上下文。它的其他参数也很好用,但这个要领一向没有转正。。。
  • 非常处置惩罚,我们想知道哪一个组件失足,虽然有了React DevTool,然则太深的组件树查找起来照样很费劲。愿望有个要领告诉我失足位置,而且失足时能让我有时机举行一些修复事变
  • HOC的盛行带来两个题目,毕竟是社区鼓起的计划,没有考虑到ref与context的向下通报。
  • 组件的机能优化全凭人肉,而且重要集合在SCU,愿望框架能干些事变,纵然不必SCU,机能也能上去。

新特征:

  • render / 纯组件能够 return 任何数据结构
  • CreatePortal API,更好的处置惩罚 Dialog 这类场景组件
  • 新的 context api,尝试替代一部份 redux 的职责
  • 异步衬着/时候切片(time slicing),成倍进步机能
  • componentDidCatch,毛病边境,框架层面上进步用户 debug 的才能
  • 收集要求 IO(Suspense),更好的处置惩罚异步收集 IO

参考材料:

    原文作者:海丽安娜
    原文地址: https://segmentfault.com/a/1190000018701625
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞