【react框架】别把Fiber整得那么难理解,来参考下我是咋理解的,用大白话解释

文章目录

  • 前言
  • Fiber出现背景:响应式更新特性带来的问题
  • Fiber带来的好处:拆分任务、按需执行
  • 工作的简单原理:Scheduler与Fiber
  • Fiber对象长啥样
  • 用什么关键api去实现的原理
  • Fiber与Vue的响应式更新区别

前言

看b站的视频和网上一些博客,在讲fiber的时候,动不动就上源码解释,各种高大上的概念词汇,对于第一次接触fiber想了解它的人来说极其的不友好。

所以我就通过阅读别人的博客,结合自己的理解,写了一篇用大白话解释fiber的文章,希望能帮助到新人。

文中还有很多不严谨的地方,等我后续有空了慢慢完善


Fiber出现背景:响应式更新特性带来的问题

用过react的小伙伴们应该知道,当我们修改了一个组件的某个节点数据时,组件会重新去更新每一个节点,包括所有子组件,也就是大家常说的,自顶向下重新渲染组件

不相信的话可以用节点渲染{Math.random()}去验证。

在每次的重新渲染过程中,都会重新生成一颗新的虚拟dom树。当这颗虚拟树很复杂的时候(例如嵌套了很多复杂的子组件),主线程在做diff时,会长期霸占时长,这个时候留给重排重绘的时间就无法保证1s内运行60次,页面就会卡了(这里不理解的话可以看【计算机原理交集】一起探讨和梳理下,浏览器怎么解析HTML文件的)。

这是react15的痛点,16版本为了解决这个痛点才推出了fiber。


Fiber带来的好处:拆分任务、按需执行

简单来讲,用上了fiber后能够把diff的任务切割成非常小的小任务,然后看每16.6ms的主线程是否有空闲时期,有的话就塞入执行。

并且他还有个能力,打个不严谨的比方啊,例如某次的diff任务需要被分割成100份小任务,他不是一下子全部分割完,而是一边分割一边塞入每16.6ms的主线程空闲时期中执行。


工作的简单原理:Scheduler与Fiber

fiber的本质其实是一个js对象,可能代替了15版本的虚拟dom节点对象(猜测,等我以后看源码证实下)。

然后我们知道,diff遍历虚拟树的时候,用的是深度优先。然后每个fiber节点对象上都有父级、子级、兄弟节点的指向。

当主线程空闲时,遍历虚拟树,当主线程被占用时,记录遍历到的节点位置。接着主线程又空闲了,通过记录的位置找到任务暂停的节点,通过指向关系继续遍历。

任务就这样得到了切割。

其实做切分的动作是由React Scheduler去完成的,后面会说咋做的

Fiber对象长啥样

我从网上嫖来一段给大家看看,其实只需要知道有指向,有类型就ok了:

type Fiber = {
  // 用于标记fiber的WorkTag类型,主要表示当前fiber代表的组件类型如FunctionComponent、ClassComponent等
  tag: WorkTag,
  // ReactElement里面的key
  key: null | string,
  // ReactElement.type,调用`createElement`的第一个参数
  elementType: any,
  // The resolved function/class/ associated with this fiber.
  // 表示当前代表的节点类型
  type: any,
  // 表示当前FiberNode对应的element组件实例
  stateNode: any,

  // 指向他在Fiber节点树中的`parent`,用来在处理完这个节点之后向上返回
  return: Fiber | null,
  // 指向自己的第一个子节点
  child: Fiber | null,
  // 指向自己的兄弟结构,兄弟节点的return指向同一个父节点
  sibling: Fiber | null,
  index: number,

  ref: null | (((handle: mixed) => void) & { _stringRef: ?string }) | RefObject,

  // 当前处理过程中的组件props对象
  pendingProps: any,
  // 上一次渲染完成之后的props
  memoizedProps: any,

  // 该Fiber对应的组件产生的Update会存放在这个队列里面
  updateQueue: UpdateQueue<any> | null,

  // 上一次渲染的时候的state
  memoizedState: any,

  // 一个列表,存放这个Fiber依赖的context
  firstContextDependency: ContextDependency<mixed> | null,

  mode: TypeOfMode,

  // Effect
  // 用来记录Side Effect
  effectTag: SideEffectTag,

  // 单链表用来快速查找下一个side effect
  nextEffect: Fiber | null,

  // 子树中第一个side effect
  firstEffect: Fiber | null,
  // 子树中最后一个side effect
  lastEffect: Fiber | null,

  // 代表任务在未来的哪个时间点应该被完成,之后版本改名为 lanes
  expirationTime: ExpirationTime,

  // 快速确定子树中是否有不在等待的变化
  childExpirationTime: ExpirationTime,

  // fiber的版本池,即记录fiber更新过程,便于恢复
  alternate: Fiber | null,
}

用什么关键api去实现的原理

其实严谨来说是问Scheduler的实现原理,因为任务切分与派发都是他去做的。

我听网上说是之前用的伪requestIdleCallback,去做的实现。因为requestIdleCallback的兼容性很差,例如safari直接就不支持了。

然后react18使用了MessageChannel

想详细了解的,具体可以参考React Scheduler 为什么使用 MessageChannel 实现


Fiber与Vue的响应式更新区别

用过vue的都知道,vue的响应式更新是精确更新,例如某个组件的节点发生改动,那就只更新这个节点就好了。利用的就是proxy代理(vue2是defineProperty),但每个响应式变量都需要代理,每个组件都搜集了很多依赖,所以在性能上也不是十全十美的。

只能说这两者各有各的好吧。

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