其实React组件并不是真正的DOM, 而是会生成JS对象的虚拟DOM, 虚拟DOM会经历创建,更新,删除的过程
这一个完整的过程就构成了组件的生命周期,React提供了钩子函数让我们可以在组件生命周期的不同阶段添加操作
1. 生命周期函数的理解
1.1 组件生命周期函数说明
- 组件生命周期函数主要分三类,组件的初始挂载,组件的更新渲染以及组件的卸载
- 有一些生命周期函数还在三个阶段都会执行的
1.2 组件的生命周期图
2. 生命周期函数
2.1 组件初始挂载
2.1.1 组件初始挂载生命周期函数认识
当组件实例被创建并插入 DOM 中时,其生命周期调用顺序如下:
-
constructor
: 完成React数据的初始化 -
conponentWillMount/UNSAFE_componentWillMonut
: 页面渲染前调用(未来将废弃) -
render
: class组件中唯一必须实现的方法,返回React元素渲染页面 -
componentDidMount
页面初始化渲染完毕以后执行
2.1.2 constructor函数
说明:
- 如果不需要初始化state,或者不进行方法的绑定,则不需要实现constructor构造函数
- 在组件挂载前调用构造函数,如果继承
React.Component
,则必须调用super(props)
- constructor通常用于处理了state初始化和为事件处理函数绑定
this
实例 - 尽量避免将props外部数据赋值给组件内部state状态
注意: constructor 构造函数只在初始化化的时候执行一次
示例代码如下:
class MyCom extends React.Component{
// 1. constructor 构造函数
constructor(props){
super(props)
console.log("组件初始化数据");
// 初始化组件状态
this.state = {
count: 0
}
// 处理事件函数绑定组件实例
this.handleClick = this.handleClick.bind(this)
}
handleClick(){
this.setState(() => ({
count: ++ this.state.count
}))
}
render(){
return (
<div>
<p>点击次数{ this.state.count }</p>
<button onClick={ this.handleClick }>点击</button>
</div>
)
}
}
// 将组件渲染到页面上
ReactDOM.render(<MyCom/> ,document.getElementById("root"))
之前有讲过组件state也可以不定义在constructor构造函数中,事件函数也可以通过箭头函数处理this问题
因此如果不想使用constructor 也可以将两者移出
示例代码如下
class MyCom extends React.Component{
// 初始化组件状态
state = {
count: 0
}
// 箭头函数处理this问题
handleClick= ()=>{
this.setState(() => ({
count: ++ this.state.count
}))
}
//...
}
结果一样正常显示
2.1.3 componentWillMount函数
说明:
- componentWillMount生命周期函数在组件渲染前执行,
- 使用时函数能正常运行, 但是控制台会打印警告,推荐使用
UNSAFE_componentWillMount
- 但是react官网表示
UNSAFE_componentWillMount
即将过时,不建议使用 - 这个生命周期函数也只会执行一次
这个生命周期函数一般使用较少
示例代码:
class MyCom extends React.Component{
// 1. 初始化构造函数
constructor(props){
super(props)
console.log("组件初始化数据");
// 初始化组件状态
this.state = {
count: 0
}
// 处理事件函数绑定组件实例
this.handleClick = this.handleClick.bind(this)
}
// 2.组件渲染前执行
UNSAFE_componentWillMount(){
console.log("组件内容渲染前执行 conponentWillMount");
}
handleClick(){
this.setState(() => ({
count: ++this.state.count
}))
}
render(){
return (
<div>
<p>点击次数{ this.state.count }</p>
<button onClick={ this.handleClick }>点击</button>
</div>
)
}
}
// 将组件渲染到页面上
ReactDOM.render(<MyCom/> ,document.getElementById("root"))
2.1.4 render渲染函数
说明:
- class组件中唯一必须实现的方法,其他生命周期函数没有需求可以不写,这个必须实现
- render函数返回一个通过JSX语法创建的React元素
- render函数应为纯函数,意味着在不修改组件状态和父组件传递的props时,每次返回相同的结果
- render函数初始只会渲染一次,但是每次状态的改变都会重新执行
示例代码:
class MyCom extends React.Component{
// 1. 初始化构造函数
constructor(props){
super(props)
console.log("组件初始化数据");
// 初始化组件状态
this.state = {
count: 0
}
// 处理事件函数绑定组件实例
this.handleClick = this.handleClick.bind(this)
}
// 2.组件渲染前执行
UNSAFE_componentWillMount(){
console.log("组件内容渲染前执行 conponentWillMount");
}
// 3. render渲染函数
render(){
console.log("render 渲染函数")
return (
<div>
<p>点击次数{ this.state.count }</p>
<button onClick={ this.handleClick }>点击</button>
</div>
)
}
// 自定义事件函数
handleClick(){
this.setState(() => ({
count: ++this.state.count
}))
}
}
// 将组件渲染到页面上
ReactDOM.render(<MyCom/> ,document.getElementById("root"))
2.1.5 componentDidMount函数
说明:
- 组件渲染完毕以后执行, 在render渲染函数之后
- componentDidMount生命周期函数只会执行一次
- 可以在这个函数中发送ajax请求
示例代码:
class MyCom extends React.Component{
// 1. 初始化构造函数
constructor(props){
super(props)
console.log("组件初始化数据");
// 初始化组件状态
this.state = {
count: 0
}
// 处理事件函数绑定组件实例
this.handleClick = this.handleClick.bind(this)
}
// 2.组件渲染前执行
UNSAFE_componentWillMount(){
console.log("组件内容渲染前执行 conponentWillMount");
}
// 3. render渲染函数
render(){
console.log("render 渲染函数")
return (
<div>
<p>点击次数{ this.state.count }</p>
<button onClick={ this.handleClick }>点击</button>
</div>
)
}
// 4.组件渲染完毕后执行
componentDidMount(){
console.log("组件内容渲染完毕,componentDidMount");
}
// 自定义事件函数
handleClick(){
this.setState(() => ({
count: ++this.state.count
}))
}
}
// 将组件渲染到页面上
ReactDOM.render(<MyCom/> ,document.getElementById("root"))
2.2 组件渲染更新
组件的更新会出现两种状况, 父组件传递的props更新以及当前组件更新自己的state
2.2.1 组件渲染更新生命周期函数认识
-
componentWillReceiveProps
组件接受的Props将要更新 -
shouldComponentUpdate
是否允许组件渲染更新,函数返回true表示通过, false表示不通过,渲染不更新 -
componentWillUpdate/UNSAFE_componentWillUpdate
组件内容更新渲染前执行 -
render
组件内容更新渲染 -
componentDidUpdate
组件内容更新渲染之后执行
2.2.2 componentWillReceiveProps
函数
说明:
- 在父组件传递的props更新时执行的生命周期函数
- 函数接受一个参数nextProps,为更新后的props数据
- 使用时控制台会打印警告, 建议使用
UNSAFE_componentWillReceiveProps
- 但是官网表示
UNSAFE_componentWillReceiveProps
也将会被废弃
示例代码如下:
// 子组件
class MyCom extends React.Component{
// 初始化组件状态
state = {
count: 0
}
// 1. 组件将要接受props更新
componentWillReceiveProps(){
console.log("组件将要接受props更新: componentWillReceiveProps");
}
render(){
console.log("render 渲染函数")
return (
<div>
<p>父组件传递props数据{ this.props.num }</p>
<p>子组件state数据{ this.state.count }</p>
<button onClick={ this.handleClick }>子组件按钮</button>
</div>
)
}
// 自定义事件函数
handleClick=()=>{
this.setState(() => ({
count: ++this.state.count
}))
}
}
// 父组件
class MyParent extends React.Component{
state = {
num:0
}
changeNum = () => {
this.setState(() => ({
num: ++ this.state.num
}) )
}
render(){
return (
<div>
<button onClick={ this.changeNum }>父组件按钮</button>
<MyCom num={this.state.num}/>
</div>
)
}
}
// 将组件渲染到页面上
ReactDOM.render(<MyParent /> ,document.getElementById("root"))
2.2.3 shouldComponentUpdate函数
说明:
-
shouldComponentUpdate
函数使用来判读是否更新渲染组件 - 函数返回值是一个布尔值,为true,表示继续走其他的生命周期函数,更新渲染组件
- 如果返回一个false表示,不在继续向下执行其他的生命周期函数,到此终止,不更新组件渲染
- 函数接受两个参数, 第一个参数为props将要更新的数据, 第二个参数为state将要更新的数据
示例代码如下:
// 子组件
class MyCom extends React.Component{
// 初始化组件状态
state = {
count: 0
}
// 1. 组件将要接受props更新
componentWillReceiveProps(nextProps){
console.log("组件将要接受props更新: componentWillReceiveProps");
}
// 2. 是否允许组件渲染更新
shouldComponentUpdate(nextProps, nextState){
console.log("是否允许组件渲染更新: shouldComponentupdate");
return true
}
render(){
console.log("render 渲染函数")
return (
<div>
<p>父组件传递props数据{ this.props.num }</p>
<p>子组件state数据{ this.state.count }</p>
<button onClick={ this.handleClick }>子组件按钮</button>
</div>
)
}
// 自定义事件函数
handleClick=()=>{
this.setState(() => ({
count: ++this.state.count
}))
}
}
// 父组件
//...
2.2.4 componentWillUpdate 函数
说明:
-
componentWillUpdate
组件更新渲染前执行 - 函数接受两个参数,第一个是props将要更新的数据,第二个是state将要更新的数据
- 使用是浏览器会警告, 建议使用
UNSAFE_componentWillUpdate
- 但是官网表示
UNSAFE_componentWillUpdate
也将被废弃
示例代码:
class MyCom extends React.Component{
// 初始化组件状态
state = {
count: 0
}
// 1. 组件将要接受props更新
componentWillReceiveProps(nextProps){
console.log("组件将要接受props更新: componentWillReceiveProps");
}
// 2. 是否允许组件渲染更新
shouldComponentUpdate(nextProps, nextState){
console.log("是否允许组件渲染更新: shouldComponentupdate");
return true
}
// 3.组件渲染更新前
componentWillUpdate(nextProps, nextState){
console.log("组件内容更新前: componentWillUpdate");
}
render(){
console.log("render 渲染函数")
return (
<div>
<p>父组件传递props数据{ this.props.num }</p>
<p>子组件state数据{ this.state.count }</p>
<button onClick={ this.handleClick }>子组件按钮</button>
</div>
)
}
// 自定义事件函数
handleClick=()=>{
this.setState(() => ({
count: ++this.state.count
}))
}
}
// 父组件
//...
2.2.5 render函数
更初始化一样,没事数据更新都会触发render函数重新执行, 更新页面渲染
2.2.6 componentDidUpdate函数
说明:
- 组件render执行后,页面渲染完毕了以后执行的函数
- 接受两个参数,分别为更新前的props和state
示例代码如下:
// 子组件
class MyCom extends React.Component{
// 初始化组件状态
state = {
count: 0
}
// 1. 组件将要接受props更新
componentWillReceiveProps(nextProps){
console.log("组件将要接受props更新: componentWillReceiveProps");
}
// 2. 是否允许组件渲染更新
shouldComponentUpdate(nextProps, nextState){
console.log("是否允许组件渲染更新: shouldComponentupdate");
return true
}
// 3.组件渲染更新前
componentWillUpdate(){
console.log("组件内容更新前: componentWillUpdate");
}
// 4. render渲染函数
render(){
console.log("render 渲染函数")
return (
<div>
<p>父组件传递props数据{ this.props.num }</p>
<p>子组件state数据{ this.state.count }</p>
<button onClick={ this.handleClick }>子组件按钮</button>
</div>
)
}
// 5. 组件内容更新渲染后
componentDidUpdate( prevProps, prevState){
console.log("组件内容更新后执行");
}
// 自定义事件函数
handleClick=()=>{
this.setState(() => ({
count: ++this.state.count
}))
}
}
// 父组件
//...
2.3 组件销毁
说明:
- 组件销毁阶段只有一个生命周期函数
componentWillUnmount
-
componentWillUnmont
组件销毁前执行的生命周期函数, - 没有组件销毁后执行的生命周期函数, 没什么意义,组件被销毁了,获取不到对应的内容了
- 通过会在组件销毁前清理一些不会被组件自动清理的内容, 如: 定时器,或一些特殊的事件绑定
示例代码:
// 子组件
class MyCom extends React.Component{
// 初始化组件状态
state = {
count: 0
}
render(){
console.log("render 渲染函数")
return (
<div>
<p>父组件传递props数据{ this.props.num }</p>
<p>子组件state数据{ this.state.count }</p>
<button onClick={ this.handleClick }>子组件按钮</button>
</div>
)
}
componentWillUnmount(){
console.log("组件被销毁前执行:componentWillUnmount")
}
// 自定义事件函数
handleClick=()=>{
this.setState(() => ({
count: ++this.state.count
}))
}
}
// 父组件
class MyParent extends React.Component{
state = {
isShow: true
}
changeNum = () => {
this.setState(() => ({
isShow: !this.state.isShow
}) )
}
render(){
return (
<div>
<button onClick={ this.changeNum }>父组件按钮</button>
{ this.state.isShow ? <MyCom num={this.state.num}/>: "组件不显示"}
</div>
)
}
}
// 将组件渲染到页面上
ReactDOM.render(<MyParent /> ,document.getElementById("root"))
2.4 getSnapshotBeforeUpdate函数
说明:
-
getSnapshotBeforeUpdate
必须跟componentDidUpdate
一起使用,否则就报错 - 但是不能与
componentWillReceiveProps
和componentWillUpdate
一起使用 - state和props更新都会触发这个函数的执行, 在render函数之后执行
- 接受两个参数,更新前的props和当前的state
- 函数必须return 返回结果
-
getSnapshotBeforeUpdate
返回的结果将作为参数传递给componentDidUpdate
示例代码如下:
class MyCom extends React.Component{
// 初始化组件状态
state = {
count: 0
}
render(){
console.log("render 渲染函数")
return (
<div>
<p>父组件传递props数据{ this.props.num }</p>
<p>子组件state数据{ this.state.count }</p>
<button onClick={ this.handleClick }>子组件按钮</button>
</div>
)
}
getSnapshotBeforeUpdate(){
console.log("getSnapshotBeforeUpdate")
return true
}
componentDidUpdate(){
console.log("组件内容更新后执行");
}
// 自定义事件函数
handleClick=()=>{
this.setState(() => ({
count: ++this.state.count
}))
}
}
// 父组件
class MyParent extends React.Component{
state = {
isShow: true,
num : 0
}
changeShow = () => {
this.setState(() => ({
isShow: !this.state.isShow
}) )
}
changeNum = () => {
this.setState(() => ({
num: ++this.state.num
}) )
}
render(){
return (
<div>
<button onClick={ this.changeShow }>父组件按钮</button>
<button onClick={ this.changeNum }>更新props</button>
{ this.state.isShow ? <MyCom num={this.state.num}/>: "组件不显示"}
</div>
)
}
}
// 将组件渲染到页面上
ReactDOM.render(<MyParent /> ,document.getElementById("root"))
2.5 生命周期函数参数
2.6 特殊情况的生命周期函数
在React 17 版本之后将会改变几个生命周期函数
3. React 新增生命周期函数
3.1 新版生命周期图
添加静态方法后的生命周期函数图
3.2 新增的静态方法
说明:
- react新增了静态方法
getDerviedStateFromProps
方法在生命周期中 - 这个方法用来替换
componentWillReceiveProps
, - 通常会在
ComponentWillReceiveProps
中利用Props更新state -
getDerivedStateFromProps
方法接受两个参数, 将要更新的props和state属性 - 需要返回一个布尔值,不然会报错
- 还可以取代
componentWillMount
的功能, 因为这个静态方法在组件初始化也会执行
示例代码:
class MyCom extends React.Component{
// 初始化组件状态
state = {
count: 0
}
// 生命周期上新增的静态方法
static getDerivedStateFromProps(nextProps,nextState){
console.log("getDerivedStateFromProps");
// 利用props的值更新当前组件的状态
nextState.count = nextProps.num
return true
}
render(){
console.log("render 渲染函数")
return (
<div>
<p>父组件传递props数据{ this.props.num }</p>
<p>子组件state数据{ this.state.count }</p>
<button onClick={ this.handleClick }>子组件按钮</button>
</div>
)
}
// 自定义事件函数
handleClick=()=>{
this.setState(() => ({
count: ++this.state.count
}))
}
}
// 父组件
class MyParent extends React.Component{
state = {
isShow: true,
num : 0
}
changeShow = () => {
this.setState(() => ({
isShow: !this.state.isShow
}) )
}
changeNum = () => {
this.setState(() => ({
num: ++this.state.num
}) )
}
render(){
return (
<div>
<button onClick={ this.changeShow }>父组件按钮</button>
<button onClick={ this.changeNum }>更新props</button>
{ this.state.isShow ? <MyCom num={this.state.num}/>: "组件不显示"}
</div>
)
}
}
// 将组件渲染到页面上
ReactDOM.render(<MyParent /> ,document.getElementById("root"))
4. 组件更新的方法
组件更新自己的状态,除了使用setState
更新外,还可以通过forceUpdate
更新
4.1 forceUpdate
说明:
- 在组件中也可以组件实例调用
forceUpdate
方法直接更新数据 - 在
forceUpdate
方法会强制更新状态, -
forceUpdate
方法会执行静态方法getDerivedStateFromProps
函数 - 但是
forceUpdate
方法不会触发shouldComponetUpdate
判断是否更新的生命周期函数
示例代码:
class MyCom extends React.Component{
// 初始化组件状态
state = {
count: 0
}
// 2. 是否允许组件渲染更新
shouldComponentUpdate(nextProps, nextState){
console.log("是否允许组件渲染更新: shouldComponentupdate");
// nextState.count = nextProps.num
return true
}
static getDerivedStateFromProps(nextProps,nextState){
console.log("getDerivedStateFromProps");
console.log(arguments);
return true
}
// 自定义事件函数
handleClick=()=>{
this.state.count = ++ this.state.count
this.forceUpdate();
}
// 4. render渲染函数
render(){
console.log("render 渲染函数")
return (
<div>
<p>父组件传递props数据{ this.props.num }</p>
<p>子组件state数据{ this.state.count }</p>
<button onClick={ this.handleClick }>子组件按钮</button>
</div>
)
}
}
// 父组件
//...
示例说明:
- 示例中直接修改状态并不会触发重新渲染
- 但是通过组件实例调用
forceUpdate
强制更新,会重新触发页面渲染 - 会触发生命周期
getDerivedStateFromProps
函数执行, - 但是不会是否判断是否更新的
shouldComponentUpdate
函数的执行
5. 生命周期总结
5.1 常用生命周期函数
说明:
- React生命周期函数很多, 但是并不是每一个都是那么的常用
- 因此可以将生命周期图简化一下
常用生命周期图
5.2 生命周期总结
5.2.1 组件的三个生命周期分三个阶段
- Mount : 初始化插入真实DOM
- Update : 数据更改重新渲染
- Unmount: 被移出真实的DOM
5.2.2 生命周期不同阶段的钩子函数
-
第一次初始化渲染显示: ReactDOM.render()
constructor(): 创建对象初始化state
componentWillMount() : 将要插入回调
render(): 用于插入虚拟DOM回调
componentDidMount() : 已经插入回调(启动定时器,发送ajax,只执行一次)
-
每次更新 state: this.setState()
componentWillUpdate() : 将要更新回调
render() : 更新(重新渲染)
componentDidUpdate() : 已经更新回调
- 移除组件 : ReactDOM.unmountComponentAtNode(containerDom)
- componentWillUnmount() : 组件将要被移出回调(收尾工作,例如清除定时器)
5.2.3 重要的钩子函数
render(): 初始化渲染或更新渲染调用
componentDidMount() : 开启监听,发送ajax请求
componentWillUnmount(): 做一些收尾工作,如,清理定时器
5.3 示例代码:
// 1. 定义组件
class Files extends React.Component {
constructor (){
super()
// 定义状态
this.state = {
opacity: 1
}
}
removeComponent () {
// 执行unmountComponentAtNode()移出DOM节点
ReactDOM.unmountComponentAtNode(document.getElementById('root'))
}
// 组件添加完后执行的钩子函数
componentDidMount () {
console.log('componentDidMount()')
// 开启定时器
this.timer = setInterval(function(){
console.log('定时器...')
// 1. 获取状态中的值
let {opacity} = this.state;
// 2. 改变透明度
opacity -= 0.1;
// 3. 判断透明度,如果小于0,就变为1
if(opacity <= 0){
opacity = 1
}
// 4. 改变状态
this.setState({
opacity
})
}.bind(this),200)
}
// 组件被移出后执行的钩子函数
componentWillUnmount(){
console.log('componentWillUnmount()')
// 组件被移除后清除定时器
clearInterval(this.timer)
}
render(){
console.log('render()')
// 取出状态中的透明度
let {opacity} = this.state;
return (
<div>
<h2 style={{opacity:opacity}}>{this.props.msg}</h2>
<button onClick={this.removeComponent}>删除组件</button>
</div>
)
}
}
// 设置props类型
Files.propTypes = {
msg: PropTypes.string
}
// 2. 渲染组件标签
ReactDOM.render(
<Files msg="我太难了" />,
document.getElementById('root')
)