平平无奇古哥哥

V1

2023/05/23阅读:14主题:山吹

对线面试官:怎么理解React的Fiber和并发模式

面试官:

有了解过Fiber吗,讲一下你对Fiber的理解?

小黑子:

在v16之前,React的渲染流程是,JSX -> render function -> VDOM。

v16之后,React的渲染流程变成了,JSX -> render function -> VDOM -> Fiber。

很显然,使用Fiber就相当于是把虚拟DOM多转换了一次,举个例子:

虚拟DOM结构是这样的:

转换成Fiber之后结构是这样的:

其中child指向子节点,return指向父节点,sibling指向的是下一个兄弟节点。

这样就形成了一个链表树的结构,在遍历的时候就可以采用循环的方式去遍历。


面试官:

那什么是并发模式呢,有什么作用?

小黑子:

并发在服务端一般针对的是请求,高并发是指大量请求在短时间内打到服务器上,造成性能瓶颈。

而在React里,并发指的是触发setState的事件,一次setState就是一次更新,如果在短时间内触发了多次setState,就会出现并发问题。

如果一次setState的耗时很长,在一帧之内还没有更新完,那么就会让用户感觉卡顿。

而由于React的更新机制效率比较低,在一些极端场景中,可能会出现耗时长的更新任务,为了解决极端场景的性能瓶颈问题,React设计了并发模式。

总结一句话就是:并发模式是用来解决React自身更新性能差的问题。


面试官:

好的,那你说React的更新性能不好,原因是什么呢?

小黑子:

主要有两方面的原因:

  1. 一是在v16之前,React更新时的diff是“无脑diff”,由于React内部并不知道具体哪些组件需要更新,所以setState时会将整颗虚拟DOM树进行比对,说直白点就是一竿子捅到底,这样会导致很多不必要的更新计算,影响到性能。即便React提供了包括shouldComponentUpdate、memo等API让开发者去手动优化,在复杂场景依然会出现过度更新的情况。而Vue由于采用了响应式的依赖收集,在状态改变时就已经知道哪些组件需要更新,只需要通知到对应的组件,在组件内部做diff算法,更新粒度会小很多,所以Vue压根就不需要shouldComponentUpdate也能表现很好。
  2. 第二个原因是JSX过于自由,在编译时能做的静态优化比较有限,这也导致React在性能上不如Vue。

面试官:

😏你还真是个小黑子啊,你小子是尤雨溪派来的吧,那你再讲讲为什么Fiber要设计成链表树的结构?

小黑子:

是为了实现可中断更新,因为并发模式要求将任务拆成一段一段,每完成一小段就去看看一帧内的时间还够不够,不够的话就先暂停,让优先级更高的用户事件先响应,等到浏览器空闲了再去执行暂停了的任务。

而由于Fiber树是一颗链表树,它不仅记录着父子上下级关系,还记录着前后的顺序,相当于遍历每个节点的顺序都被记录了下来,所以即便中断了,也可以在之后恢复。


面试官:

那你再讲讲优先级是怎么回事?

小黑子:

刚刚不是讲到可中断更新嘛,为了实现低优先级任务给高优先级任务让步,React弄了一套优先级机制,并且实现了一套任务调度,有点类似于实现了属于React自己的事件循环机制。(不得不说React玩得可真花,而且这里面还用到了二进制和小顶堆的知识。)

具体来说就是,React的Scheduler给任务的优先级划分成5个级别(Nopriprity除外):

除了0之外,数字越小的优先级越高。ImmediatePriority 是最高优先级,用于表示紧急任务,主要是一些离散型事件,像用户点击和输入,以及开发者手动标记的高优先级任务。

其次是UserBlockingPriority,主要是一些连续型事件,比如scroll,drag,mouseover,这些虽然也是事件但相对点击和输入来说并不那么紧急。

另外NormalPriority是普通优先级,LowPriority 低优先级,IdlePriority是空闲优先级,优先级最低。

至于怎么判断具体任务属于哪个优先级,React另外弄了一套Lane优先级的机制,使用二进制来表示优先级,然后在运行时会将Lane优先级转换成Scheduler优先级。

而关于Lane优先级里面具体的实现细节,今天我就不讲了,如果我能进入你们公司,再单独开个分享会讲讲吧。


面试官:

😂哈哈,你小子还装上了。那你说了这么多,并发模式有实际的应用场景吗?

小黑子:

有,但不多。因为前端的绝大多数场景,并不会触及到React的性能瓶颈,对于前端来说,纯CPU计算的性能损耗甚至远不及一次DOM操作,而React的可中断更新,只能中断纯计算,并不能中断DOM操作。

我这里举一些并发模式可能会起作用的场景:

  1. 游戏应用:游戏应用有频繁的交互,并发模式可以用来提升交互体验。当然,很多场景其实也可以用防抖和节流来优化。
  2. 地图应用:地图应用往往有大量的数据渲染,会带来复杂的计算,开启并发模式能起到一定优化作用。
  3. 机器学习应用:比如用Tesorflow.js开发浏览器人工智能。因为机器学习的计算大多是CPU密集型的且计算量繁大,理论上也是并发模式的适用场景之一。

面试官:

看来你小子懂的挺多啊,下周来参加复试吧。

小黑子:

好的,收到,看我下周继续来虐你。

分类:

前端

标签:

前端

作者介绍

平平无奇古哥哥
V1