React16之前在做reconciler时,都是同步的,假设我们有一个如下的React tree。
假设节点1先执行setState(), 然后节点2执行setState(), 如果节点2对一个的是一个简单的Input, 其onChange=((e) => { this.setState({ value: e.target.value()})}),
由于1先setState,React会做1子树的reconciler,这个过程是同步的,如果1的子树特别大,这个过程可能会耗时很久,执行时间有可能超过16ms, 这时候节点2的setState迟迟不会执行,将导致用户输入无响应。
fiber就是为了解决这个问题,他将每个reconciler拆成很多个子任务,可以这么简单的理解,每个React Element都对应一个子任务,每个子任务的内容包含如下内容
1. 执行getStateDerivedFromProps
2. 执行shouldComponentUpdate
3. 执行render
4. 跟上一个版本进行compare, 生成对应的effects
每个react Element 对应的子任务可以称为unitwork, 当执行完一个节点的unitwork之后,如果还有空余时间,会继续执行下一个unitwork, 如果没有空余时间了,可以暂停,等待下一个周期的时候在接着执行下一个unitwork。
1对应的setState对应reconciler执行到一半(在requestIdleCallbalck里做),下个周期用户可能会下拉滚动条等操作,这时候浏览器先做UI layout,将滚动条滑倒对应位置,然后在这个周期的requestIdleCallbalck做剩余的reconciler
再看另一种情形,1对应的setState对应reconciler执行到一半时, 2的setState开始执行,由于其优先级比较高,1的setState会先暂停,先执行2的setState,当2的setState执行完时, 再回到1的setState, 此时估计1之前做的一半会都丢弃掉,重新在做一遍。所以getStateDerivedFromProps,shouldComponentUpdate,render生命周期可能在一次reconciler里会执行多次,这些生命周期不能包含任何side effects操作。
参考文献:
1. https://medium.com/dailyjs/the-how-and-why-on-reacts-usage-of-linked-list-in-fiber-67f1014d0eb7
2. https://medium.com/react-in-depth/inside-fiber-in-depth-overview-of-the-new-reconciliation-algorithm-in-react-e1c04700ef6e
2. https://github.com/acdlite/react-fiber-architecture
4. https://juejin.im/post/5ab7b3a2f265da2378403e57
,