react 生命周期
生命周期经历三个过程
- 装载过程(mount), 也就是把组件第一次在DOM树中渲染的过程
- 更新过程(updata), 当组件被重新渲染的过程
- 卸载过程(Unmount), 组件充DOM中删除的过程
三种不同的过程,React库会一次调用组件的一些成员函数,即生命周期钩子
装载过程中的生命周期钩子
constructor
getinitialState
getDefaultProps
componentWillMount
render
componentDidMount
getinitialState、getDefaultProps
是React.createClass
创建组件时用到的生命周期函数,在高版本的React中已经放弃这种方法,所以这次就不进行展示;
constructor
在面向对象编程中,类(class)是对象(object)的模板,定义了同一组对象(又称"实例")共有的属性和方法。
Javascript语言不支持"类",但是可以用一些变通的方法,模拟出"类"。
constructor
ES6中每个类的构造函数,并不是每个组件都需要定义自己的构造函数,无状态的React组件往往不需要定义构造函数,一个React组件需要构造函数,往往是为了下面的目的
- 初始化state,因为组件生命周期中任何函数都可以访问state,那么整个生命周期中第一个被调用构造函数自然是初始化state最理想的地方
- 绑定成员函数的this
在ES6语法下,类的每个成员函数在执行时this应不是和类实例自动绑定的,而是在构造函数中,this就是当前组件实例,所以为了方便将来的调用,往往在构造函数中将这个实例的特定函数绑定this为当前实例
this.count = this.count.bind(this)
render
render 函数无疑是React
组件中最重要的函数,一个React组件可以忽略其他所有函数都不实现,但是一定要实现render函数,因为React组件的父类React.Component
类对除了render
之外的生命周期都有默认实现
render 函数并不做实际的渲染动作,它返回一个JSX的描述的结构,最容由React来操作渲染过程
某些特殊组件的作用不是渲染洁面,或者,组件在某些情况下没有什么内容取进行更新,那么让render函数返回一个null或者false,等于告诉React,这次组件不需要渲染任何DOM元素
注意: render 函数应是一个纯函数,完全根据this.state
和 this.props
来决定返回的结果,而且不要产生任何副作用。在render函数中取调用this.setState
毫无疑问是错误的,因为一个纯函数不应该引起状态的改变
componentWillMoun和componentDidMount
在装载过程中,componentWillMount
会在调用render
函数前被调用,componentDidMount
则会在调用render
函数后被调用,正好分别来做render前后必备的工作;
componentWillMount
放生在"将要装载"的时候;这个时候没有任何渲染出来的结果,即使调用this.setState
修改状态也不会引发重新绘制,因为一切都晚了,在这个时候可以做的事情都可以放到constructor
中,可以认为这个函数存在的主要目的是和componentDidMount
对称;
componentDidMount
调用在render
函数被调用完之后,componentDidMount
被调用的时候,render
函数返回的东西已经引发了渲染,组件已经被"装载"到DOM树上;值得注意的时,render
函数被调用后,componentDidMount
函数并不是马上被渲染;
代码如下:
import React, { Component } from 'react';
import Counter from './Counter';
// app.js
class App extends React.Component {
render() {
console.log('enter ControlPanel render')
return (
<div className="App" >
<div style={{border: '1px solid', textAlign: 'center'}}>
<Counter caption="First" initValue={0}/>
<Counter caption="Second" initValue={10} />
<Counter caption="Third" initValue={20} />
</div>
</div>
)
}
}
// Counter.js
import React, { Component } from 'react';
class Counter extends Component {
constructor(props) {
super(props);
this.state = {
count: this.props.initValue,
name: this.props.caption
}
}
componentWillMount () {
console.log('render 运行前': this.state.name)
}
componentDidMount () {
console.log('render 运行后': this.state.count)
}
render () {
retrun (
<div>{this.state.name}: {this.state.count}<div>
)
}
}
结果:
可以清除的看到,componentDidMount是当三个组件都调用完成后,才一起被调用;
因为
render
函数本身并不往DOM树上渲染或者装载内容,它返回的是一个JSX表示的对象,然后由React库根据返回对象决定如何渲染,而React库把所有组件返回的结果综合起来,才知道如何产生对应的DOM修改,所以React库调用Counter三个组件的render函数后,才有可能完成状态,这个时候才会依次调用组件的componentDIdMount
函数;
更新过程
componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
render
componentDidUpdate
componentWillReceiveProps
只要父组件的render函数被调用,在render函数里面被渲染的子组件就会经历更新过程,不管父组件传给子组件的props有没有改变,都会触发子组件的componentWillReceiveProps函数。
// 代码 接上部分代码
// app.js
<button onClick={() => this.forceUpdate()}>aaaa</button>
// Counter.js
componentWillReceiveProps (nextProps) {
console.log('enter componentReceiveProps' + this.props.name)
}
shouldComponentUpdate(nextProps, nextState)
除了render函数外,shouldCompoentUpdate可能是整个组件生命周期中最重要的一个函数了;
shouldCompoentUpdate
函数决定了一个组件什么时候不需要进行渲染;
shouldCompoentUpdate
和render
是React生命周期函数中唯二两个要求有返回结果的函数,render返回结果将用于构造DOM对象,而shouldCompoentUpdate函数返回一个布尔值,告诉React库这个组件在本次更新中国呢是否继续;
在React库首先调用shouldCompoentUpdate
函数,如果返回true则仅需更新,调用render,如果返回false则停止更新过程,也不会引发后续渲染。
// 代码接上面
// Counter.js
shouldComponentUpdate(nextProps, nextState) {
return (nextProps.count !== this.props.count) || (nextState.count !== this.state.count)
}
componentWillUpdate () {
console.log('componentWillUpdate')
}
componentDidUpdate () {
console.log('componentDidUpdate')
}
当点击aaaa进行刷新时,不会调用shouldComponentUpdate
,同时也不会进行componentWillUpdate
函数等的调用,当进行加减操作石,则触发;
componentWillUpdate和componentDidUpdate
componentWillUpdate
组件初始化时不调用,只有在组件将要更新时才调用,此时可以修改state
componentDidUpdate
组件初始化时不调用,组件更新完成后调用,此时可以获取dom节点。
卸载过程
componentWillUnmount
防止内存泄漏
用法官方实例