什么是生命周期函数
- 组件中在某个阶段会自动执行的函数。
- 比如我们执行使用render函数,在prop或者state变化时,render函数自动执行。
- 因此render函数就是一个生命周期函数。
- constructor在组件创建的时候也会自动调用。但是他不是react独有,是es6中的函数所以,我们不将他列为生命周期函数。
生命周期分为4个阶段
- initialization(组件初始化)
- 我们在创建组件的时候需要继承react Component这个基类,也就继承这个react的基类,才能有render(),生命周期等方法可以使用,这也说明为什么函数组件不能使用这些方法的原因。同时也让组件能使用setState方法。
- 然后我们在 constructor 构造函数中使用 super(props)来将父组件传递的props注入给这个组件,以及使用this.state初始化这个组件的属性。
- 这一系列动作就是组件的初始化。
- mount (组件的挂载)
- 此阶段分为三个时期
-
componentWillMount(挂载之前)
- 这个函数在组件挂载到DOM之前的时候执行,所以你在这里引用setState方法,是不会引起组件的重新渲染。
- 同样这里做的事情如果放在constructor构造函数中去使用也是可以的。
- 这个函数只会被调用一次,就是组件挂载到DOM之前的时候。其他时候是不会触发这个函数的。
-
render(挂载中)
- 根据组件的props和state是否变化,变化即执行,这里的变化要注意,并不是值变化。只要重新赋值,新旧值相同也会执行。
- 然后return 一个React元素(描述组件,即UI),不负责组件实际渲染工作,之后由React自身根据此元素去渲染出页面DOM。render是纯函数(Pure function:函数的返回结果只依赖于它的参数;函数执行过程里面没有副作用。
- 不能在里面执行this.setState,会有改变组件状态的副作用。
-
componentDidMount(挂载完成)
- 约定将ajax请求放在这个生命周期函数中
- 组件挂载到DOM后被执行,只会执行一次。
-
- 此阶段分为三个时期
- update (组件更新时)
首先我们来了解一下什么时组件更新。只有在通过setState函数使sate和props变化或重新赋值时才叫组件更新。
setState引起父组件的render函数执行,同时也会引起它的子组件的render函数执行。
原因是react虚拟DOM的diff算法,同级比较原理。
只要重新赋值就是组件更新,如果值并没有变,也更新组件,这样就会耗性能,也是我们讲同级比较的时候说的一个弊端。
-
接下来我们了解一下有那些周期函数
-
componentWillReceiveProps(nextProps)
- 此方法只调用于props引起的组件更新过程中,参数nextProps是父组件传给当前组件的新props。但父组件render方法的调用不能保证重传给当前组件的props是有变化的,所以在此方法中根据nextProps和this.props来查明重传的props是否改变,以及如果改变了要执行啥,比如根据新的props调用this.setState出发当前组件的重新render
-
shouldComponentUpdate(nextProps, nextState)
- 此方法通过比较nextProps,nextState及当前组件的this.props,this.state,返回true时当前组件将继续执行更新过程,返回false则当前组件更新停止,以此可用来减少组件的不必要渲染,优化组件性能。ps:这边也可以看出,就算componentWillReceiveProps()中执行了this.setState,更新了state,但在render前(如shouldComponentUpdate,componentWillUpdate),this.state依然指向更新前的state,不然nextState及当前组件的this.state的对比就一直是true了。
-
componentWillUpdate(nextProps, nextState)
- 此方法在调用render方法前执行,在这边可执行一些组件更新发生前的工作,一般较少用。
-
render
- render方法在上文讲过,这边只是重新调用。
-
componentDidUpdate(prevProps, prevState)
- 此方法在组件更新后被调用,可以操作组件更新的DOM,prevProps和prevState这两个参数指的是组件更新前的props和state
-
- 优化弊端
- 1.当因为父组件重新render,使得props重新被赋值,导致子组件跟着渲染.
/**
* 方法一:解决上述弊端,可以在shouldComponentUpdate函数,组件更新前进行判断,props是否改变,再确定是否执行重新渲染。
*/
class Child extends Component {
shouldComponentUpdate(nextProps){
if(nextProps.value === this.props.value){
return false
}
return true
}
render() {
return <div>{this.props.value}</div>
}
}
/**
* 方法二:
* 1.解决上述弊端,也可以先将this.props.value赋值给子组件的state
* 2.再在componentWillReceiveProps函数中,使用setState去重新给stae中的属性赋值this.props.value
* 3.文档中提到,在该函数(componentWillReceiveProps)中调用 this.setState() 将不会引起第二次渲染。
* 4.因为componentWillReceiveProps是在props有变化的时候才会触发,所以在这里面做this.setState()一定是有改变state
*/
//
class Child extends Component {
constructor(props) {
super(props);
this.state = {
value: props.value // 先将this.props.value赋值给子组件的state
};
}
componentWillReceiveProps(nextProps) { // 在props有变化的时候才会触发这个方法
this.setState({value: nextProps.value}); // 重新赋值,引起render
}
render() {
return <div>{this.state.value}</div>
}
}
- 2.组件本身调用setState方法,但是并没有改变state中的值。
/**
* 也是通过shouldComponentUpdate判断新旧值是否改变,改变才做render
*/
class Test extends Component {
constructor(props) {
super(props);
this.state = {
value:1
}
}
shouldComponentUpdate(nextState){ // 应该使用这个方法判断新旧值是否改变
if(nextState.value === this.state.value){
return false // 没有改变返回false
}
return true
}
changeState = () => { // 虽然调用了setState ,但state并无变化
const value = this.state.value
this.setState({
value
})
}
render() {
return <div onClick = {this.changeState}>{this.state.value}</div>
}
}
- 卸载阶段(componentWillUnmount)
- 只有一个生命周期方法componentWillUnmount
- 此方法在组件被卸载前调用,可以在这里执行一些清理工作,比如清楚组件中使用的定时器,清楚componentDidMount中手动创建的DOM元素等,以避免引起内存泄漏。
- 在react组件中,除了render函数,其他任何生命周期函数都可以不写,因为组件继承了react中的 Component,Component内置了其他的生命周期函数