,大家好,我是前端西瓜哥。,为了提高 React 的性能,React 团队在开发 React 16 时做了底层的重构,引入了 React Fiber 的概念。,Fiber,本意为 “纤维”,在计算机世界中则是 ”纤程“ 的意思。纤程可以看作是协程的一种,是一种任务调度方式。,JavaScript 是单线程的,有一个 event loop 的概念,它有一个有优先级的任务队列,只能按顺序执行一个任务,是不支持多个任务同时执行的。,这种设计的好处就是不用考虑多线程导致的顺序问题,并为此做一些加锁的额外逻辑,确保执行顺序符合预期。但也因为无法使用并行能力,在 CPU 密集的场景会有性能问题, 比如一个任务耗时过长会导致其他的任务,导致用户的交互响应发生延迟。,React 的组件更新是 CPU 密集的操作,因为它要做对比新旧虚拟 DOM 树的操作(diff,React 中 Reconcilation 负责),找出需要更新的内容(patch),通过打补丁的方式更新真实 DOM 树(React 中 Renderer 负责)。当要对比的组件树非常多时,就会发生大量的新旧节点对比,CPU 花费时间庞大,当耗时大大超过 16.6ms(一秒 60 帧的基准) 时,用户会感觉到明显的卡顿。,这一系列操作是通过递归的方式实现的,是 同步且不可中断 的。因为一旦中断,调用栈就会被销毁,中间的状态就丢失了。这种基于调用栈的实现,我们称为 Stack Reconcilation。,React 16 的一个重点工作就是优化更新组件时大量的 CPU 计算,最后选择了使用 “时间分片” 的方案,就是将原本要一次性做的工作,拆分成一个个异步任务,在浏览器空闲的时间时执行。这种新的架构称为 Fiber Reconcilation。,在 React 中,Fiber 模拟之前的递归调用,具体通过链表的方式去模拟函数的调用栈,这样就可以做到中断调用,将一个大的更新任务,拆分成小的任务,并设置优先级,在浏览器空闲的时异步执行。,前面我们说到使用了链表的遍历来模拟递归栈调用,其中链表的节点 React 用 FiberNode 表示。,FiberNode 其实就是虚拟 DOM,它记录了:,Fiber 通过 return 指向父 Fiber,child 指向子 Fiber 的首位、sibling 指向下一个兄弟节点。通过它们我们其实就能拿到一个完整的结构树。,对于:,形成的 Fiber 树为:,,其中弧线为调用顺序。紫色为 beginWork、粉色为 completeWork。beginWork 是 “递” 的过程,而 comleteWork 则是 “归” 的过程。,generator 和 async/await 也可以做到在函数中间暂停函数执行的逻辑,将执行让出去,能做到将同步变成异步。,但 React 没有选择它们,这是因为:,具体见官方的 github issue 讨论:,https://github.com/facebook/react/issues/7942#issuecomment-254987818。,,做了时间分片,拆分了多个任务,React 就可以以此为基石,给任务设置优先级。,React 实现了一个 Scheduler(调度器)来实现任务调度执行,并单独抽离为一个单独的包,它会在浏览器有空闲的时候执行。其实浏览器也提供了一个 requestIdleCallback 的 API,支持这个能力,但兼容性实在不好,React 还是自己实现了一套。,这个 Scheduler 支持优先级,底层使用了 小顶堆,确保能高效拿到最快要过期的任务,然后执行它。,任务的 优先级 分为几种:,React 自身也有优先级,叫做 Lane,两者是不同的。,React 的架构过于宏大,今天先随便说一点吧。,总的来说,React Fiber 是在 React 16 中引入的新的架构,将原本同步不可中断的更新,变成异步可中断更新,将原本一个耗时的大任务做了时间分片,拆分成一个个小任务,在浏览器空闲的时间执行。此外添加优先级的概念,将一些重要的任务先执行,比如一些用户交互的响应函数。,一切为了更好的用户体验。
© 版权声明
文章版权归作者所有,未经允许请勿转载。