1 前言
React生命周期描述了组件在创建、更新和销毁过程中所经历的不同阶段。这些生命周期方法可以在组件的代码中定义并实现,以便在适当的时候执行自定义操作。
现在我们先学习旧版(16.x及以前)的生命周期函数,然后在对比学习新版(17.x及以后)声明周期方法。
以下是React 16及之前版本中组件的生命周期图示:
2 示例引出生命周期函数
示例效果如下图1-1所示:
- 字体透明度逐渐变淡重复执行
- 点击移除按钮,组件移除
代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>1201_引出生命周期</title>
</head>
<body>
<div id="test"></div>
<!-- react核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 用于支持react操作DOM -->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!-- 用于将jsx转为js -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/babel">
/**
* 创建组件
* 生命周期函数:生命周期钩子函数
*/
class Life extends React.Component {
// 初始化状态
state = {
opacity: 1 // 透明度
}
/**
* 移除组件
*/
remove = () => {
// 卸载组件
ReactDOM.unmountComponentAtNode(document.getElementById('test'))
}
/**
* 组件挂载完毕
*/
componentDidMount() {
// 实现字体逐渐变淡效果
this.timer = setInterval(() => {
let {opacity} = this.state
// 字体透明度为0即透明后,重新赋值1
if (opacity <= 0) {
opacity = 1
}
opacity -= 0.1
this.setState({opacity})
}, 200);
}
/**
* 组件卸载之前
*/
componentWillUnmount() {
// 清除定时器
clearInterval(this.timer)
}
/**
* 初始化渲染,状态更新重新渲染
*/
render() {
return (
<div>
<h2 style={{opacity: this.state.opacity}}>学习React组件生命周期</h2><br/>
<button onClick={this.remove}>移除</button>
</div>
)
}
}
// 2.渲染虚拟DOM到页面
ReactDOM.render(<Life/>, document.getElementById('test'))
</script>
</body>
</html>
- ReactDOM.unmountComponentAtNode()为卸载组件API
- 定时器要求组件挂载之后创建,创建一次;在组件卸载之前清除。
- componentDidMount():组件挂载之后执行
- componentWillUnmount():组件卸载之前执行
- React还提供了很多其他生命周期函数
生命周期函数也称生命周期钩子函数,生命周期钩子。React会在合适的时间点,调用相应的生命周期函数。
3 生命周期
React 16之前的生命周期被分为三个阶段:
- 挂载(Mounting)阶段
- 更新(Updating)阶段
- 卸载(Unmounting)阶段
3 挂载阶段
挂载阶段(Mounting):这是组件在实例化并插入DOM中的过程。在这个阶段,React会依次调用以下方法:
- constructor():组件的构造函数,在组件创建时调用。
- componentWillMount():组件插入DOM之前被调用。
- render():生成虚拟DOM并返回JSX。
- componentDidMount():组件插入DOM后被调用。
示例代码如下3-1所示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>1202_生命周期(旧)</title>
</head>
<body>
<div id="test"></div>
<!-- react核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 用于支持react操作DOM -->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!-- 用于将jsx转为js -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/babel">
/**
* 创建组件
* 生命周期函数:生命周期钩子函数
*/
class Count extends React.Component {
constructor(props) {
console.log('Count === constructor');
super(props)
// 初始化状态
this.state = {
count: 1 // 计数
}
}
/**
* +1
*/
add = () => {
this.setState({count: this.state.count + 1})
}
/**
* 卸载组件
*/
remove = () => {
ReactDOM.unmountComponentAtNode(document.getElementById('test'))
}
/**
* 组件将要挂载
*/
componentWillMount() {
console.log('Count === componentWillMount');
}
/**
* 组件挂载完毕
*/
componentDidMount() {
console.log('Count === componentDidMount');
}
/**
* 组件卸载之前
*/
componentWillUnmount() {
console.log('Count === componentWillUnmount');
}
/**
* 初始化渲染,状态更新重新渲染
*/
render() {
console.log('Count === render');
const { count } = this.state
return (
<div>
<h2>当前计数:{count}</h2><br />
<button onClick={this.add}>点我+1</button>
</div>
)
}
}
// 2.渲染虚拟DOM到页面
ReactDOM.render(<Count />, document.getElementById('test'))
</script>
</body>
</html>
如下图3-1所示:
4 更新阶段
在更新阶段,组件的props或state被修改,导致重新渲染。以下是更新阶段的生命周期方法:
- componentWillReceiveProps():用于在组件接收到新的 props 时进行操作。它会在组件接收到新的 props 之前被调用,并且可以根据新的 props 来更新组件的状态。
- shouldComponentUpdate(nextProps, nextState):在组件更新时调用,决定是否需要重新渲染组件,可以优化性能,默认返回true。
- componentWillUpdate():作用是让组件在更新之前进行一些准备工作,例如计算一些属性或状态的值,或者更新组件之前进行一些必要的清理操作。但需要注意的是,在此方法中不能直接更新组件的状态或DOM,因为此时组件尚未被更新。
- render():在组件更新时调用,返回虚拟DOM,用于渲染到页面。
- componentDidUpdate(prevProps, prevState, snapshot):在组件更新后调用,可以执行DOM操作或发起网络请求等操作。
第一种更新,setState()引起的更新
- shouldComponentUpdate返回true与返回false的区别
以代码3-1为例,额外添加shouldComponentUpdate函数代码如下;
shouldComponentUpdate(nextProps, nextState) {
console.log('count === shouldComponentUpdate :', nextProps, '----', nextState);
return true;
}
返回true时,如下图4-1所示:
返回false时,如下图4-2所示:
声明周期函数shouldComponentUpdate()默认返回true,该方法之后的生命周期函数正常执行;当返回false时,不在执行后续的生命周期函数。
第二种更新,执行forceUpdate()后的更新
示例,上述基础在添加一个按钮,点击执行forceUpdate,示例代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>1203_updating-生命周期(旧)</title>
</head>
<body>
<div id="test"></div>
<!-- react核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 用于支持react操作DOM -->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!-- 用于将jsx转为js -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/babel">
/**
* 创建组件
* 生命周期函数:生命周期钩子函数
*/
class Count extends React.Component {
constructor(props) {
console.log('Count === constructor');
super(props)
// 初始化状态
this.state = {
count: 1 // 计数
}
}
/**
* +1
*/
add = () => {
this.setState({count: this.state.count + 1})
}
/**
* 不更新状态,强制更新
*/
force = () => {
console.log('Count ==== forceUpate');
this.forceUpdate()
}
/**
* 卸载组件
*/
remove = () => {
ReactDOM.unmountComponentAtNode(document.getElementById('test'))
}
/**
* 组件将要挂载
*/
componentWillMount() {
console.log('Count === componentWillMount');
}
/**
* 组件挂载完毕
*/
componentDidMount() {
console.log('Count === componentDidMount');
}
/**
* 组件是否应该被更新
*/
shouldComponentUpdate(nextProps, nextState) {
console.log('count === shouldComponentUpdate :', nextProps, '----', nextState);
return true;
}
/**
* 组件将要被更新之前
*/
componentWillUpdate(nextProps, nextState) {
console.log('count === componentWillUpdate :', nextProps, '----', nextState);
}
/**
* 组件将要被更新之后
*/
componentDidUpdate(prevProps, prevState) {
console.log('count === componentDidUpdate :', prevProps, '----', prevState);
}
/**
* 组件卸载之前
*/
componentWillUnmount() {
console.log('Count === componentWillUnmount');
}
/**
* 初始化渲染,状态更新重新渲染
*/
render() {
console.log('Count === render');
const { count } = this.state
return (
<div>
<h2>当前计数:{count}</h2><br />
<button onClick={this.add}>点我+1</button><br/>
<button onClick={this.force}>不更改状态强制更新</button>
</div>
)
}
}
// 2.渲染虚拟DOM到页面
ReactDOM.render(<Count />, document.getElementById('test'))
</script>
</body>
</html>
点击示例效果如下图4-3所示:
执行组件的forceUpdate()方法,不受shouldComponentUpdate函数的影响。
第三种更新,父子组件中父组件更新传值引起的子组件更新。
实例代码如下4-4代码所示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>1204_parent-son-生命周期(旧)</title>
</head>
<body>
<div id="test"></div>
<!-- react核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 用于支持react操作DOM -->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!-- 用于将jsx转为js -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/babel">
/**
* 创建组件
* 生命周期函数:生命周期钩子函数
*/
class SuperCar extends React.Component {
state = {carName: '第一代奔驰'}
changeCar = () => {
this.setState({carName: '第二代奔驰'})
}
/**
* 初始化渲染,状态更新重新渲染
*/
render() {
const { carName } = this.state
return (
<div>
<h2>我的父组件</h2><br />
<button onClick={this.changeCar}>换车</button><br/>
<SubCar carName={carName}/>
</div>
)
}
}
class SubCar extends React.Component {
/**
* 组件将要接受更新props
*/
componentWillReceiveProps(nextProps) {
console.log('SubCar === componentWillReceiveProps', '---', nextProps);
}
/**
* 组件将要挂载
*/
componentWillMount() {
console.log('SubCar === componentWillMount');
}
/**
* 组件挂载完毕
*/
componentDidMount() {
console.log('SubCar === componentDidMount');
}
/**
* 组件是否应该被更新
*/
shouldComponentUpdate(nextProps, nextState) {
console.log('SubCar === shouldComponentUpdate :', nextProps, '----', nextState);
return true;
}
/**
* 组件将要被更新之前
*/
componentWillUpdate(nextProps, nextState) {
console.log('SubCar === componentWillUpdate :', nextProps, '----', nextState);
}
/**
* 组件将要被更新之后
*/
componentDidUpdate(prevProps, prevState) {
console.log('SubCar === componentDidUpdate :', prevProps, '----', prevState);
}
/**
* 组件卸载之前
*/
componentWillUnmount() {
console.log('SubCar === componentWillUnmount');
}
render() {
console.log('SubCar === render');
return (
<div>
<h2>我是子组件,接收到的是{this.props['carName']}</h2>
</div>
)
}
}
// 2.渲染虚拟DOM到页面
ReactDOM.render(<SuperCar />, document.getElementById('test'))
</script>
</body>
</html>
效果如下图4-4所示:
componentWillReceiveProps默认第一次不会执行,再次更新props才会执行。
5 卸载阶段
通过ReactDOM.unmountComponentAtNode(组件)卸载组件,在卸载之前回调生命周期函数componentWillUnmount()
在#2中有示例,这里不在演示。
目前旧版本React组件的生命周期讲解完毕,下面我们对比讲解下新版React组件的生命周期。
后记
❓QQ:806797785
⭐️源代码仓库地址:https://gitee.com/gaogzhen/react-study
参考:
[1]尚硅谷React教程(2022加更,B站超火react教程)[CP/OL].2020-12-15.p37-p42.
[2]React官网[CP/OL].
[2]ChatGPT[CP/OL].