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.构造方法:constructor constructor () { 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.构造方法:constructor constructor () { console.log('HelloWorld constructor') super(); this.state = { message: 'Hello World' } } changeText () { this.setState({ message: '你好啊 李银河' }) } // 2.执行render方法 render () { const { message } = this.state console.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.构造方法:constructor constructor () { console.log('HelloWorld constructor') super(); this.state = { message: 'Hello World' } } changeText () { this.setState({ message: '你好啊 李银河' }) } // 2.执行render方法 render () { const { message } = this.state console.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.构造方法:constructor constructor () { console.log('HelloWorld constructor') super(); this.state = { message: 'Hello World' } } changeText () { this.setState({ message: '你好啊 李银河' }) } // 2.执行render方法 render () { const { message } = this.state console.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. 不常用的生命周期如下图: