这篇文章主要就是对一些常见的fiber的问题进行一些个人解读,可能不会特别深入,但是应该足够回答大部分提问了。
什么是 React Fiber?它与以前的调和机制有什么不同之处
React Fiber 是 React 16 中引入的一种新的渲染架构,它重新实现了调度算法和渲染流程。它将原来的同步渲染方式改为异步渲染,使得 React 应用能够更好地控制任务的优先级,实现更流畅的用户界面。
fiber的目的或者作用是什么
引入 React Fiber 的主要目的是为了解决长时间任务的中断问题,以改善用户界面的响应和性能。在传统的栈调用中,如果存在一个长时间的渲染任务,将会阻塞浏览器的主线程,导致用户无法与应用进行交互。Fiber 的目标是实现可中断和可恢复的渲染过程,使得 React 应用可以更好地响应用户的操作,并能够优先处理重要的任务。旨在优化渲染过程、实现异步渲染,并提高应用的性能和用户体验。
说一下fiber的结构
很多时候我们称他为Fiber树,这是因为Fiber既可以看作是链表,也可以看作是树。说他是树,是因为它的形状像树,但是并没有树的特征,树是一个只有指向子节点的指针,并没有指向父节点的指针。
markdown
/**
* fiber架构
* type: 标记类型
* key: 标记当前层级下的唯一性
* child : 第一个子元素 fiber
* sibling : 下一个兄弟元素 fiber
* return: 父fiber
* node: 真实dom节点
* props:属性值
* base: 上次的节点 fiber
* effectTag: 标记要执行的操作类型(删除、插入、更新)
*/
上面是一个正常的fiber架构里面常见的几个属性,除了一些继承下来的属性,下面是几个关键点: key : 唯一标识fiber节点的属性,帮助react识别哪些节点可以被添加移除或重排 sibling : 下一个兄弟元素 fiber,类似于链表结构,按同级元素一个链接一个,实现同级元素的快速插入、删除
child : 第一个子元素的 fiber,支持向下递归。每个Fiber节点可以有零个或多个子节点,这些子节点是当前节点的直接下级。子节点的处理顺序可以根据算法进行调整,以优化渲染性能。 return : 父 fiber。
说一下fiber是怎么实现的
简单来说,主要是通过两个原生的 API
来实现的 requestAnimationFrame
和 requestIdleCallback
显示器每秒 60
帧我们看着才不会感觉到卡,比如动画的时候,一帧的时间内布局和绘制结束,还有剩余时间,JS
就会拿到主线程使用权,如果 JS
某个任务执行过长,动画下一帧开始时 JS
还没有执行完,就会导致掉帧,出现卡顿。
所以就通过把 JS
任务分成更小的任务块,分到每一帧上的方式,一帧时间到先暂停 JS
执行,然后下一帧绘制任完成再把主线程交给 JS
,在每一帧绘制之前调用 requestAnimationFrame
;在每一帧空间阶段,就是一帧动画任务完成,下一帧还没到开始时间,这中间还有时间的话就调用 requetIdleCallback
,执行它里面的任务。 api说明: requestAnimationFrame(RAF): - RAF是一个浏览器提供的API,用于在下一次浏览器渲染帧之前执行回调函数。通常用于执行与动画和绘制相关的任务。 - 在React Fiber中,RAF被用于分割渲染任务,将任务拆分成多个小的任务单位,使其分布在多个刷新周期内执行。这样做的好处是避免长时间的任务执行,使得用户界面保持响应,并能够在每个浏览器帧之间进行任务切换。
requestIdleCallback(RIC): - RIC是一个浏览器提供的API,用于在浏览器空闲时执行回调函数。它会在浏览器处理其他高优先级任务后才触发回调,以确保不会影响用户体验。 - 在React Fiber中,RIC被用于执行渲染任务的优先级控制和调度。React将任务分为不同的优先级,并在浏览器空闲时调用RIC来执行具有不同优先级的任务。这种方式可以确保高优先级任务(如用户交互)得到及时响应,而低优先级任务(如异步渲染)会在浏览器空闲时才执行。
为什么requestAnimationFrame和requetIdleCallback这两个API对于Fiber架构很重要呢?
- RAF和RIC能够与浏览器的渲染流程和空闲时间进行协作,以提供更平滑和高效的用户体验。
- 它们提供了一个机制来分割、调度和控制任务的执行顺序和优先级,从而确保在不同优先级的任务之间获得良好的平衡。
- 这种任务的分割和调度方式使得React能够逐步渲染和处理大型复杂的组件树,而不会阻塞主线程的执行。
总结一下就是,RAF和RIC作为Web平台提供的原生API,与React Fiber架构一起工作,为React应用程序提供了更好的性能和用户体验。它们是Fiber实现任务拆分、优先级控制和调度的重要工具。
能用什么别的方式实现或者模拟fiber
JavaScript 提供了一种称为生成器 Generator 的功能,它可以用于实现协程。生成器函数可以暂停执行,并且在需要的时候可以从上一个暂停的位置恢复执行。通过调用生成器的 next() 方法,可以逐步执行生成器函数的代码并获取生成的值。此外,还可以使用 yield 关键字来指定生成器函数的暂停点,并将值传递给调用者。
那为什么不直接用generator
这个问题 React
开发者有在源码里回答过:
- 一是因为
React
是迭代的,而使用generator
+yield
的话需要把所有代码都包装成这个形式,非常麻烦,工作量很大 - 二是
generator
内部是有状态的,比如一个函数里有用到多个yield
中断,就像await
一样,有时候后面的会依赖前面的结果,可当后面的执行前,前面的又更新了,后面就无法拿到最新的值,这样就不可控了,所以就自己实现一个完全可控的
浏览器什么时候会有空呢
每秒60帧算,每帧就是 1000/60 = 16.7ms
差不多,每帧任务执行完会调 requetIdleCallback(callback)
,并且在 callback
中会有一个参数告诉我们当前帧还有多少时间给我们执行任务
那浏览器一帧内要做哪些事情
比如布局(layout
)、绘制(paint
)、JS
的执行、处理用户输入事件、requestAnimation
调用等,如果在一帧内处理完了这些剩余时间就用来执行 requetIdleCallback
,直到下一帧开始
如果浏览器很忙,一帧结束了还没执行怎么办
requetIdleCallback(callback, options)
,还有第二个参数,里面有个 timeout
字段(毫秒),如果超过这个时间还没有被执行的话,那么下一帧就会强制执行
React Fiber 如何处理用户交互和应用状态的变化?
使用事件委托的机制来处理用户交互。例如,React 使用事件委托机制将所有的事件监听器注册到顶层节点上,并将事件信息传递到对应的组件。当应用状态发生变化时,React Fiber 使用任务调度机制和异步渲染来处理组件更新,并将变化应用到实际的 DOM。
requetIdleCallback兼容性怎么样
兼容性因浏览器而异:
- Chrome: 支持自Chrome 47版本起。
- Firefox: 支持自Firefox 55版本起。
- Safari: 只在Safari 11.1及之后的版本中支持。
- Edge: 自Edge 15版本起开始支持。
- Internet Explorer: 不支持该API。
对于不支持requestIdleCallback的浏览器,可以使用polyfill或类似的替代方案来实现类似的功能。 整体来看,requetIdleCallback
兼容性是比较差的,React
也是通过 messageChannel
模拟实现的 requetIdleCallback
的功能以次来达到在不支持requestIdleCallback的浏览器中提供相似的空闲回调功能。
具体来说,React使用了一个MessageChannel对象,通过在两个页面间传递消息来模拟空闲回调。React在浏览器的主线程空闲时会发送一个空闲消息,然后使用MessageChannel在下一个空闲时刻触发回调。这种方式类似于requestIdleCallback的工作原理,通过利用浏览器的事件循环机制来模拟空闲时段的回调。
这种messageChannel模拟方法在不支持requestIdleCallback的浏览器中提供了类似的能力,使得React能够在这些浏览器中进行任务拆分和优先级控制,以实现更高效的渲染和响应用户交互。
简单说一下React Fiber 的工作原理。从任务调度、优先级、增量渲染等
React Fiber 的工作原理包括以下几个关键步骤:
- 任务调度:React Fiber 使用一个双缓冲技术,构建一个可中断的任务链表,每个任务单元称为一个 Fiber 节点,通过 diff 算法对 Fiber 树进行优化识别哪些节点需要更新。
- 优先级调度:每个 Fiber 节点都与一个优先级相关联。调度器根据任务的优先级决定任务执行的先后顺序。具有较高优先级的任务会打断正在执行的低优先级任务。
- 增量渲染:为了提高渲染的平滑度,React Fiber 将渲染任务拆分为多个小任务,在每个任务之间释放控制权给浏览器,从而避免了长时间的 JavaScript 执行阻塞。
展开说说 React Fiber 的调度算法
-
任务分割:
- React Fiber将渲染任务分割为多个小的任务单元,称为Fiber节点。每个Fiber节点代表一个组件或DOM节点。
- 每个Fiber节点都有一个优先级标记,用于决定任务的紧急程度。优先级可以是高、中、低等级。默认情况下,新任务会具有中等优先级。
-
调度器:
- 调度器是Fiber的核心部分,它负责控制任务的优先级和执行顺序。
- 调度器使用双缓冲技术,维护两个工作中的Fiber树,分别为当前工作树和备份树。当前工作树用于处理任务,备份树用于在任务中断时回滚到上一个稳定的状态。
- 调度器使用优先级比较和任务队列来决定哪些任务应该被调度和执行。任务队列按照优先级被划分成多个优先级队列,高优先级任务排在低优先级任务之前。
-
协调过程:
- React Fiber使用一种称为"Diff"的算法来比较新旧Fiber树的差异,并找出需要更新的部分。
- 在协调过程中,React会根据组件的优先级和更新的优先级判断是否需要中断当前任务,以允许更紧急和高优先级的任务先执行。
- 调度器会根据任务的优先级和状态来更新任务队列中的任务顺序,确保高优先级任务在下一个浏览器空闲时刻得到及时执行。
-
增量渲染:
- React Fiber实现了增量渲染的能力,即将渲染任务分成多个小的任务单元,并通过空闲回调(如requestIdleCallback)在浏览器空闲时执行。
- 增量渲染使得React能够逐步渲染复杂的组件树,而不会阻塞主线程。每个渲染单元被称为一个工作单元,可以在多个帧之间分布执行。
总结一下就是React Fiber的调度算法通过任务分割、优先级控制和增量渲染实现了可中断、高效且流畅的渲染过程。它能够智能地调度任务,根据任务的优先级和浏览器空闲状态来确定任务的执行顺序,以提供更好的用户体验。
那任务的优先级是如何影响协调顺序的呢。
在任务的优先级方面,React Fiber 使用优先级调度算法来决定哪些任务在给定时间片内应该优先执行。较高优先级的任务将打断较低优先级的任务,并将执行截止时间设置为低优先级任务的执行时间。这样可以保证高优先级任务的快速响应和交互性,同时保证低优先级任务的进行。
React
里有 5
个优先级的等级,高优先级的会被优先执行,低优先级的慢慢等下去,等级是这样的
Immediate
: 最高优先级,会马上执行的不能中断UserBlocking
: 这一般是用户交互的结果,需要及时反馈Normal
: 普通等级的,比如网络请求等不需要用户立即感受到变化的Low
: 低优先级的,这种任务可以延后,但最后始终是要执行的Idle
: 最低等级的任务,可以被无限延迟的,比如console.log()
如果是相同优先级的任务,就会按推入任务队列的顺序来执行
详细说一下时间切片和可中断渲染
-
时间切片:
- 时间切片是指将渲染任务分成更小、更可控的单元,以便在多个帧之间分布执行。这样可以将渲染任务均匀地分摊到多个浏览器帧中,避免长时间的渲染任务阻塞主线程。
- React Fiber通过将任务划分为多个小的任务单元(Fiber节点)来实现时间切片。每个任务单元都有自己的优先级,并按照优先级顺序执行。
- 在每个浏览器空闲时刻,React Fiber会选择当前优先级最高的任务单元进行执行,然后暂停并等待下一个空闲时刻继续执行。这样可以使任务在多个帧之间分布执行,从而保持用户界面的响应性。
-
可中断渲染:
- 可中断渲染是指在渲染过程中,可以根据任务的优先级中断当前任务并让更高优先级的任务先执行。
- React Fiber的调度器会根据任务的优先级判断是否需要中断当前任务。当一个更高优先级的任务进入调度器时,调度器会暂停当前任务,并将控制权交给更高优先级的任务。
- 可中断渲染使得React能够根据用户交互和任务优先级动态地调整任务的执行顺序,以提供更好的响应性和用户体验。
总结一下,通过时间切片和可中断渲染,React Fiber能够在渲染过程中将任务切分为更小的单元,并根据任务的优先级进行智能调度和暂停,从而提供更好的用户体验和性能。这样可以避免长时间渲染阻塞主线程,使应用具有更好的响应性,并充分利用浏览器的空闲时间来执行任务,提升渲染的效率。
React Fiber 中的虚拟 DOM 和实际 DOM 之间是如何交互和同步的
React Fiber 中的虚拟 DOM 是用于组件渲染和协调的中间表示。React 使用虚拟 DOM 与组件的实际 DOM 进行比较,确定不同的部分并进行必要的更新和重新渲染。在 React Fiber 中,虚拟 DOM 的比较和更新过程是在任务调度和更新阶段进行的,React Fiber 会根据更新的需求生成新的虚拟 DOM,并使用 diff 算法比较新旧虚拟 DOM 的差异,最后将差异应用到实际 DOM 上。
错误处理在 React Fiber 中是如何实现的?什么是错误边界?
React Fiber 使用错误边界机制来限制错误在组件树中的传播,并提供恰当的容错措施。错误边界是指一个特殊的组件,它包裹了子组件,并且可以捕获和处理子组件中发生的错误。一旦错误被错误边界捕获,React Fiber 将会停止错误的进一步传播,并渲染指定的错误信息或备用 UI,以避免整个应用崩溃。
在 React Fiber 中,如何处理复杂的状态更新和副作用?
React Fiber 使用批处理机制和异步渲染来处理复杂的状态更新和副作用。通过批处理机制,React Fiber 可以将多个状态更新合并为一次更新,从而提高更新性能。对于副作用,React Fiber 支持使用 useEffect 钩子函数来管理和处理副作用逻辑。React Fiber 还提供了任务调度和优先级的控制,以确保副作用的执行顺序和优先级。
React Fiber 如何应对并发更新和并发错误处理?
React Fiber 的设计目标之一就是支持并发更新。为了处理并发更新,React Fiber 用了一组工作单元,可以在浏览器空闲时间执行。这样可以确保及时响应用户交互,并在保持界面流畅性的同时处理并发更新。对于并发错误处理,React Fiber 通过错误边界机制和错误处理边界组件来处理错误的捕获和限制传播,从而防止整个应用程序崩溃。
React Fiber 中是否可以手动控制任务的执行顺序和优先级?
React Fiber 是提供了任务的优先级调度机制,可以让任务的执行顺序可以根据优先级进行控制。但是,React Fiber 的设计并不鼓励手动控制任务的执行顺序和优先级。优先级调度算法已经根据任务优先级和执行截止时间进行了优化,以提供最佳的性能和用户体验。手动控制任务的执行可能会破坏 React Fiber 的协调过程和性能优化,因此在大多数情况下,推荐依赖于 React Fiber 的优先级调度算法。
React Fiber 如何处理大量列表渲染的性能问题?
React Fiber 提供了虚拟滚动和分批加载等技术来处理大量列表渲染的性能问题。虚拟滚动技术只渲染可见区域内的列表项,而不是渲染整个列表,从而减少了渲染的负载。分批加载技术将列表数据分成。
详细说说React如何实现增量渲染
React 实现增量渲染是通过 Fiber 架构和协调器来实现的。增量渲染是一种渐进式渲染方式,它将整个渲染过程拆分成多个增量步骤,并将这些步骤分布在多个时间片段中执行,从而实现渲染的分阶段、增量式进行。
通过时间分片的方式将整个渲染过程分成多个时间片段。这样页面的渲染过程被分成多个增量步骤进行,避免了长时间的渲染阻塞。React
使用虚拟 DOM
和 Diff
算法来对比前后两个状态的差异,然后仅更新真正需要变化的部分,而不是重新渲染整个组件树。这样,React
只需要更新变化的部分,从而实现增量渲染。
增量渲染使得页面可以更快地显示,从而保持页面的流畅性和响应性,提高了用户体验。