React 源码结构设计
一、reactv16之前的架构
reactv15的架构可分为两部分:
- reconciler(协调器) - 负责找出变化组件
- renderer(渲染器) - 负责将变化的组件渲染到页面
在React中可以通过this.setState、this.forceUpdate、ReactDOM.render等API触发更新。
reconciler
每当有更新发生时,reconciler会如下工作:
- 调用函数组件、或者render方法,将返回的jsx转化成v-dom
- 将v-dom和上次更新时到v-dom对比
- 通过对比找出本次更新中变化到v-dom
- 通知renderer将变化v-dom渲染到页面
renderer
不同到平台有不同到渲染器,react-dom、react-native、react-art和react-test等。
缺点
reconciler在,mount组件会调用mountComponent,update的组件会调用updateComponent。者两个方法都会递归更新子组件。
由于递归执行,所以一旦开始,中途就无法中断,当层级很深时,递归更新超过16ms,用户交互就会卡顿。
二、reactv16的架构
react16的架构可分为三层:
- scheduler(调度器)- 调度任务的优先级,高优任务先进入reconciler
- reconciler(协调器)- 负责找出变化的组件
- renderer(渲染器)- 负责将变化的组件渲染到页面上
scheduler
由于部分浏览器requestIdleCallback这个api兼容性和触发频率不稳定到问题,react放弃浏览器的该api。
基于这个原因react实现了功能更完备的requestIdleCallback polyfill,也就是scheduler。除了在空闲时触发回调的功能外,Scheduler还提供了多种调度优先级供任务设置。
reconclier
react16的reconciler的更新工作从递归变成了可以中断的循环过程。具体表现在每次循环都会调用shouldYield判断当前是否有剩余时间。
如:
1 | /** @noinline */ |
那么React16是如何解决中断更新时DOM渲染不完全的问题呢?
在React16中,Reconciler与Renderer不再是交替工作。当Scheduler将任务交给Reconciler后,Reconciler会为变化的虚拟DOM打上代表增/删/更新的标记,
类似这样:
1 | export const Placement = /* */ 0b000000000000000010; |
整个scheduler与reconciler的工作都在内存中进行,只有当所有的组件都完成reconciler的工作,才会统一交给renderer。
renderer
renderer根据reconciler为v-dom打的标记,同步执行对应组件更新。