什么是Fiber
计算机领域中有两个大家很熟悉也很重要的概念:进程(Process)和线程(Thread),
进程:就是包换上下文切换的程序执行时间总和 = CPU加载上下文+CPU执行+CPU保存上下文,一个进程可以有多个线程。
线程是共享了进程的上下文环境,的更为细小的CPU时间段。线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位
但是在计算机科学中还有一个概念叫做Fiber,英文含义就是“纤维”,意指比Thread更细的线,也就是比线程(Thread)控制得更精密的并发处理机制。
React Fiber
React Fiber 并不是所谓的纤程(微线程、协程),而是一种基于浏览器的单线程调度算法,背后的支持 API 是大名鼎鼎的:requestIdleCallback。
Fiberl是一种将 recocilation (递归 diff),拆分成无数个小任务的算法;它随时能够停止,恢复。停止恢复的时机取决于当前的一帧(16ms)内,还有没有足够的时间允许计算。
解决什么问题
我们都知道,React最引以为豪的技术,可能就是虚拟DOM,通过虚拟DOM,可以将原来十分耗时的DOM操作简化,从而提高页面性能。虚拟DOM的背后,需要的是一套快速对比(diff)出更新的算法。在React 16版本之前,React 通过递归比对新旧DOM树来完成这个过程,并且这个过程是同步进行的。虽然React已经对这个diff算法做了很多优化,但是挡不住组件个数多啊。
所以,当多个组件(100个以上)同步进行更新操作的时候,浏览器将处于一种“假死”状态,在这个状态将无法处理其他事件,所以导致页面卡顿等影响体验的问题。
通过使用React fiber技术,优化了虚拟DOM的diff算法,同时也创建真实DOM和组件渲染拆分为无数的小分片任务,每次执行完一个小分片后就冒泡看看有没有优先级比较高的任务,有的话就去执行优先级比较高的任务,没有就继续执行下一个分片任务。
React Fiber引发的新问题
上面我们说的,React fiber 把创建真实DOM和组件渲染拆分成小分片来轮训执行,如果有新的优先级较高的任务,就先执行高优先级的任务,然后再来执行分片,这样就会引起一个新的问题:一个更新任务还没有完成,就被另一个更高优先级的更新过程打断了怎么办?
这时候,优先级高的更新任务会优先处理完,而低优先级更新任务所做的工作则会完全作废,然后等待机会重头再来。
因为一个更新过程可能被打断,所以React Fiber一个更新过程被分为两个阶段(Phase):第一个阶段Reconciliation Phase和第二阶段Commit Phase。
在第一阶段Reconciliation Phase,React Fiber会找出需要更新哪些DOM,这个阶段是可以被打断的;但是到了第二阶段Commit Phase,那就一鼓作气把DOM更新完,绝不会被打断。
这两个阶段大部分工作都是React Fiber做,和我们相关的也就是生命周期函数。
新的解决方案
上面我们说到,使用React Fiber技术后,生命周期函数可能在一次更新过程中不止一次的被调用。
第一阶段可能会调用下面这些生命周期函数,说是“可能会调用”是因为不同生命周期调用的函数不同。
componentWillMount
componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
下面这些生命周期函数则会在第二阶段调用。
componentDidMount
componentDidUpdate
componentWillUnmount
通过以上分析我们了解到,原来的React 生命周期函数中:
componentWillMount
componentWillReceiveProps
componentWillUpdate
这三个应该为可能不止一次被调用,之前可能会有部分比较耗时的业务代码(操作真是DOM)写在这些函数里,当不止一次调用这些函数的时候,可能会一起很大的新能问题。
因此,React 16+ 引入了一个新的静态生命周期函数 getDerivedStateFromProps 来将属性从props中添加(同步)到state中,这是一个“静态函数”,它不属于任务一个实例,内部的 this并不指向组件本身,其实就是强迫把这个函数变成一个纯函数
getSnapshotBeforeUpdate这个函数在 commit阶段执行,我们知道到commit阶段后,这个函数只会执行一次,所以可以在这里完成那些必须的操作真实DOM的操作。