React学习笔记2


js中 class类

class Parent {
    // new 的时候,执行的构造函数(可写可不写:需要接收传进来的实参信息,才需要constructor)
    constructor(x,y){
        // this -> 创建的实例
        this.total = x + y
    }
    num = 2000 // 等价于 this.num=2000 给实例设置私有属性
    getNum = ()=>{
        // 箭头函数没有自己的this,所用的this是宿主环境中的
        console.log(this) // this -> 当前创建的实例
    }
    sum(){
        // 类似于 sum = function sum(){}
        // 它是给Parent.prototype上设置公共方法 [sum是不可枚举的] 
    }
    
    // 把构造函数当作一个普通对象,为其设置静态的私有属性方法 Parent.xxx
    static avg = 1000;
    static average(){
        
    }
}

// 在外部手动给构造函数原型上设置公共的属性
Parent.prototype.y = 2000

let p = new Parent(10,20)
console.log(p)
console.dir(Parent)

React中的组件

函数组件是静态组件

  • 组件第一次渲染完毕后,无法基于内部的某些操作 让组件更新【无法实现自更新】,但是,如果调用它的父组件更新了,那么调用它的子组件一定会更新【可能传递最新的属性值来】
  • 函数组件具备:属性 【其他状态等都没有】
  • 优势:比类组件处理机制简单,导致函数组件渲染速度更快

类组件是动态组件:

  • 组件在第一次渲染完毕后,除了父组件更新可以触发其更新外,还可以通过:this.setState 修改状态,或者this.forceUpdate 等方式让组件实现【自更新】
  • 类组件具备:属性、状态、周期函数、ref … 【几乎组件应该有的都具备】
  • 优势:功能强大

Hooks组件:【推荐】

  • 具备了函数组件和类组件各自的优势,在函数组件的基础上,基于hooks函数,让函数组件也可以拥有状态、周期函数等,让函数组件也可以实现自更新【动态化】

React中的类组件

index.jsx 主页面

import React from 'react'
import ReactDOM from 'react-dom/client'
import Vote from './Vote'
const root = ReactDOM.creatRoot(document.getElementById('root'))
root.render(
    <>
        <Vote title="学习React" />
    </>
)

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

  • 字符串:创建一个标签

  • 普通函数:把函数执行,并把props传递给函数

  • 构造函数:把贵族凹函数基于new执行【也就是创建类的一个实例】,也会把解析出来的props传递过去

    • 没第哦啊用一次类组件都会创建一个单独的实例

    • 把在类组件中编写的render函数执行,把返回的jsx 【virtualDom】当作组件视图进行渲染

      例如:new Vote({ title:’学习React’ })

Vote.jsx 组件

创建类组件

0、创建一个构造函数(类)

  • 要求必须即继承React.Component / PureComponent 这个类
  • 我们习惯于使用ES6中的class创建类 [ 因为方便 ]
  • 必须给当前类设置一个render的方法 [ 放在其原型上 ] : 在render方法中,返回需要渲染的视图

从调用类组件 [ new Vote({…}) ] 开始,类组件内部发生的事情

    • 初始化属性 && 规则校验

      先进行规则校验,校验完毕后,再处理属性的其他操作

      • 方案一

        constructor(props){

        ​ super(props) // 会把传递过来的属性挂载到this实例上

        ​ console.log(this.props) // 获取到传递的属性

        }

      • 方案二

        即使不在constructor中处理【或者constructor都没写】,在constructor处理完毕后,React内部也会把传递的props挂载到实例上;所以在其他的函数中,只要保证this是实例,就可以基于this.props获取传递的属性

        • 同样this.props获取的属性对象也是被冻结的【只读】,Object.isFrozen(this.props) -> true
    • 初始化状态

      状态:后期修改状态,可以触发视图的更新

      需要手动初始化,如果没有进行相关的处理,则默认会往实力上挂载一个state,初始值是null,即this.tate=null

      手动处理:state = { … }

    • 修改状态,控制视图更新

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

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

      • this.setState(partialState) 既可以修改状态,也可以让视图更新 【推荐】

        partialState 部分状态

        this.setState({ xxx:xxx,})

      • this.forceUpdate() 强制更新

  1. 触发 componentWillMount 周期函数 :在组件第一次渲染之前

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

    • componentWillMount 触发 在组件第一次渲染之前

      • 目前是不安全的【虽然可以用,但是未来可能要被移除,不建议使用】

        使用时会抛出黄色警告,可以暂时使用 UNSAFE_componentWillMount 周期函数

      • 如果开启了React.strictMode【React的严格模式】,则 使用 UNSAFE_componentWillMount 会抛出红色警告错误

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

        ”use strict“ js的严格模式

  2. 触发render 周期函数:渲染

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

    • 已经把 virtualDom 渲染成真是DOM 【意味着可以获取真是dom了】

组件更新的逻辑 【当改变了相关状态,组件会更新】

  1. 触发 shouldComponentUpdate 周期函数:返回 true / false是否允许更新
  2. 触发 componentWillUpdate 周期函数:更新之前
    • 此周期函数也是不安全的
    • 在这个阶段状态还没有改变
  3. 修改状态值 / 属性值 【让this.state.xxx成为最新的值】
  4. 触发 render 周期函数:组件更新
    • 按照最新的状态 / 属性,把JSX编译为virtualDom
    • 和上次渲染出来的virtualDom进行对比 【DOM-DIFF】
    • 把差异的部分进行渲染【渲染真实的dom】
  5. 触发 componentDidUpdate 周期函数:组件更新完毕

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

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

  • 父组件第一次渲染

    父 willMount -> 父render 【子 willMount-> 子render-> 子didMount 】-> 父didMount

  • 父组件更新

    父 shouldUpdate-> 父willUpdate-> 父render -> 【子 willReceiveProps -> 子shouldUpdate -> 子willUpdate -> 子render ->子didUpdate】 -> 父didUpdate

组件销毁的逻辑

  • 触发componentWillUnmount 周期函数:组件销毁之前
  • 销毁
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:20,
        oppNum:10
    }
    
    
    render(){
        let {title} = this.props,
            {supNum,oppNum} = this.state
        return (
            <div className="vote-box">
                <div className="header">
                    <h2>{title}</h2>
                    <span>{supNum + oppNum}</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.forceUpdate()
                         }}>反对</button>
                 </div>
            </div>
        )
    }
    
    componentWillMount(){ // UNSAFE_
        console.log('componentWillMount:第一次渲染之前')
    }
    
    componentDidMount(){
        console.log('componentDidMount:第一次渲染完成')
    }
    
    shouldComponentUpdate(nextProps,nextState){
        // nextState:存储要修改的最新状态
        // this.state:存储的还是修改前的状态【此时状态还没有改变】
        console.log(this.state,nextState)
        
        // 此周期函数需要返回 true / false
        // true:允许更新,会执行下一个操作
        // false:不允许更新,接下来啥都不处理,阻断操作
        return true
    }
    
    componentWillUpdate(){ // UNSAFE_
         console.log('componentWillUpdate:组件更新之前')
    }
    
    componentDidUpdate(){
         console.log('componentDidUpdate:组件更新完成')
    }
    
    componentWillReceiveProps(nextProps){  // UNSAFE_
         console.log('componentWillReceiveProps:接收最新属性之前',this.props,nextProps)
    }
    
    componentWillUnmount(){
        console.log('componentWillUnmount:组件销毁之前')
    }
    
}

export default Vote

Demo.jsx

PureComponent 与 Component 的区别

PureComponent 会给类组件加一个shouldComponentUpdate【内置,代码加会报错】 周期函数

  • 在次周期函数内 它对新老属性、状态进行浅比较
  • 如果经过浅比较,发现属性和状态没有改变,返回false 【也就是不会更新组件】,返回true【更新组件】
class Demo extends React.PureComponent{
    state = {
        arr : [10,20,30]	
    }
    render (
        <div>
            {
               arr.map((item,index)=>{
                   return <span key={index}>{item}</span>
               })
            }
            <br />
            <button onClick={
                        ()=>{
                            arr.push(40)
                            this.setState({
                                arr  // 最新修改的状态地址不变 PureComponent 浅比较下 组件不更新
                            })
                        }
                    }></button> 
        </div>
    )
    
    // PureComponent 内置钩子,代码加会报错
    shouldComponentUpdate(nextProps,nextState){
        let {props,state} = this
        // props / state : 修改之前的属性状态
        // nextProps / nextState : 将要修改的属性状态
        return !shallowEqual(props,nextProps) || !shallowEqual(state,nextState)
    }
    
}

浅比较

浅比较只比较对象的第一级,对于深层次的内容不会继续宁比较

先比较对象成员的数量,如果不一致,则两个对象肯定不一致

判断属性key是否两个对象都存在,另一方不存在则对象肯定不一致

对比两个属性的值是否一致

【注意:直接对比 NaN === NaN 为false ,Object.is(NaN,NaN) 为true】

// 检测是否为对象 且 不为null
const isObject = function isObject(obj){
    return obj !== null && /^(object|function)$/.test(typeof obj)
}

// 对象浅比较的方法
const shallowEqual = function shallowEqual(objA,objB){
    // 有一方不是对象 返回false
    if(!isObject(objA) || !isObject(objB)) return false
    
    // 两方对象引用地址一致 返回true
    if( objA === objB ) return true
    
    let keysA = Reflect.ownKeys(objA),
        keysB = Reflect.ownKeys(objB);
    
    // 属性数量不一致 返回false
    if(keysA.length !== keysB.length) return false
    
    // 数量一致,比较内部成员【只比较第一级】 for可以随时退出循环
    for(let i=0;i<keysA.length;i++){
        let key = keysA[i]
        // objB不存在objA的属性 或 属性都存在但是值不相等 返回false
        if(!objB.hasOwnProperty(key) || !Object.is(objA[key],objB[key]) ) return false
    }

    // 以上处理完成,没有发现不相同的成员,则认为两个对象是相等的【浅比较】
    return true
}
let obj = { z : 20 }  // 0X001
let objA = {
    X:10,
    Y:obj,  // 0X001
    ARR:[10,20,30]  // 0X002
}
OBJ.N = 999
let objb = {
    X:10,
    Y:obj, // 0X001
    ARR:[10,20,30] // 0X003
}
// 改变属性进行对象比较
console.log(shallowEqual(objA,objB))

文章作者: 何不去高出
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 何不去高出 !
  目录