9. React组件生命周期
2. React组件生命周期
-
2.1. 认识生命周期
- 2.1.1. 很多事物都有从创建到销毁的整个过程,这个过程称之为生命周期;
- 2.1.2. React组件也有自己的生命周期,了解生命周期可以让我们在最合适的地方完成想要的功能
- 2.1.3. 生命周期和生命周期函数的关系:
- 2.1.3.1. 生命周期是一个抽象的概念,在生命周期的整个过程,分成了很多个阶段;
- 比如挂载阶段(
Mount
), 组件第一次在DOM树中被渲染的过程; - 比如更新阶段(
Update
), 组件状态发生变化时,重新更新渲染的过程; - 比如卸载阶段(
Unmount
), 组件从DOM树中移除的过程;
- 比如挂载阶段(
- 2.1.3.2. React内部为了告诉我们当前处于那些阶段,会
对组件内部实现的某些函数进行回调
,这些函数就是生命周期函数
;- 比如实现
componentDidMount函数
:组件已经挂载到DOM上
时,就会回调; - 比如实现
componentDidUpdate函数
:组件已经更新
时,就会回调; - 比如实现
componentWillUnmount函数
:组件即将被移除
时,就会回调; - 可以在这些回调函数中编写自己的逻辑代码,来完成自己的需求功能;
- 比如实现
- 2.1.3.1. 生命周期是一个抽象的概念,在生命周期的整个过程,分成了很多个阶段;
- 2.1.4. React生命周期时,主要指的是类的生命周期,因为函数式组件是没有生命周期函数的(后面可以通过hooks来模拟一些生命周期的回调,写到hooks会在进行记录)
-
2.2. 生命周期解析
-
2.2.1. 先来了解下最基础、最常用的生命周期函数:
compentDidMount
、componentDidUpdate
、componentWillUnmount
函数 -
2.2.2. 生命周期函数如图:官网图谱地址
-
2.2.3. 挂载阶段:
componentDidMount
-
- 在挂载时,首先会创建一个组件实例,在创建实例的过程中,就会执行类中
constructor函数
(每次引入使用组件时,创建的是ReactElement
, 每次调用就创建一个组件实例, 每次创建实例时,就会先执行constructor函数
)
类 -> 类实例class Person {} -> 类const p1 = new Person() -> 类实例
- 在挂载时,首先会创建一个组件实例,在创建实例的过程中,就会执行类中
-
- 紧接着调用类中的
render方法
- 紧接着调用类中的
-
- 挂载完成后会调用的
componentDidMount函数
- 挂载完成后会调用的
-
- 如下图:
- 如下图:
-
- 示例代码如下:
- HelloWorld.jsx
//import React from 'react';class HelloWorld extends React.Component {// 1.构造方法:constructorconstructor () {console.log('HelloWorld constructor')super();this.state = {message: 'Hello World'}}// 2.执行render方法render () {console.log('HelloWorld render')return (<div><h2>Hello World</h2><p>Hello World是程序员的第一个代码</p></div>)}// 3.组件被渲染到DOM:被挂载到DOM上componentDidMount () {console.log('HelloWorld componentDidMount')}}export default HelloWorld;
- App.jsx
import React from "react";import HelloWorld from "./HelloWorld";class App extends React.Component {render () {return (<div>哈哈哈哈{/* 组件每引用一次,就创建一个实例,每创建一次组件实例,就会先执行constructor函数 */}<HelloWorld /><HelloWorld />{/* <HelloWorld /><HelloWorld /> */}</div>)}}export default App;
-
-
2.2.4. 更新阶段:componentDidUpdate
-
- 当数据发生更新时,执行setState方法
-
- 执行setState方法后,重新渲染页面, 重新执行render方法,它会根据最新修改之后的message,重新生成一个新的树,然后在进行diff算法,然后决定要更新那部分
-
- react会更新dom和refs,并调用componentDidUpdate方法
-
- 如下图:
- 如下图:
-
- 关键代码如下;
// 4.组件的DOM被更新完成:DOM发生更新componentDidUpdate () {/*** * - 1. 当数据发生更新时,执行setState方法* - 2. 执行setState方法后,重新渲染页面, 重新执行render方法,它会根据最新修改之后的message,重新生成一个新的树,然后在进行diff算法,然后决定要更新那部分* - 3. react会更新dom和refs,并调用componentDidUpdate方法* * * */console.log('HelloWorld componentDidUpdate')}
- 详细代码如下:
import React from 'react';class HelloWorld extends React.Component {// 1.构造方法:constructorconstructor () {console.log('HelloWorld constructor')super();this.state = {message: 'Hello World'}}changeText () {this.setState({ message: '你好啊 李银河' })}// 2.执行render方法render () {const { message } = this.stateconsole.log('HelloWorld render')return (<div><h2>{message}</h2><button onClick={e => this.changeText()}>修改文本</button><p>{message}是程序员的第一个代码</p></div>)}// 3.组件被渲染到DOM:被挂载到DOM上componentDidMount () {/*** * - 1. 在挂载时,首先会创建一个组件实例,在创建实例的过程中,就会执行类中`constructor函数`(每次引入使用组件时,创建的是ReactElement, 每次调用就创建一个组件实例, 每次创建实例时,就会先执行constructor函数)* - 2. 紧接着调用类中的`render方法`* - 3. 挂载完成后会调用的`componentDidMount函数`* * * */ console.log('HelloWorld componentDidMount')}// 4.组件的DOM被更新完成:DOM发生更新componentDidUpdate () {/*** * - 1. 当数据发生更新时,执行setState方法* - 2. 执行setState方法后,重新渲染页面, 重新执行render方法,它会根据最新修改之后的message,重新生成一个新的树,然后在进行diff算法,然后决定要更新那部分* - 3. react会更新dom和refs,并调用componentDidUpdate方法* * * */console.log('HelloWorld componentDidUpdate')}}export default HelloWorld;
- 关键代码如下;
-
-
2.2.5. 卸载阶段:
componentWillUnmount
-
- 当组件从 DOM 中移除时,会调用 componentWillUnmount() 方法。
-
- 如下图:
- 如下图:
-
- 示例代码:
- 关键代码:
// 5.组件从DOM中卸载掉:从DOM移除掉componentWillUnmount () {console.log('HelloWorld componentWillUnmount')}
-
- 完整代码:
- HelloWorld.jsx
import React from 'react';class HelloWorld extends React.Component {// 1.构造方法:constructorconstructor () {console.log('HelloWorld constructor')super();this.state = {message: 'Hello World'}}changeText () {this.setState({ message: '你好啊 李银河' })}// 2.执行render方法render () {const { message } = this.stateconsole.log('HelloWorld render')return (<div><h2>{message}</h2><p>{message}是程序员的第一个代码</p><button onClick={e => this.changeText()}>修改文本</button></div>)}// 3.组件被渲染到DOM:被挂载到DOM上componentDidMount () {/*** * - 1. 在挂载时,首先会创建一个组件实例,在创建实例的过程中,就会执行类中`constructor函数`(每次引入使用组件时,创建的是ReactElement, 每次调用就创建一个组件实例, 每次创建实例时,就会先执行constructor函数)* - 2. 紧接着调用类中的`render方法`* - 3. 挂载完成后会调用的`componentDidMount函数`* * * */ console.log('HelloWorld componentDidMount')}// 4.组件的DOM被更新完成:DOM发生更新componentDidUpdate () {/*** * - 1. 当数据发生更新时,执行setState方法* - 2. 执行setState方法后,重新渲染页面, 重新执行render方法,它会根据最新修改之后的message,重新生成一个新的树,然后在进行diff算法,然后决定要更新那部分* - 3. react会更新dom和refs,并调用componentDidUpdate方法* * * */console.log('HelloWorld componentDidUpdate')}// 5.组件从DOM中卸载掉:从DOM移除掉componentWillUnmount () {console.log('HelloWorld componentWillUnmount')}}export default HelloWorld;
- App.jsx
import React from "react"; import HelloWorld from "./HelloWorld";class App extends React.Component {constructor () {super();this.state = {isSHowHw: true}}switchShowHw () {this.setState ({ isSHowHw: !this.state.isSHowHw })}render () {const { isSHowHw } = this.state;return (<div>哈哈哈哈{/* 组件每引用一次,就创建一个实例,每创建一次组件实例,就会先执行constructor函数 */}<button onClick={ e => this.switchShowHw() }>切换</button>{ isSHowHw && <HelloWorld />}</div>) } }export default App;
-
-
2.2.6. 开发中常用的是:
constructor()、render()、componentDidMount()、componentWillUnmount()
-
-
2.3. 生命周期函数
- 2.3.1.
constructor
-
- 如果不初始化state或不进行方法绑定,则不需要为React组件实现构造函数。
-
- constructor中通常只做两件事:
- 通过给
this.state
赋值对戏那个初始化内部的state; - 为事件绑定实例(this);
-
- 2.3.2
componentDidMount
-
componentDidMount()会在组件挂载之后(插入DOM树中)立即调用
-
componentDidMount
中通过进行那些操作?
- 2.1. 依赖于DOM的操作可以在这里进行;
- 2.2. 再次是发送网络请求最好的地方(官方建议)
- 2.3. 可以在此处添加一些订阅(在
componentWillUnmount
中取消订阅,不然会内存泄漏)
-
- 2.3.3
componentDidUpdate
-
componentDidUpdate()
会在组件更新之后立即调用, 首次渲染不会执行此方法;
- 当组件更新后,可以在此处对DOM进行操作;
- 如果对更新前后的props进行了比较,也可以选择在此处进行网络请求(例如:当props未发生变化时,则不会执行网络请求)
-
- 2.3.4
componentWillUnmount
-
componentWillUnmount()
会在组件卸载以及销毁之前直接调用。
- 在此方法中执行必要的清理操作;
- 例如:清楚timer, 取消网络请求或清除在
componentDidMount
中创建的订阅等;
-
- 2.3.1.
-
2.4. 不常用的生命周期函数
- 2.4.1. 不常用的生命周期如下图:
-
2.4.2. 不常用生命周期多出来这些方法:
getDerivedStateFromProps()、shouldComponentUpdate()、getSnapshotBeforeUpdate()
-
2.4.3.
getDerivedStateFromProps()
-
getDerivedStateFromProps()
:在更新或者第一次挂载时,如果state里面的数据需要依赖Props, 可以在这个回调函数里完成,这个函数用的非常少
-
getDerivedStateFromProps()
: state的值在任何使用时候依赖于props时候用,该方法返回一个对象来更新state
-
-
2.4.4.
shouldComponentUpdate ()
-
shouldComponentUpdate()
: 该生命周期函数很常用,等记录到性能优化时再详细记录;
-
- 组件是否需要更新(shouldComponentUpdate翻译:组件要不要更新),返回true或者false,默认是true。
-
- 当通过setState去更新数据的时候,render函数一般会直接执行,重新渲染,如果在shouldComponentUpdate生命周期里面返回false,render()不会执行,组件不会重新渲染。
-
- 某些情况下返回false,可以提高性能
-
-
2.4.5.
getSnapshotBeforeUpdate()
-
getSnapshotBeforeUpdate()
:React更新DOM之前回调的一个函数,可以获取DOM更新前的一些信息(例如:滚动位置)
-
- (Snapshot:快照)在组件更新之前,获取快照,保存一些数据,返回值会作为参数传递给componentDidUpdate()方法。
-
-
2.4.6. 更详细的生命周期相关的内容,可以参考官网:官网
-
2.4.7. 不常用生命周期函数代码示例如下:
- 关键代码:
// 6.不常用的生命周期补充shouldComponentUpdate () {// 是否需要重新更新,默认为true,重新更新render函数,重新渲染return true}getSnapshotBeforeUpdate () {console.log('getSnapshotBeforeUpdate')return {scrollPosition: 1000}}
- 完整代码:
import React from 'react';class HelloWorld extends React.Component {// 1.构造方法:constructorconstructor () {console.log('HelloWorld constructor')super();this.state = {message: 'Hello World'}}changeText () {this.setState({ message: '你好啊 李银河' })}// 2.执行render方法render () {const { message } = this.stateconsole.log('HelloWorld render')return (<div><h2>{message}</h2><p>{message}是程序员的第一个代码</p><button onClick={e => this.changeText()}>修改文本</button></div>)}// 3.组件被渲染到DOM:被挂载到DOM上componentDidMount () {/*** * - 1. 在挂载时,首先会创建一个组件实例,在创建实例的过程中,就会执行类中`constructor函数`(每次引入使用组件时,创建的是ReactElement, 每次调用就创建一个组件实例, 每次创建实例时,就会先执行constructor函数)* - 2. 紧接着调用类中的`render方法`* - 3. 挂载完成后会调用的`componentDidMount函数`* * * */ console.log('HelloWorld componentDidMount')}// 4.组件的DOM被更新完成:DOM发生更新componentDidUpdate (prevProps, prevState, snapshot) {/*** * - 1. 当数据发生更新时,执行setState方法* - 2. 执行setState方法后,重新渲染页面, 重新执行render方法,它会根据最新修改之后的message,重新生成一个新的树,然后在进行diff算法,然后决定要更新那部分* - 3. react会更新dom和refs,并调用componentDidUpdate方法* * * */ console.log('HelloWorld componentDidUpdate: ', prevProps, prevState, snapshot)}// 5.组件从DOM中卸载掉:从DOM移除掉componentWillUnmount () {console.log('HelloWorld componentWillUnmount')}// 6.不常用的生命周期补充shouldComponentUpdate () {// 是否需要重新更新,默认为true,重新更新render函数,重新渲染, 返回false, 不执行render,不重新渲染界面return true}getSnapshotBeforeUpdate () {console.log('getSnapshotBeforeUpdate')return {scrollPosition: 1000}}}export default HelloWorld;
- 2.4.1. 不常用的生命周期如下图: