React学习之旅Part8:通过例子和示意图弄懂React的生命周期

一、概述

什么是生命周期

每个组件的实例从创建到运行再到销毁 在这个过程中的特定阶段会触发一系列的事件
这些事件就叫做组件的生命周期函数

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...")
    }
}

========== 组件运行阶段 ==========

shouldComponentUpdatecomponentWillUpdaterendercomponentDidUpdate

在这里以一个计数器作为案例:

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()

三、从示意图中看生命周期

(画了张示意图 希望能够帮助理解)
在这里插入图片描述


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章