React 源码学习1-React架构
老的架构-React15的架构
React15使用的架构主要分两层:
- Reconciler(协调器)
- Renderer(渲染器)
协调器负责找出组件的变化,也就是我们说的diff,而Renderer负责渲染组件。首先为了方便我们后续更好的理解,先略微讲一下Render工作
JSX
在这之前我们先将一下JSX,React 使用 JSX 来替代常规的 JavaScript。JSX 是一个看起来很像 XML 的 JavaScript 语法扩展。是FaceBook公司为了方便我们编写代码,研究出的一套特殊语法。JSX的优点有以下几个方面:
- JSX 执行更快,因为它在编译为 JavaScript 代码后进行了优化。
- 它是类型安全的,在编译过程中就能发现错误。
- 使用 JSX 编写模板更加简单快速。
我们看以下代码:
代码1:1
2
3
4
5
6
7
8
9
10
11
12
13
14class HelloMessage extends React.Component {
render() {
return (
<div>
Hello {this.props.name}
</div>
);
}
}
ReactDOM.render(
<HelloMessage name="Taylor" />,
document.getElementById('hello-example')
);
代码2:1
2
3
4
5
6
7
8
9
10
11
12class HelloMessage extends React.Component {
render() {
return React.createElement(
"div",
null,
"Hello ",
this.props.name
);
}
}
ReactDOM.render(React.createElement(HelloMessage, { name: "Taylor" }), document.getElementById('hello-example'));
猜测一下这两段代码有什么区别?
其实就是写法上不一样而已,实际的效果是一模一样的。那我们不妨猜测一下JSX的作用?
JSX 会被babel编译成React.createElement方式运行
。
那到这里,我们继续往下猜测,React.createElement
方法是做什么的?
React.createElement 方法
我们知道,Vue 和React 框架都是基于虚拟dom的。那么React.createDom 方法就是创建虚拟dom节点的方法。那如果存在组件嵌套的情况,在第三个参数那里可以调用React.createElement 去创建一个节点。最后得到一整个虚拟dom节点,交给render函数进行渲染。
ReactDom.render
ReactDom.render 这个函数从名字上来看就可以猜出它是干什么的,就是专门用来把虚拟dom节点转成真实dom节点,最后挂载带页面上。也就是我们说的渲染。那我们可以思考一下,这个dom节点如果很大会怎么样?对就是会卡顿。于是需要在render之前做一下diff。
React 是一个跨平台的框架,所以有多套Renderer。每当Renderer接到Reconciler的通知,Renderer会根据平台来渲染。
Reconciler(协调器)
协调器的作用是 负责找出变化的组件。每当有组件需要更新时,协调器需要做如下工作:
- 调用函数组件、或class组件的render方法,将返回的JSX转化为虚拟DOM
- 将虚拟DOM和上次更新时的虚拟DOM对比
- 通过对比找出本次更新中变化的虚拟DOM
- 通知Renderer将变化的虚拟DOM渲染到页面上
老架构的缺点
即使有diff算法来优化性能,但如果dom层级很深的话,由于React使用递归的方式来遍历虚拟dom节点,必然会造成视觉上的卡顿。
递归更新的缺点
由于递归执行,所以更新一旦开始,中途就无法中断。当层级很深时,递归更新时间超过了16ms,用户交互就会卡顿。
新的React架构
React16架构可以分为三层:
- Scheduler(调度器)—— 调度任务的优先级,高优任务优先进入Reconciler
- Reconciler(协调器)—— 负责找出变化的组件
Renderer(渲染器)—— 负责将变化的组件渲染到页面上
那么什么是调度器?
Fiber 的三层含义
- 作为架构来说,之前React15的Reconciler采用递归的方式执行,数据保存在递归调用栈中,所以被称为stack Reconciler。React16的Reconciler基于Fiber节点实现,被称为Fiber Reconciler。
作为静态的数据结构来说,每个Fiber节点对应一个React element,保存了该组件的类型(函数组件/类组件/原生组件…)、对应的DOM节点等信息。
作为动态的工作单元来说,每个Fiber节点保存了本次更新中该组件改变的状态、要执行的工作(需要被删除/被插入页面中/被更新…)。
调度器
我们知道React16最大的更新就是新增了Fiber架构,Fiber架构把一个完整的渲染任务拆成很多个小任务,分成不同的优先级。增量渲染(把渲染任务拆分成块,匀到多帧),十任务渲染更流畅,避免卡顿现象。
调度器处理完任务的优先级,将需要渲染的任务传递给协调器,进行diff。最后传递给renderer进行渲染。