熟悉 react 的朋友都知道,在 react 中有个核心的算法,叫 diff 算法。web 界面由 dom 树组成,不一样的 dom 树会渲染出不一样的界面。react 使用 virtual dom 来表示 dom 树,而 diff 算法就是用于比较 virtual dom 树的区别,并更新界面须要更新的部分。diff 算法和 virtual dom 的完美结合的过程被称为 reconciler,这但是 react 攻城拔寨的绝对利器。有了 reconciler,开发者能够脱身操做真实的 dom 树,只须要向 react 描述界面的状态,而 react 会帮助你高效的完成真正 dom 操做。前端
那么 fiber 究竟有什么好的呢?react
不知道你们有没有遇到过这样的状况,点击一个页面的按钮时感受到页面没有任何的反应,让你怀疑电脑是否是死机了,而后你快速切出浏览器,发现电脑并无死机,因而再切回浏览器,这时候才发现页面终于更新了。为何会出现这种状况?在多数状况下,多是由于浏览器忙着执行相关的 js 代码,致使浏览器主线程没有及时响应用户的操做或者没有及时更新界面。下面这张图就表示了这种现象,你的公司只有一个程序员 (main thread),当这个程序员在执行你的任务 (your code) 时,处于沉浸式编程的状态,没法响应外部的其余事件,什么下班吃饭,都是不存在的。这就像浏览器忙着执行 js 代码的时候,不会去执行页面更新等操做。程序员
原先的 stack reconciler 像是一个递归执行的函数,从父组件调用子组件的 reconciler 过程就是一个递归执行的过程,这也是为何被称为 stack reconciler 的缘由。当咱们调用 setState 的时候,react 从根节点开始遍历,找出全部的不一样,而对于特别庞大的 dom 树来讲,这个递归遍历的过程会消耗特别长的时间。在这个期间,任何交互和渲染都会被阻塞,这样就给用户一种“死机”的感受。web
在作显示方面的工做时,常常会听到一个目标叫 60 帧,这表示的是画面的更新频率,也就是画面每秒钟更新 60 次。这是由于在 60 帧的更新频率下,页面在人眼中显得流畅,无明显卡顿。每秒钟更新 60 次也就是每 16ms 须要更新一次页面,若是更新页面消耗的时间不到 16ms,那么在下一次更新时机来到以前会剩下一点时间执行其余的任务,只要保证及时在 16ms 的间隔下更新界面就彻底不会影响到页面的流畅程度。fiber 的核心正是利用了 60 帧原则,实现了一个基于优先级和 requestIdleCallback 的循环任务调度算法。算法
function fiber(剩余时间) {
if (剩余时间 > 任务所需时间) {
作任务;
} else {
requestIdleCallback(fiber);
}
}
复制代码
fiber 还会为不一样的任务设置不一样的优先级,高优先级任务是须要立刻展现到页面上的,好比你正在输入框中输入文字,你确定但愿你的手指在键盘上敲下每个按键时,输入框能立马作出反馈,这样你才能知道你的输入是否正确,是否有效。低优先级的任务则是像从服务器传来了一些数据,这个时候须要更新页面,好比这篇文章喜欢的人数+1 或是评论+1,这并非那么紧急的更新,延迟 100-200ms 并不会有多大差异,彻底能够在后面进行处理。fiber 会根据任务优先级来动态调整任务调度,优先完成高优先级的任务。编程
{
Synchronous: 1, // 同步任务,优先级最高
Task: 2, // 当前调度正执行的任务
Animation 3, // 动画
High: 4, // 高优先级
Low: 5, // 低优先级
Offscreen: 6, // 当前屏幕外的更新,优先级最低
}
复制代码
在 fiber 架构中,有一种数据结构,它的名字就叫作 fiber
,这也是为何新的 reconciler 叫作 fiber 的缘由。fiber
其实就是一个 js 对象,这个对象的属性中比较重要的有 stateNode
、tag
、return
、child
、sibling
和 alternate
。api
Fiber = {
tag // 标记任务的进度
return // 父节点
child // 子节点
sibling // 兄弟节点
alternate // 变化记录
.....
};
复制代码
咱们能够看出 fiber
基于链表结构,拥有一个个指针,指向它的父节点子节点和兄弟节点,在 diff 的过程当中,依照节点链接的关系进行遍历。浏览器
在 fiber 中,更新是分阶段的,具体分为两个阶段,首先是 reconciliation 的阶段,这个阶段在计算先后 dom 树的差别,而后是 commit 的阶段,这个阶段将把更新渲染到页面上。第一个阶段是能够打断的,由于这个阶段耗时可能会很长,所以须要暂停下来去执行其余更高优先级的任务,第二个阶段则不会被打断,会一口气把更新渲染到页面上。服务器
componentWillMount
、
componentWillReceiveProps
和
componetWillUpdate
标记为 unsafe,并使用新的生命周期函数
getDerivedStateFromProps
和
getSnapshotBeforeUpdate
进行替换。
还有一个问题是饥饿问题,意思是若是高优先级的任务一直插入,致使低优先级的任务没法获得机会执行,这被称为饥饿问题。对于这个问题官方提出的解决方案是尽可能复用已经完成的操做来缓解。相信官方也正在努力提出更好的方法去解决这个问题。微信
文 / Xss
编 / 荧声
本文已由做者受权发布,版权属于创宇前端。欢迎注明出处转载本文。本文连接:knownsec-fed.com/2018-10-23-…
想要订阅更多来自知道创宇开发一线的分享,请搜索关注咱们的微信公众号:创宇前端(KnownsecFED)。欢迎留言讨论,咱们会尽量回复。
感谢您的阅读。