#渲染原理

#渲染原理

渲染:生成用于显示的对象,以及将这些对象形成真实的DOM对象

- React元素:React Element,通过React.createElement创建(语法糖:JSX)

  - 例如:

  - ```

标题

```

    - ``````

- React节点:专门用于渲染UI界面的对象,React会通过React元素,创建React节点

ReactDOM一定是通过React节点来进行渲染的

- 节点类型:

  - React DOM节点:创建该节点的React元素类型是一个字符串

  - React 组件节点:创建该节点的React元素是一个函数或者是一个类

  - React TextNode(文本)节点: 有字符串、数字创建的

  - React 空节点:由null、undefined、false、true

  - React 数组节点:该节点由一个数组创建

-真实DOM:通过document.createElement创建的元素

##首次渲染(新节渲染)

1.通过参数的值创建节点

2.根据不同的节点,做不同的事情

1.文本节点:通过document.createTextNode创建真实的文本节点

2.空节点:什么都不做

3.数组节点:遍历数组,将数组每一项递归创建节点(回到第1步进行反复操作,知道遍历结束)

4.DOM节点:通过document.createElement创建真实的DON对象,然后立即设置真实DOM元素的各种属性,然后遍历对应React元素的children属性,递归操作(回到第1步进行反复操作,知道遍历结束)

5.组件即节点

1.函数组件:调用函数(该函数必须返回一个可以生成节点的内容),将该函数的返回结果递归生成节点(回到第1步进行反复操作,知道遍历结束)

2.类组件:

1.创建类的实例

2.立即调用对象的生命周期方法:static getDerivedStateFromProps

3.运行对象的render方法,拿到节点对象(将该节点递归操作,回到第1步进行反复操作)

4.将该组件的componentDidMount加入到执行队列(先进先出,先进先执行),档整个虚拟DOM树全部构建完毕,并且将真实的DOM对象加入到容器中后,执行该队列

3.生成出虚拟dom树之后,将该树保存起来,一边后续使用,

4.将之前生成的真实DOM对象,加入容器中

虚拟DOM树

## 更新节点

更新的场景:

1. 重新调用ReactDom.render,触发根节点更新

2. 在类组件的实例对象中调用setState,会导致该实例所在的节点更新

**节点的更新**

- 如果调用的是ReactDOM.render,进入根节点的**对比(diff)更新**

- 如果你调用的setState

  - 1.运行生命周期函数,static getDerivedStateFromProps

  - 2.运行shouldComponentUpdate,如果该函数返回false,终止当前流程

  - 3.运行render,得到一个新的节点,进入改新的节点的**对比更新**

    - 4.将生命周期函数getSnapshotBeforeUpdate加入执行队列,以待将来执行

  - 5.将生命周期函数componentDidUpdate加入执行队列,以待将来执行

后续步骤:

1.更新虚拟DOM树

2.完成真实的DOM更新

3.依次调用执行队列中的componentDidMount

4.依次调用执行队列中的getSnapshotBeforeUpdate

5.依次调用执行队列中的componentDidUpdate

6.依次调用执行队列中的componentWillMount

**对比更新**

将新产生的节点,对比之前虚拟DOM的节点,发现差异,完成更新

问题:对比之前DOM树中哪个节点

React为了提高对比效率,做出一下假设

1.假设节点不会出现层次的移动(对比时,直接找到旧树中对应位置的节点进行对比)

2.不同的节点类型会生成不同的结构

1. 相同的节点类型:节点本身结构相同,如果是由React元素生成,type值必须一致

2. 其他的,都属于不相同的节点类型

3.多个兄弟通过唯一标识(key)来确定对比的新节点

key值的作用:用于通过旧节点,寻找对应的新节点,如果某个旧节点有key值,则其更新时,会寻找相同层级中的相同key值的节点,进行对比。

#### 找到了对比的目标

判断节点类型是否一致

- **一致**

根据不同的节点类型,做不同的事情

**空节点**:不做任何事情

**DOM节点**:

1.直接重用之前的真实DOM对象

2.将其属性的变化记录下来,以待将来统一完成更新(现在不会真正的变化)

3.遍历该新的React元素的子元素,**递归对比更新**

**文本节点**:

1.直接重用之前的真实DOM对象

2.将新的文本变化记录下来,将来统一完成更新

**组件节点**:重新调用函数,得到一个节点对象,进入**递归对比更新**

**函数组件**:

1.重用之前的实例

2.调用生命周期方法getDerivedStateFromProps

3.调用生命周期方法shouldComponentUpdate,若该方法返回false,终止

4.运行render,得到新的节点对象,进入**递归对比更新**

5.将该对象的getSnapshotBeforeUpdate加入队列

6.将该对象的componentDidUpdate加入队列

- **不一致**

整体上,卸载旧的节点,全新创建新的节点

**创建新节点**

进入新节点的挂载流程

**卸载旧节点**

1.**文本节点、DOM节点、数组节点、空节点、函数数组节点**:直接放弃该节点,如果节点有子节点,递归卸载节点

2.**类数组节点**:

1.直接放弃该节点

2.调用该节点的componentWillUnMount函数

3.递归卸载子节点

### 没有找到对比的目标

新的DOM树中有节点被添加

新的DOM树中有节点被删除

- 创建新加入的节点

- 卸载多余的旧节点

你可能感兴趣的:(#渲染原理)