一、概述
什么是生命周期
每个组件的实例从创建到运行再到销毁 在这个过程中的特定阶段会触发一系列的事件
这些事件就叫做组件的生命周期函数
React的组件生命周期函数
React组件的生命周期分为三部分:创建 运行 销毁
组件创建
阶段
(在组件的整个生命周期中只会被执行一次)
- componentWillMount / 组件将要挂载
- render / 渲染虚拟DOM元素
- componentDidMount / 组件已经挂载
组件运行
阶段
(根据props属性或state属性的改变 有选择性地执行0或多次
若属性从创建开始从未改变 那就是0次
属性改变了多少次 就执行多少次)
- componentWillReceiveProps / 组件将要接收属性
- shouldComponentUpdate / 组件是否需要更新(判断 返回true则渲染页面 返回false则不渲染页面 但数据依旧还是最新的)
- componentWillUpdate / 组件将要更新
- render / 渲染虚拟DOM元素
- componentDidUpdate / 组件完成更新
组件销毁
阶段
(在组件的整个生命周期中只会被执行一次)
- componentWillUnmount / 组件将要解除挂载
二、从案例中看生命周期
========== 组件创建阶段 ==========
componentWillMount() 和 render() 和 componentDidMount()
import React from 'react';
export default class Hello extends React.Component
{
constructor(props)
{
super(props);
this.state={
msg:"aaa"
}
}
// ★【componentWillMount】在组件即将挂载到页面之前执行 此时页面尚未挂载到页面中 虚拟DOM也尚未创建
componentWillMount() // 相当于Vue的created生命周期
{
// 此时无法获取到页面上的任何元素
console.log(document.getElementById("myh3")) // 输出undefined
console.log(this.props.initCount) // 1
console.log(this.state.msg) // aaa
this.testFunction();
}
// 在render的return执行之前 此时虚拟DOM尚未创建 页面上是空的 无法获取任何元素
render()
{
console.log(document.getElementById("myh3")) // 输出undefined
return <div>
<h1>Counter Component</h1>
<input type="button" value="+1"/>
<hr/>
<h3 id="myh3">当前数值为{this.props.initCount}</h3>
</div>
// 在render执行完毕后 内存中就有了虚拟DOM 但页面上依旧是尚未显示
}
// ★【componentDidMount】在组件挂载到页面上之后执行 此时页面上已经有可见的DOM元素了
// 在该函数中可以放心地去操作页面上的DOM元素了 且最早能操作DOM元素的生命周期就是这个
// 当执行完毕该函数后 即进入了运行中的状态 该函数是创建阶段的最后一个函数
componentDidMount() // 相当于Vue的mounted生命周期
{
console.log(document.getElementById("myh3")) // 能获取到DOM 不再是undefined了
}
testFunction()
{
console.log("just test...")
}
}
========== 组件运行阶段 ==========
shouldComponentUpdate 和 componentWillUpdate 和 render 和 componentDidUpdate
在这里以一个计数器作为案例:
import React from 'react'
import ReactDOM from 'react-dom'
import Counter from "@/components/Counter"
ReactDOM.render(<div>
<Counter initCount={1}></Counter>
</div>,document.getElementById("app"))
(为节省篇幅 在此片段只专门列举组件运行阶段的生命周期函数)
import React from 'react';
import ReactPropTypes from "prop-types";// 提供常见的数据类型 用于类型校验
export default class Counter extends React.Component
{
constructor(props)
{
super(props);
this.state={
count:props.initCount
}
}
render()
{
// 在组件运行阶段时 调用render函数 页面上的DOM元素还是旧的
console.log(this.refs.h3 && this.refs.h3.innerHTML)
return <div>
<input type="button" value="增加" onClick={() => {this.increaseCount()}}/>
<hr/>
<h3 id="myh3" ref="h3">当前数值为{this.state.count}</h3>
</div>
}
increaseCount = () => {
this.setState({
count:this.state.count+1
})
}
// ★【shouldComponentUpdate】判断组件是否需要更新
// 注:shouldComponentUpdate里获取的数值有迟滞性(慢半拍) 通过this获取的数据会是前一次的
// 可以在shouldComponentUpdate的入参中定义nextProps和nextState以获取props和state里的实时数据(入参的名称可任意取)
shouldComponentUpdate(nextProps,nextState)
{
// shouldComponentUpdate要求必须返回一个布尔值 作为组件更新生命周期是否继续执行的参考
// 若返回【true】 则继续执行后面的生命周期
// 若返回【false】 则不执行后面的生命周期了 若点击增加按钮 state中的值依旧会增加 但是后面的render函数并不会被调用 因此虚拟DOM和页面上的数据都是旧的
return true;
}
// ★【componentWillUpdate】在组件将要更新时执行 将要更新的意思就是已经确定要更新 但是还没有实际的更新操作
// 在进入该生命周期函数的时候 内存中的虚拟DOM和页面上的DOM元素都还是旧的
componentWillUpdate()
{
// 此时页面上的DOM节点还是旧的 尚未更新
console.log(this.refs.h3.innerHTML) // 比如页面上展示的数值为3 那么此时这里打印的还是2
}
// ★【componentDidUpdate】在组件完成更新后执行 此时state中的数据和虚拟DOM和页面上的DOM都已更新为最新的了
componentDidUpdate()
{
console.log(this.refs.h3.innerHTML)
}
}
componentWillReceiveProps
这里以一个父子组件作为案例:
import React from 'react';
// 父组件
export default class Parent extends React.Component
{
constructor(props)
{
super(props);
this.state={
msg:"Old Parent Msg"
}
}
render()
{
return <div>
<h1>Parent</h1>
<input type="button" value="修改" onClick={() => {this.changeMsg()}}/>
<hr/>
<Son parentMsg={this.state.msg}></Son>
</div>
}
changeMsg = () => {
this.setState({
msg:"New Parent Msg"
})
}
}
// 子组件
class Son extends React.Component
{
constructor(props)
{
super(props);
this.state={}
}
render()
{
return <div>
<h3>Son : {this.props.parentMsg}</h3>
</div>
}
// ★【componentWillReceiveProps】在组件将要接收外界传来的props时执行
// 当组件第一次被渲染到页面上的时候并不会触发该生命周期函数
// 只有 外界通过某些事件重新修改了传入的props数据 才会触发该生命周期函数
componentWillReceiveProps(nextProps)
{
// 通过this.props获得的并不是最新的props数据 而是被触发后上一次的旧值
// 若要获取最新的属性值 则需要在入参中定义nextProps 然后通过nextProps获取
console.log("旧值:"+this.props.parentMsg+" - 新值:"+nextProps.parentMsg)
}
}
(组件销毁阶段不太方便演示 在此就不演示了)
生命周期函数的入参列表:
- Mounting创建:
- constructor()
- componentWillMount()
- render()
- componentDidMount()
- Updating更新:
- componentWillReceiveProps(nextProps) // 获取最新props
- shouldComponentUpdate(nextProps, nextState) // 获取最新属性
- componentWillUpdate(nextProps, nextState) // 获取最新属性
- render()
- componentDidUpdate(prevProps, prevState) // 在全部更新完毕后 可以获取旧的属性(prev)
- Unmounting销毁:
- componentWillUnmount()
三、从示意图中看生命周期
(画了张示意图 希望能够帮助理解)