React生命周期解析
事物从创建到销毁的整个过程,称为生命周期。
React组件也有自己的生命周期,了解组件的生命周期可以让我们在最合适的阶段完成自己想要的功能。
需要注意的是,React中,只有类组件才有生命周期,函数组件没有生命周期。
React生命周期和生命周期函数
-
生命周期:从组件创建到销毁的整个过程,主要分成以下几个阶段:
挂载阶段(Mount):组件第一次在DOM树中被渲染的过程 更新阶段(Update):组件状态发生变化,重新渲染DOM树的过程 卸载阶段(Unmount):组件从DOM树中被移除的过程
-
生命周期函数:React内部为了告诉我们当前处于哪个阶段,会对我们组件内部实现的某些函数进行回调,这些回调函数就是生命周期函数,我们可以在这些回调函数中编写自己的逻辑代码,来完成自己的功能需求。
初始化阶段执行的生命周期函数:
constructor()
挂载阶段执行的生命周期函数:
componentWillMount() render() componentDidMount()
更新阶段执行的生命周期函数:
componentWillReceiveProps() shouldComponentUpdate() componentWillUpdate() render() componentDidUpdate()
卸载阶段执行的回调:
componentWillUnmount()
常见的生命周期函数
一、组件初始化阶段
-
constructor()
constructor()在组件初始化阶段回调,主要用于初始化组件状态或为事件绑定实例this
constructor(props){
super(props);
//实现constructor()时,必须实现super(),super()用于调用父类的构造方法,同时,将父组件的props注入给子组件,供子组件读取
//应在其他语句之前前调用 super(props),否则,this.props 在构造函数中可能会出现未定义的 bug
this.state = {
//在这里初始化组件状态,这里也是唯一可以给this.state赋初值的地方
}
}
二、组件挂载阶段
-
componentWillMount()
componentWillMount()在组件初始化数据之后,DOM渲染之前回调,且只会被调用一次。在componentWillMount()中调用this.setstate()修改state不会引起组件的重新渲染。
-
render()
render()函数主要是实现获取虚拟DOM,然后将其渲染为真实DOM树的过程。一般分为以下两步完成:
1.生命周期函数render():class组件中唯一必须实现的方法, 获取要渲染的虚拟DOM树。
在render()中获取要渲染的DOM结构(jsx),经编译后转为纯js由浏览器执行,最终返回一个ReactElement对象,即Virtual DOM树。
jsx —>>> 虚拟DOM:
import React from 'react'
import ReactDOM from 'react-dom'
class APP extends React.Component{
render(){ //渲染函数
return(
<div className="app">
contents
</div>
)
}
}
经babel转译为如下代码:
import React from 'react'
import ReactDOM from 'react-dom'
class APP extends React.Component{
render(){ //渲染函数
return React.createElement( //createElement()接收三个参数
"div", //第一个参数:节点type
{className:"app"}, //第二个参数:节点props
"contents" //第三个参数:节点内容或子节点
)
}
}
打印App,查看其返回内容:
console.log(App/)
可以看到,App组件返回了一个ReactElement实例对象,这个实例对象就是virtual dom树
2.ReactDOM.render():实现从虚拟DOM树到真实DOM树的转换过程,并将其挂载到页面容器上。
ReactDOM.render()接收两个参数:虚拟DOM和挂载的目标DOM
虚拟DOM —>>> 真实DOM:
function render ( vnode, container ){
return container.appendChild( _render( vnode ) );
}
function _render( vnode ){
if ( typeof vnode === 'number' ) {
vnode = String( vnode );
}
//处理文本节点
if( typeof vnode === 'string'){
const textNode = document.createTextNode( vnode )
return textNode;
}
//处理组件
if ( typeof vnode.tag === 'function' ) {
const component = createComponent( vnode.tag, vnode.attrs );
setComponentProps( component, vnode.attrs );
return component.base;
}
//普通的dom
const dom = document.createElement( vnode.tag );
if( vnode.attrs ){
Object.keys( vnode.attrs ).forEach( key => {
const value = vnode.attrs[ key ];
setAttribute( dom, key, value ); // 设置属性
} );
}
vnode.children.forEach( child => render( child, dom ) ); // 递归渲染子节点
return dom ; // 返回虚拟dom为真正的DOM
}
//实现dom挂载到页面某个元素
const ReactDOM = {
render: ( vnode, container ) => {
container.innerHTML = '';
return render( vnode, container );
}
}
-
componentDidMount()
componentDidMount()函数会在组件挂载之后立即调用。在这里通常可以操作DOM、发送网络请求、添加订阅等。
二、更新阶段
-
shouldComponentUpdate()
state发生变化后,组件会重新渲染。该方法的返回值为true时,组件更新渲染,返回false时,会阻止组件的更新
该方法主要用于性能优化:父组件的重新渲染会导致其所有子组件的重新渲染,但我们可能并不需要所有子组件都重新渲染,因此可以在该生命周期函数中添加判断,以阻止不必要的组件渲染
-
componentWillUpdate()
shouldComponentUpdate()返回true以后回调,表示组件即将重新渲染。
-
render()
在更新阶段,组件会检查this.state 和 this.props 的变化,通过diff算法比较更新前后的DOM树,然后,对有差异的最小的节点进行重新渲染
Diff算法解析在这里:react中的虚拟DOM与Diff算法详解
-
componentDidUpdate()
组件更新完成之后的回调
三、卸载阶段
-
componentWillUnMount()
即将卸载组件时回调,这里可以完成数据的销毁,如:
清除组件中所有的setTimeout,setInterval 移除组件中的事件监听removeEventListener
生命周期图解
Mounting阶段解析:
首先,当我们创建一个组件时,会先执行constructor()来初始化组件的状态
然后,会调用render(),获取要渲染的DOM结构,并且开始渲染DOM
最后,当DOM渲染完成后,会执行componentDidMount()函数
Updating阶段解析:
当修改props或调用this.setState修改组件内部状态、或直接调用forceUpdate时,会重新调用render函数,进行更新操作
当更新完成时,回调componentDidUpdate()函数
UnMounting阶段解析:
当组件不再使用时,会被从DOM中卸载,此时,回调componentWillUnMount()函数