React:类组件第一次渲染的底层逻辑

创建类组件

  • 创建一个构造函数(类)      
    • 要求必须继承React.Component/PureComponent这个类
    • 使用ES6中的class创建类(最方便)
    • 类中必须设置render方法(放置在原型上):在render方法中,返回需要渲染的视图

 

import React from 'react';

class Vote extends React.Component {
    render() {
        return <div className="vote-box">
            <div className="header">
                <h2 className="title">标题</h2>
                <span>15人</span>
            </div>
            <div className="main">
                <p>支持人数:10人</p>
                <p>反对人数:15人</p>
            </div>
            <div className="footer">
                <button onClick={ () => {} }>支持</p>
                <button onClick={ () => {} }>反对</p>
            </div>
        </div>    
    
    }
}
export default Vote;
import React from 'react';
import ReractDom from 'react-dom/client'
import Vote from './views/Vote'

const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(
	<>
    	<Vote title="React其实还是很好学的!" />
    </>
)

render函数在渲染的时候,如果type是:

  • 字符串:创建一个标签
  • 普通函数:把函数执行,并把props传递给函数
  • 构造函数: 把构造函数基于new执行(也就是创建类的一个实例),也会把解析出来的props传递过去
    • 每调用一次类组件都会创建一个单独的实例
    • 把在类组件中的render函数执行,把返回的jsx(virtualDom)当作组件视图进行渲染!!
    • 例如:
      • new Vote({ title: 'React 其实还是很好学的!'})
从调用类组件 (new Vote({...}))开始,类组件内部发生的事情
1. 初始化属性 && 规则校验

先规则校验,校验完毕后再处理属性的其他操作!!
        方案一:
        constructor(props) {
            super(props) // 会把传递进来的属性挂载到this实例上
            console.log(this.props) //获取到传递的属性
        } 

        方案二:即便我们自己不在constructor中处理(或者constructor都没写),在constructor处理完毕后,React内部也会把传递的props挂载到实例上:所以其他的函数中,只要保证this是实例,就可以基于this.props获取传递的属性!
        同样this.props获取的属性对象也是被冻结的(只读的) Object.isFrozen(this.props) -> true

2.初始化状态 

        状态:后期修改状态,可以触发视图的更新
       需要手动初始化,如果我们没有去做相关的处理,则默认会往实例上挂载一个state,初始值是null => this.state = null

        手动处理:
        state={ ... };

        修改状态:控制视图更新

        this.state.xxx=xxx:这种操作仅仅是修改了状态值,但是无法让视图更新

        想让视图更新,我们需要基于React.Component.prototype提供的方法操作

        @1 this.setState(partialState) 既可以修改状态,也可以让视图更新 (推荐)
          partialState:部分状态
          this.setState({
            xxx:xxx
          })

        @2 this.forceUpdate() 强制更新

3.触发 componentWillMount 周期函数(钩子函数):组件第一次渲染之前 

        钩子函数:在程序运行到某个阶段,我们可以基于提供一个处理函数,让开发者在这个阶段做一些自定义的事情

        此周期函数 目前是不安全的(虽然可以用,但是未来可能要被移除,不建议使用)

        控制台会抛出黄色警告(为了不抛出警告,我们可以暂时 UNSAFE_componentWillMount)

        如果开启了React.StrictMode (React的严格模式),则我们使UNSAFE_componentWillMount 这样的周期函数,控制台会直接抛出红色错误!!

        React.StrictMode VS "use strict"

                "use strict":JS的严格模式        

                React.StrictMode:React的严格模式,它会去检查React中一些不规范的语法,或者是一些不建议使用的API等!!

4. 触发 render 周期函数:渲染 
5. 触发 componentDidMount 周期函数:第一次渲染完毕

        已经把virtualDOM变为真实DOM了(所以我们可以获取真实DOM了)

组件的更新逻辑(第一种:组件内部的状态被修改,组件会更新)
1. 触发 shouldComponentUpdate 周期函数:是否允许更新

        shouldComponentUpdate(nextProps, nextState) {
             // nextState: 存储要修改的最新状态
             // this.state: 存储的还是修改前的状态(此时状态还没有改变)
             console.log(this.state, nextState);
             
             // 此周期函数需要返回true/false
             //   返回true:允许更新,会继续执行下一个操作
             //   返回false:不允许更新,接下来啥都不处理
             return true
         }

2. 触发componentWillUpdate周期函数:更新之前

        此周期函数也是不安全的,在这个阶段,状态/属性还没有被修改

3. 修改状态值/属性值 (让this.state.xxx改为最新的值)
4. 触发 render 周期函数:组件更新

       1. 按照最新的状态/属性,吧返回的JSX编译为virtualDOM

       2. 和第一次渲染出来的virtualDOM进行对比(DOM-DIFF)

       3. 把差异的部分进行渲染 (渲染为真实的DOM)

5. 触发 componentDidUpdate 周期函数:组件更新完毕 

特殊说明: 如果我们是基于 this.forceUpdate()强制更新视图,会跳过 shouldComponentUpdate 周期函数的校验,直接从willUpdate 开始进行更新 (也就是:视图一定会触发更新)!

组件更新的逻辑(第二种:父组件更新,触发的子组件更新)
 1. 触发 componentWillReceiveProps 周期函数: 接受最新属性之前

        周期函数本身是不安全的

        UNSAFA_componentWillReceiveProps(nextProps) {
           // this.props:存储之前的属性
           // nextProps:传递进来的最新属性值
          console.log('componentWillReceiveProps', this.props, nextProps);
    }

2. 触发 shouldComponentUpdate 周期函数
    ....
组件卸载的逻辑

        1. 触发 componentWillUnmount 周期函数:组件销毁之前
        2. 销毁
   

父子组件嵌套,处理机制上遵循深度优先原则: 父组件在操作中,遇到子组件,一定是把子组件处理完,父组件才能继续处理

        父组件第一次渲染

                父willMount -> 父render (子 willMount -> 子 render -> 子didMount) -> 父didMount

        父组件更新:

                父 shouldUpdate -> 父 willUpdate -> 父 render (子 shouldUpdate -> 子 willUpdate -> 子 render -> 子 didUpdate ) -> 父 didUpdate

        父组件销毁:

                 父 WillUnmount -> 处理中(子willUnmount -> 子销毁) -> 父销毁

import React from 'react'
import PropTypes from 'prop-types'

class Vote extends React.Component {
	/* 属性规则校验 */
	static defaultProps = {
		num: 0
	}
	static propTypes = {
		title: PropTypes.string.isRequired,
		num: PropTypes.number
	}
	
	constructor(props) {
		super(props)
		console.log(this.props)
	}
    
    /* 初始化状态 */
    state = {
        supNum: 10,
        oppNum: 5
    }

    render() {
        console.log('render:渲染')
        let { title } = this.props
        { supNum, oppNum } = this.state
        
        // let supNum = this.state.supNum
        
        return <div className="vote-box">
            <div className="header">
            	<h2 className="title">标题</h2>
                <span>15人</span>
            </div>
            <div className="main">
            	<p>支持人数:{supNum}人</p>
                <p>反对人数:{oppNum}人</p>
            </div>
            <div className="footer">
            	<button onClick={() => {
                        this.setState({
                            supNum: supNum + 1
                        });
                    }}>支持</button>
                
                <button onClick={() => {
                        this.state.oppNum++;
                        this.forceUpdate()
                    }}>反对</button>
            </div>
        </div>
    }
    UNSAFE_componentWillMount() {
        console.log('componentWillMount:第一次渲染之前')
    }
    
    componentDidMount() {
        console.log('componentDidMount:第一次渲染完毕')
    }
    
    shouldComponentUpdate(nextProps, nextState) {
        // nextState:存储要修改的最新状态
        // this.state:存储的还是修改前的状态(此时状态还没有改变)
        console.log(this.state, nextState)
        
        // 此周期函数需要返回true/false
        //    返回true:允许更新,会继续执行下一个操作
        //    返回false:不允许更新,接下来啥都不处理
        return true
    } 
    
    UNSAFA_componentWillUpdate(nextProps, nextState) {
        console.log('componentWillUpdate:', this.state, nextState);
    }
    
    componentDidUpdate() {
        console.log('componentDidUpdate:组件更新完毕')
    } 
    
    UNSAFA_componentWillReceiveProps(nextProps) {
        console.log('componentWillReceiveProps', this.props, nextProps);
    }
}
export default Vote;

 函数组件是静态组件

  • 组件第一次渲染完毕后,无法基于”内部的某些操作“让组件更新;但是,如果调用它的父组件更新的,那么相关的子组件也一定会更新(可能传递最新的属性值进来);
  • 函数组件具备:属性... (其他状态等内容几乎没有)
  • 优势:比类组件处理的机制简单,这样导致函数组件渲染速度更快!!
  • 弊端:静态组件,无法实现组件动态更新

 类组件是动态组件

  • 组件在第一次渲染完毕后,除了父组件更新可以触发其更新,我们可以通过:this.setState修改状态 或者 this.forceUpdate 让组件实现”自更新“!!
  • 类组件具备:属性、状态、周期函数、ref... (几乎组件应该有的东西它都具备)
  • 优势:功能强大!!

在实际应用时,我们需要根据需求,选择使用函数组件或者类组件  

Hooks组件(推荐):具备了函数组件和类组件各自优势,在函数组件基础上,基于hooks函数,让函数组件也可以拥有状态、周期函数等,让函数组件也可以实现自更新(动态化)!!

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值