一、渲染过程
渲染:生成用于显示的对象,以及将这些对象形成真实的DOM对象。
React元素:React Element,通过React.createElement创建(语法糖:JSX)
例如:
javascript"> <div>测试数据</div>
React节点:专门用于渲染到UI界面的对象,React会通过React元素,创建React节点,注意ReactDOM一定是通过React节点来进行渲染的(而不是React元素)。
节点类型有哪些?
- React DOM节点(不是真实的dom):创建该节点的React元素类型是一个字符串
javascript">import React from 'react';
import ReactDOM from 'react-dom';
const app = <div><h1>标题</h1></div>
ReactDOM.render(app, document.getElementById('root'));
- React 组件节点:创建该节点的React元素类型是一个函数或是一个类
javascript">import React from 'react';
import ReactDOM from 'react-dom';
import App from "./App"
ReactDOM.render(<App />, document.getElementById('root'));
- React 文本节点:由字符串、数字创建的
javascript">import React from 'react';
import ReactDOM from 'react-dom';
const app = 'Hello World'
ReactDOM.render(app, document.getElementById('root'));
- React 空节点:由null、undefined、false、true
javascript">import React from 'react';
import ReactDOM from 'react-dom';
const app = null
ReactDOM.render(app, document.getElementById('root'));
- React 数组节点:该节点由一个数组创建
javascript">import React from 'react';
import ReactDOM from 'react-dom';
const app = [1,2,3,4]
ReactDOM.render(app, document.getElementById('root'));
注意:app 这里不能直接写一个对象,普通对象是无法渲染的。
再补充下什么是真实dom?
真实DOM:通过document.createElement创建的dom元素
二、首次渲染(新节点渲染)
这里总结的是第一次渲染时:
A. 通过参数的值创建节点(参数是以上的几种类型,注意一定不能是对象)。
B. 根据不同的节点,做不同的事情。
-
文本节点:通过document.createTextNode创建真实的文本节点;
-
空节点:什么都不做
-
数组节点:遍历数组,将数组每一项递归创建节点(回到第A步进行反复操作,直到遍历结束)
-
DOM节点:通过document.createElement创建真实的DOM对象,然后立即设置该真实DOM元素的各种属性,然后遍历对应React元素的children属性,递归操作(回到第A步进行反复操作,直到遍历结束)
-
组件节点:
1、函数组件
调用函数(该函数必须返回一个可以生成节点的内容,若返回一个对象就不行,以为对象不能生成节点),将该函数的返回结果递归生成节点(回到第A步进行反复操作,直到遍历结束)
2、类组件
1.创建该类的实例
2.立即调用对象的生命周期方法:static getDerivedStateFromProps
3.运行该对象的render方法,拿到节点对象(将该节点递归操作,回到第A步进行反复操作)
4.将该组件的componentDidMount加入到执行队列(先进先出,先进先执行),当整个虚拟DOM树全部构建完毕,并且将真实的DOM对象加入到容器中后,执行该队列(先进先执行,子组件的先执行,父组件的后执行)
注:这个过程是很靠后的,等D执行完后才执行的,
C. 生成出虚拟DOM树之后,将该树(对象)保存起来,以便后续使用
D. 将之前生成的真实的DOM对象(下图中的黄色的部分),加入到容器中。
例子如下:
例1:DOM节点
javascript">import React from 'react';
import ReactDOM from 'react-dom';
const app = <div className="assaf">
<h1>
标题
{["abc", null, <p>段落</p>]}
</h1>
<p>
{undefined}
</p>
</div>;
ReactDOM.render(app, document.getElementById('root'));
形成的这个图就是虚拟dom树,我们发现有些节点是不会生成真实dom的。
例2:函数组件:
javascript">App.js
import React from 'react'
function Comp1(props) {
return <h1>Comp1 {props.n}</h1>
}
export default function App(props) {
return (
<div>
<Comp1 n={5} />
</div>
)
}
javascript">import React from 'react';
import ReactDOM from 'react-dom';
import App from "./App"
const app = <App />;
ReactDOM.render(app, document.getElementById('root'));
遵循上述过程,先调用组件App,返回DOM节点div,div内部又是个组件节点,又调用Comp1函数…最终生成虚拟DOM树
javascript">import React from 'react'
class Comp1 extends React.Component {
state = {}
constructor(props){
super(props);
console.log(4,"Comp1 constructor")
}
static getDerivedStateFromProps(props,state){
console.log(5,"Comp1 getDerivedStateFromProps")
return null
}
componentDidMount(){
console.log('b componentDidMount')
}
render() {
console.log(6,"Comp1 render")
return (
<h1>Comp1</h1>
)
}
}
export default class App extends React.Component {
state = {}
constructor(props){
super(props);
console.log(1,"App constructor")
}
static getDerivedStateFromProps(props,state){
console.log(2,"App getDerivedStateFromProps")
return null
}
componentDidMount(){
console.log('a componentDidMount')
}
render() {
console.log(3,"App render")
return (
<div>
<Comp1 />
</div>
)
}
}
通过运行我们发现了输出的结果,其实类组件的渲染过程和函数组件是类似的。由于是等Comp1组件挂载完成,App组件才算挂载完成,所以b比a先输出。