所谓的生命周期就是指某个事物从开始到结束的各个阶段,就好像是把人的出生到死亡分成一个个阶段,你肯定是在出生阶段起名字,而不会在成年或者死亡的阶段去起名字。当然在 React.js 中指的是组件从创建到销毁的过程,React 实例从开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、卸载等一系列过程,我们称这是 React 的生命周期,各个阶段有相对应的事件钩子,用户可以在特定的阶段调用特定的方法。每个阶段组件内部的属性都是不一样的,所以一般特定的钩子做特定的事。
【1】过去
【2】现在
这么多生命周期钩子,实际上总结起来只有三个过程:挂载、更新、卸载,挂载和卸载只会执行一次,更新会执行多次。
一个完整的React组件生命周期会依次调用如下钩子
【1】生命周期演变
React版本 | 挂载阶段 | 更新阶段 | 卸载阶段 |
之前(React 16.3 之前) |
|
|
componentWillUnmount |
现在 |
|
|
componentWillUnmount |
【2】废弃和新增
原来(React v16.0前)的生命周期在React v16推出的Fiber之后就不合适了,因为如果要开启async rendering,在render函数之前的所有函数,都有可能被执行多次。所以除了shouldComponentUpdate,其他在render函数之前的所有函数(componentWillMount,componentWillReceiveProps,componentWillUpdate)都被getDerivedStateFromProps替代。
以下生命周期钩子将被逐渐废弃,看出特点了么,都是带有will的钩子
- componentWillMount
- componentWillReceiveProps
- componentWillUpdate
引入了以下两个生命周期钩子
- getDerivedStateFromProps
- getSnapshotBeforeUpdate
【3】注意
getDerivedStateFromProps里面的this为undefined
【1】constructor(props):初始化钩子函数
constructor()中完成了React数据的初始化,它接受两个参数:props和context,当想在函数内部使用这两个参数时,需使用super()传入这两个参数。如果没有初始化状态(state),并且没有绑定方法,通常不需要为 React 组件实现一个构造函数。
注意:
- 只要使用了constructor()就必须写super(),否则会导致this指向错误。
- 不需要在构造函数中调用 setState(),只需将初始状态设置给 this.state 即可 。
React 构造函数通常只用于两个目的:
- 通过分配一个对象到this.state来初始化本地state
- 将事件处理程序方法绑定到实例
constructor(props) {
super(props);
// 不要这样做
this.state = { color: props.color };
}
【2】componentWillMount():组件将要挂载时触发的函数
这是组件挂载到DOM之前的生命周期钩子。componentWillMount()一般用的比较少,它更多的是在服务端渲染时使用。它代表的过程是组件已经经历了constructor()初始化数据后,但是还未渲染DOM时。
【3】render():组件挂载时触发的函数
render函数是类组件中唯一必须的方法,其余生命周期不是必须要写。 组件渲染时会走到该生命周期,展示的组件都是由 render() 生命周期的返回值来决定。render函数会插入jsx生成的dom结构,react会生成一份虚拟dom树,在每一次组件更新时,在此react会通过其diff算法比较更新前后的新旧DOM树,比较以后,找到最小的有差异的DOM节点,并重新渲染。
【4】componentDidMount():组件挂载完成时触发的函数
这是组件挂载到DOM之后的生命周期钩子。组件第一次渲染完成,此时dom节点已经生成,可以在这里调用ajax请求,返回数据setState后组件会重新渲染
【5】componentWillUnmount ():组件将要销毁时触发的函数
这是组件卸载之前的生命周期钩子,在此处完成组件的卸载和数据的销毁。
【6】componentWillReceiveProps (nextProps):父组件中改变了props传值时触发的函数
componentWillReceiveProps (nextProps) {
nextProps.isOpen !== this.props.isOpen && this.setState({
isOpen:nextProps.isOpen
}, () => {
//将state更新为nextProps,在setState的第二个参数(回调)可以打印出新的state
})
}
【7】shouldComponentUpdate(nextProps,nextState):是否要更新组件时触发的函数
【8】componentWillUpdate (nextProps,nextState):将要更新组件时触发的函数
shouldComponentUpdate生命周期钩子返回true以后,组件进入重新渲染的流程,进入componentWillUpdate,这里同样可以拿到nextProps和nextState。
【9】componentDidUpdate(prevProps,prevState):组件更新完成时触发的函数
这是组件更新之后触发的生命周期钩子,组件更新完毕后,react只会在第一次初始化成功会进入componentDidmount,之后每次重新渲染后都会进入这个生命周期,这里可以拿到prevProps和prevState,即更新前的props和state。
【10】static getDerivedStateFromProps(nextProps,prevState):静态方法生命周期钩子
getDerivedStateFromProps 会在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。它应返回一个对象来更新 state,如果返回 null 则不更新任何内容。为什么getDerivedStateFromProps生命周期钩子要设计成静态方法呢?因为这样开发者就访问不到this也就是实例了,也就不能在里面调用实例方法或者setsState了,用一个静态函数getDerivedStateFromProps来取代被废弃的其他几个生命周期函数,就是强制开发者在render之前只做无副作用的操作,而且能做的操作局限在根据props和state决定新的state 而已。
【11】 getSnapshotBeforeUpdate(prevProps,prevState):保存状态快照
它是用来代替componentWillUpdate生命周期钩子函数的,常见的 componentWillUpdate 的用例是在组件更新前,读取当前某个 DOM 元素的状态,并在 componentDidUpdate 中进行相应的处理。
这两者的区别在于:
- 在 React 开启异步渲染模式后,在 render 阶段读取到的 DOM 元素状态并不总是和 commit 阶段相同,这就导致在
componentDidUpdate 中使用 componentWillUpdate 中读取到的 DOM 元素状态是不安全的,因为这时的值很有可能已经失效了。- getSnapshotBeforeUpdate 会在最终的 render 之前被调用,也就是说在 getSnapshotBeforeUpdate 中读取到的 DOM 元素状态是可以保证与 componentDidUpdate 中一致的。此生命周期返回的任何值都将作为参数传递给componentDidUpdate()。
【1】挂载阶段
创建一个子组件,代码如下,在父组件引入渲染即可
import React ,{Component} from 'react'
class Child extends Component{
// 初始化
constructor(props){
console.log('01构造函数')
super(props)
this.state={}
}
// 组件将要挂载时候触发的生命周期函数
componentWillMount(){
console.log('02组件将要挂载')
}
// 组件挂载完成时候触发的生命周期函数
componentDidMount(){
console.log('04组件挂载完成')
}
// 组件挂载时触发的生命周期函数
render(){
console.log('03数据渲染render')
return(
Child组件
)
}
}
export default Child
控制台打印结果顺序如下:
01构造函数
02组件将要挂载
03数据渲染render
04组件挂载完成
【2】更新阶段
数据更新的话第一步是shouldComponentUpdate确认是否要更新数据,当这个函数返回的是true的时候才会进行更新,并且这个函数可以声明两个参数nextProps和nextState,nextProps是父组件传给子组件的值,nextState是数据更新之后值,这两个值可以在这个函数中获取到。第二步当确认更新数据之后componentWillUpdate将要更新数据,第三步依旧是render,数据发生改变render重新进行了渲染。第四步是componentDidUpdate数据更新完成。
import React, { Component } from 'react'
class Child extends Component {
constructor(props) {
super(props)
this.state = {
msg: '我是一个msg数据'
}
}
//是否要更新数据,如果返回true才会更新数据
shouldComponentUpdate(nextProps, nextState) {
console.log('01是否要更新数据')
return true; //返回true,确认更新
}
//将要更新数据的时候触发的生命周期函数
componentWillUpdate() {
console.log('02组件将要更新')
}
//更新完成时触发的生命周期函数
componentDidUpdate() {
console.log('04组件更新完成')
}
//更新数据方法
setMsg() {
this.setState({
msg: '我是改变后的msg数据'
})
}
render() {
console.log('03数据渲染render')
return (
{this.state.msg}
)
}
}
export default Child
点击按钮更新数据,控制台打印结果顺序如下:
01是否要更新数据
02组件将要挂载
03数据渲染render
04组件更新完成
React官网: https://zh-hans.reactjs.org/
文章每周持续更新,可以微信搜索「 前端大集锦 」第一时间阅读,回复【视频】【书籍】领取200G视频资料和30本PDF书籍资料