React组件化开发[函数组件和类组件]

组件化开发的优势

  • 利于团队协作开发

  • 利于组件复用

  • 利于SPA单页面应用开发

  • ……

React中的组件化开发:

没有明确全局和局部的概念「可以理解为都是局部组件,不过可以把组件注册到React上,这样每个组件中只要导入React即可使用」

  1. 函数组件

  1. 类组件

  1. Hooks组件:在函数组件中使用React Hooks函数

函数组件

// views/FunctionComponent.jsx
const FunctionComponent = function FunctionComponent() {
    return <div>
        我是函数组件
    </div>;
};
export default FunctionComponent;
// index.jsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import FunctionComponent from '@/views/FunctionComponent';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <>
    <FunctionComponent/>
  </>
);

设置私有属性

// 赋值属性默认值
FunctionComponent.defaultProps = {
    x: 0,
    title: '',
    arr: [],
    num: 0
};
// 设置属性规则
FunctionComponent.propTypes = {
    x: PropTypes.number,
    title: PropTypes.string.isRequired,
    arr: PropTypes.array,
    num: PropTypes.any
};

props:获取传递的属性和children,它是被冻结的,不能新增/修改/删除...

但是我们可以给传递的属性做规则校验[是否为必传、默认值、类型]

+ 给函数设置静态的私有属性:defaultProps、propTypes...

+ 设置其他规则,需要依赖官方的插件:import PropTypes from "prop-types";

+ 如果规则校验没有通过。则控制台会保持警告,但是不影响属性的获取

https://www.npmjs.com/package/prop-types

实现具名插槽

// index.jsx
root.render(
  <>
    <FunctionComponent>
      <div className='slot-box' slot="head">
        我是插槽信息1
      </div>
      <div className='slot-box' slot="foot">
        我是插槽信息2
      </div>
    </FunctionComponent>
  </>
);

方法一

// views/FunctionComponent.jsx
import React from "react";
const FunctionComponent = function FunctionComponent(props) {
  let children = React.Children.toArray(props.children),
    headSlots = children.filter(item => item.props.slot === 'head'),
    footSlots = children.filter(item => item.props.slot === 'foot');
  return <div>
    {headSlots}
    我是组件内容
    {footSlots}
  </div>;
};
export default FunctionComponent;

props.children的值可能没有,或者是一个值,再或者是一个数组,为了方便处理,我们可以把其转换为数组

基于React.Children对象中提供的方法,处理传递的children属性值

+ toArray

+ forEach/map ->React.Children.forEach(children,()=>{})

+ count

+ ...

方法二

在当下前端开发中,组件化开发是必然的,所以我们一定要具备"抽离、分析、封装"组件的能力,而且需要让组件具备很强的"复用性"[满足更多的需求]

设计开发中这款组件的时候,需要具备很多"不确定性"

+ 需要调用组件的时候告诉组件,这样组件内部才能基于传递的不同信息,呈现出不同的效果

+ 主要就是基于 "属性和插槽"给组件传递信息

+ 如果需要传递一些数据值,则基于不同的属性传递即可,组件内部接受属性且做规则校验!!

+ 如果需要传递一些更复杂的内容(比如DOM元素),则基于插槽把内容传递进来!!

所以属性和插槽是用来增强一个组件的复用性的!!

函数组件是静态组件

  • 不具备状态、生命周期函数、ref等内容

  • 第一次渲染完毕,除非父组件控制其重新渲染,否则内容不会再更新

  • 优势:渲染速度快

  • 弊端:静态组件,无法实现组件动态更新

const Demo = function Demo(props) {
  console.log("R");
  let num = 0
  return <div className="demo=box">
    <span>{num}</span>
    ----
    <span>{props.x}</span>
    <br />
    <button onClick={() => {
    num++
    console.log(num);
  }

    }>累加</button>
  </div >
}
export default Demo;
函数组件原来:只有第一次调用了Demo函数组件,后来点击的都是onClick函数,所以Demo不会再次创建虚拟DOM,所以也不会更新渲染,所以它就是静态组件

const fn = function () {
    let x = 10;
    return function () {
        x++;
        console.log(x);
    }
}
let f = fn();//fn只执行了一次,其余都是f()执行
f();//11
f();//12 

默认情况下,函数组件是一个静态组件:组件第一次渲染完毕后,组件无法进行更新,这样渲染的内容也就无法进行改变

原因:函数组件的每一次渲染和更新,都是要把函数重新执行,[产生新的闭包&重新编译出新的 VirtualDMO];默认情况下,函数第一次执行完毕后,我们没有办法基于内部的某些操作,让函数 可以重新执行(只能是修改第一次闭包中的先关信息),所以也就导致了函数组件无法更新!

如果我们想让函数组件更新,[静态组件动态化],需要怎么做:

@1 找一个可以让函数组件重新执行的办法即可,==>React中的Hooks函数

@2 如果让父组件更新,这样对应的子组件也会更新[传递最新的属性属性值进来]

import React from 'react';
import ReactDOM from 'react-dom/client';
import Demo from './Demo3';

let x = 100
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <Demo x={x}></Demo>
)
setInterval(() => {
  x += 100
  root.render(
    <Demo x={x}></Demo>
  )
}, 1000)

类组件

生命周期函数[俗称,钩子函数]

在一段程序,完整流程内的某个阶段,我们可以单独做一些事情:这些函数是框架内部提供的,我们只需要在指定的函数内部做自己要做的事,框架在处理中,会帮我们把对象的函数执行

类组件:new 构造函数执行,创建类组件的实例,并且把含包含children的props作为实参传递进去[传递到构造函数中]

+ getDefaultProps 初始化属性[含规则校验]

+ 执行contruction

+ gteInitalState 初始化状态[把视图中需要的数据,放在this.state中==>构建Model数据层]

+ [钩子] 执行componrntWillMount[第一次渲染之前]

注意:此钩子函数是不安全的,未来可能要被移除,如果一点要使用,前面好设置UNSAFE(UNSAFE_componentWillMount),但是是在React.StrictMode模式下,会抛出警告错误

+ [钩子] 执行render[组件渲染:所以我们需要把组件的视图放在render函数中返回]

+ [钩子] 执行componentDidMount[第一次渲染完毕(可以获取到真实的DOM元素)]

+ 此时可以获取到真实的DOM元素了

+ 还可以在此处异步获取服务器数据,然后让组件更新渲染真实的内容!!原本应该在 componentWillMount中获取服务器数据,没有渲染就获取数据,提高性能,但是因为 不 安全,也可以在constructor中,初试完数据后获取数据,但是函数组件没有constructor

+ 有些需求还需要设置定时器/监听器,在或者用一些其他的插件等

+ ....

附:

两种导出方式

exports.func = function func() {

    }
exprots.sum= function sum(){
  
}
    // module.exports = {
    //     func: function func() {

//     }
// } 
let Tc = require("./test");//Commonjs
console.log(Tc);


import Tc from './test';//ES6
console.log(Tc);

import {sum,func} from './text'

创建一个类,并继承React.Component/PureComponent,且具备render这个钩子函数,用来构建view视图!!

import { Component } from "react";

class Demo extends Component {
    //this->创建的实例
    /* 初始化属性 */
    static defaultProps = {//静态私有属性
        name: "",
        age: 18
    }
   
    render() {
        return <div className="demo-box" >

        </div>
    }
}
export default Demo

constructor:

如果写了constructor 第一行一定要写super[class做了继承后的语法要求]

super()把父类(Component)中私有的继承给子类私有[类似于class继承]

React.Component.call(this)把父类当做普通函数执行,this指向子类的实例

this.props=props;
this.context=context;
this.refs={};
this.updater=...;

处理完毕后,this实例上就挂载了4个属性

可以在constructor中初始化状态

constructor如果不写,则:

+ React内部会把四个属性,挂载到this实例上[且都是赋好值的]

+ 可以直接在外面,基于state={...}初始化状态

总结super的原理; 把父类Component当做普通函数执行,让this变成子类的实例,让实例上挂载4个属性,super传了props就是挂载到实例上(this.props)

// constructor(props) {
//         //经过初始化后的属性对象
//         super(props)//this.props=props
//         // console.log(props, this.props);
//         this.state = {
//             num: 10
//         }
//     }

    /* 初始状态 */
state = {
        num: 10
    }

处理 componentWillMount安全性

// componentWillMount() {
    //     console.log("componentWillMount");
    // }
    UNSAFE_componentWillMount() {//解除警告
        console.log("componentWillMount");
    }
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <Demo name="zxc"></Demo>
  </React.StrictMode>

)

类组件更新

如果在类组件内部,基于某些操作,让组件更新

  1. setState 修改状态[内部]

+ [钩子] 执行shouldComponentUpdate 是否允许更新返回TRUE则继续,返回FALSE则结束

在此阶段,THIS上的属性和状态还未更改

+ [钩子] 执行componentWillUpdate[不安全的] 更新之前

修改THIS上的属性和状态信息

+ [钩子] 执行render

+ [钩子] 执行componentDidUpdate

+ 触发setState中的callback执行

    /* 初始状态 */
    state = {
        num: 10
    };
    // componentWillMount() {
    //     console.log("componentWillMount");
    // }
    shouldComponentUpdate() {
        console.log("shouldComponentUpdate");
        return true

    }
    UNSAFE_componentWillUpdate() {//解除警告
        console.log("componentWillUpdate");
    }
    componentDidUpdate() {
        console.log("componentDidUpdate");
        //第一次渲染结束
          //只要视图更新,它就会执行,和setState里面的回调函数不一样,callback只有在上面第一个参数更新以后,才会执行。如果有100个状态,我只需要其中一个更新,只能用callback
    }

    render() {
        console.log("render");
        let { num } = this.state
        return <div className="demo-box" >
            {num}
            <button onClick={() => {
                /* 类组件中,最常用的修改状态的办法:setState(partialState,callback)
                    + partialState:支持部分状态更改
                    + callback:在视图更新完毕之后触发执行,晚于DidUpdate 即是shouldComponentUpdate返回的是false(不允许更新),这个callback也会被执行

                */

                this.setState({
                    num: num + 1
                }, () => {
                    console.log("setSate");
                })
            }}>按钮</button>
        </div>
    }
}
  1. forceUpdate 强制更新

会直接跳过shouldComponentUpdate函数,强制视图更新

+ [钩子] 执行componentWillUpdate

+ [钩子] 执行render

+ [钩子] 执行componentDidUpdate 更新完毕

+ 触发forceUpdate中的callback执行

    /* 初始状态 */
    state = {
        num: 10
    };
    // componentWillMount() {
    //     console.log("componentWillMount");
    // }
    shouldComponentUpdate() {
        console.log("shouldComponentUpdate");
    }
    UNSAFE_componentWillUpdate() {//解除警告
        console.log("componentWillUpdate");
    }
    componentDidUpdate() {
        console.log("componentDidUpdate");
        //第一次渲染结束
    }

    render() {
        console.log("render");
        let { num } = this.state
        return <div className="demo-box" >
            {num}
            <button onClick={() => {
                this.state.num++;//这样写,只是单纯修改了状态值,但是没有让视图更新[React中,修改状态且让视图更新,需要基于特点的方法处理]
                this.forceUpdate(() => {
                    //callback:在视图更新完毕触发执行,要晚于DidUpdate 
                    console.log("forceUpdate");
                })
            }}>按钮</button>
        </div>
    }
}

---------

如果是父组件更新,则子组件也是更新的[外部]

+ [钩子] 执行componentWillReceiveProps[不安全]

+ [钩子] 执行shouldComponentUpdate

+ ....

---------

如果组件销毁

+ [钩子] 执行componentWillUnmpunt

销毁组件

React组件分类

  • 函数组件

  • 不具备“状态、ref、周期函数”等内容,第一次渲染完毕后,无法基于组件内部的操作来控制其更新,因此称之为静态组件!

  • 但是具备属性及插槽,父组件可以控制其重新渲染!

  • 渲染流程简单,渲染速度较快!

  • 基于FP(函数式编程)思想设计,提供更细粒度的逻辑组织和复用!

  • 类组件

  • 具备“状态、ref、周期函数、属性、插槽”等内容,可以灵活的控制组件更新,基于钩子函数也可灵活掌控不同阶段处理不同的事情!

  • 渲染流程繁琐,渲染速度相对较慢!

  • 基于OOP(面向对象编程)思想设计,更方便实现继承等!

React Hooks 组件,就是基于 React 中新提供的 Hook 函数,可以让函数组件动态化!

两种组件的渲染机制

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值