React 的学习笔记一 (未完结)

发布时间 2023-03-22 21:16:18作者: __fairy

一、React 是什么

 React 是一个声明式,高效且灵活的用于构建用户界面的 JavaScript 库。使用 React 可以将一些简短、独立的代码片段组合成复杂的 UI 界面,这些代码片段被称作“组件”。

1.1、React的优点

  • 采用组件化模式,声明式编程( react是面向数据编程,不需要直接去控制dom,你只要把数据操作好,react自己会去帮你操作dom,可以节省很多操作dom的代码。这就是声明式开发),提高开发效率与组件复用率;

  • 在 React Native 中可以使用 React 语法进行移动端开发;
  • 通过虚拟DOM + Diffing算法,最大限度的减少与DOM的交互,速度很快;

  • 使用JSX,代码可读性很好;

  • React 可以与已有的库或者框架很好的配合;

  • 使用 React 构建组件使代码更加容易得到复用,可以很好的应用在大项目的开发过程中;

  • 其单向数据流减少了重复的代码,轻松实现数据绑定。

1.2、React的缺点

  • React 不适合单独做一个完整的框架,做大型项目需要和其他框架组合使用

  • React 的库非常庞大,新手很难理解需要花费一定的时间,使用内联模板和JSX,使编码变得复杂

1.3、React 与 Vue 的区别

理念差异:
从设计理念上来说,vue更像是一种模板引擎。所谓模板引擎就是在静态模板上动态替换 Js 变量,最终渲染出页面。一个vue文件中,有html、css、js,放在一起。很直观,html 是静态模板,js 是动态变量。vue 在模板引擎的基础上加了很多 v 指令,大大简化了开发。但是当 js 逻辑很复杂的时候,状态管理变得很麻烦。

而对于React来说,React受函数式编程的影响,将 html 视为数据映射的结果,一个数据映射一个 html ,组合起来就是一个完整的页面DOM树。这则是区别于“ 模板思维”的“映射思维”。这是React与Vue最核心的差异。

对于 vue 来说,html 是首位,vue 中的 html 模板已经算是一个近乎完整的页面,只不过有几个变量无法确定,再把 js 加上去就好。而对于 React 来说,Js 是首位,有 Js 才有 html,Js 是因,html 是果。

共同点:
虚拟Dom:改变真实的DOM状态远比改变一个JavaScript对象的花销要大得多。 Virtual DOM是一个映射真实 DOM 的 JavaScript 对象,如果需要改变任何元素的状态,那么是先在 Virtual DOM 上进行改变,而不是直接改变真实的 DOM。当有变化产生时,一个新的 Virtual DOM 对象会被创建并计算新旧 Virtual DOM 之间的差别。之后这些差别会应用在真实的 DOM上。这极大的提高了性能。

组件化:vue 和 react 都建议将应用拆分成多个组件,每个组件负责不同的工作,组件之间通过特殊的方式相互关联。使应用的结构变得清晰,也有利于组件复用。

区别:
Vue 建议使用 html 模板进行编写,而 React 则使用了Javascript 的扩展语法 —— JSX,赋予了开发者许多变成能力。
React 应用中的状态是(React)关键的概念。也有一些配套框架被设计为管理一个大的 state 对象,如 Redux。此外,state 对象在 React 应用中是不可变的,意味着它不能被直接改变(这也许不一定正确)在 React 中你需要使用 setState() 方法去更新状态。

在 vue中,数据由 data 属性在 Vue 对象中进行管理。data 参数就是应用中数据的保存者。 对于管理大型应用中的状态这一话题而言,Vue 解决方案适用于小型应用,但对于对于大型应用而言不太适合。

第一标题的原文链接:https://blog.csdn.net/qq_45643079/article/details/119914443

二、React 的实现

第一次渲染数据的时候,会在Virtual-Dom里面进行比较,如果没有找到相同 DOM 就会创建一个新的虚拟DOM,然后就渲染到页面真实 DOM 重排页面元素,如果对比有相同虚拟DOM 就会复用该虚拟DOM,页面元素不会重排。

原本数组里面有2条数据,展示后虚拟 DOM 就会创建这两个数据<li>的虚拟DOM ,此时添加一条新的数据,这时候数组就会有三条数据,前面两条数据在第一次对比的时候就已经创建虚拟DOM了,前两条数据对比相同就不会渲染,而新添加的第三条数据,对比内存没有相对应的虚拟DOM,则创建新的虚拟DOM ,再重新渲染到页面上!

2.0、创建 react 项目

2.0.1、使用脚手架创建

创建命令:

npx create-react-app 项目名

2.0.2、使用 vite 创建

创建命令:

npm create vite@latest

选择 React !!!

2.0.3、react脚手架项目结构

public ---- 静态资源文件夹

favicon.icon ------ 网站页签图标

index.html -------- 主页面

logo192.png ------- logo图

logo512.png ------- logo图

manifest.json ----- 应用加壳的配置文件

robots.txt -------- 爬虫协议文件

src ---- 源码文件夹

App.css -------- App组件的样式

App.js --------- App组件

App.test.js ---- 用于给App做测试

index.css ------ 样式

index.js ------- 入口文件

logo.svg ------- logo图

reportWebVitals.js --- 页面性能分析文件(需要web-vitals库的支持)

setupTests.js ---- 组件单元测试的文件(需要jest-dom库的支持)

2.1、使用 react 输出 “ hello,react ”  案例

实现代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>hello-react</title>
</head>
<body>

    <!-- 容器 -->
    <div id="test"></div>

    <!-- 引用的时候,一定要注意先后顺序!!! -->
    <!-- react:react核心库 -->
    <script type="text/javascript" src="./js/react.development.js"></script>
    <!-- react-dom:用于支持react操作DOM元素 -->
    <script type="text/javascript" src="./js/react-dom.development.js"></script>
    <!-- babel:用于将jsx转换为js -->
    <script type="text/javascript" src="./js/babel.min.js"></script>

    <!-- 请注意!! 这里一定要写babel -->
    <script type="text/babel">
        // 1、创建虚拟DOM
        const VDDM = <h1>hello,react</h1> /* 这里一定不要写单引号,因为不是字符串 */
        // 2、渲染虚拟DOM到页面里,使用ReactDOM.render(虚拟DOM,容器);
        ReactDOM.render(VDDM,document.getElementById("test"));
        // 请注意,我想再这个容器里面再写一句话
        // const VDDM2 = <h1>hello,react</h1>
        // ReactDOM.render(VDDM2,document.getElementById("test")); // 这个是失败的,因为它不是追加,而是覆盖

    </script>
</body>
</html>

运行结果:

 2.2、虚拟DOM 的两种创建方法,并使用多级层次结构

2.2.1、使用 jsx 来创建虚拟 DOM

实现代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title></title>
</head>
<body>

    <!-- 容器 -->
    <div id="test"></div>

    <!-- 引用的时候,一定要注意先后顺序!!! -->
    <!-- react:react核心库 -->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!-- react-dom:用于支持react操作DOM元素 -->
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <!-- babel:用于将jsx转换为js -->
    <script type="text/javascript" src="../js/babel.min.js"></script>

    <!-- 请注意!! 这里一定要写babel -->
    <script type="text/babel">
        // 1、创建虚拟DOM
        const VDDM = (
            <h1 id="title">
                <span>hello,react</span>
            </h1>
        ) /* 这里一定不要写单引号,因为不是字符串 */
        // 2、渲染虚拟DOM到页面里,使用ReactDOM.render(虚拟DOM,容器);
        ReactDOM.render(VDDM,document.getElementById("test"));

    </script>
</body>
</html>

2.2.2、使用 js 来创建虚拟 DOM

实现代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title></title>
</head>
<body>

    <!-- 容器 -->
    <div id="test"></div>

    <!-- 引用的时候,一定要注意先后顺序!!! -->
    <!-- react:react核心库 -->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!-- react-dom:用于支持react操作DOM元素 -->
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <!-- 不需要引入 babel -->

    <!-- 请注意!! 这里改为javascipt -->
    <script type="text/javascript">
        // 1、创建虚拟DOM,创建节点:React.createElement("标签名","标签属性","标签内容")
        const VDDM = React.createElement("h1",{id: "title",class: "titles"},React.createElement("span",{},"Hello,React"));
        // 2、渲染虚拟DOM到页面里,使用ReactDOM.render(虚拟DOM,容器);
        ReactDOM.render(VDDM,document.getElementById("test"));

    </script>
</body>
</html>

运行结果:

 2.3、虚拟DOM与真实DOM

虚拟DOM:

const VDDM = (
            <h1 id="title">
                <span>hello,react</span>
            </h1>
  )
真实DOM:
document.getElementById("test");
关于虚拟DOM:
  • 1.本质是Object类型的对象(是一般对象)

  • 2.虚拟DOM比较“轻”,真实DOM比较“重”,因为虚拟DOM是React内部在用,无需真实DOM上那么多的属性。

  •  3.虚拟DOM最终会被React转换为真实DOM,呈现在页面上。

  • 4.它是运行在内存里面的。

2.4、jsx 概要

JSX 是 Javascript 的一种语法拓展

JSX是 JavaScript XML 简写,表示在 JavaScript 中编写XML格式代码(也就是HTML格式)

2.4.1、jsx的优点

    • 声明式语法更加直观

    • 与HTML结构相同

    • 降低了学习成本、提升开发效率

注意:JSX 并不是标准的 JS 语法,是 JS 的语法扩展,浏览器默认是不识别的,脚手架中内置的 @babel/plugin-transform-react-jsx 包,用来解析该语法的!

2.4.2、jsx语法规则

1.定义虚拟DOM时,不要写引号;

2.标签中混入JS表达式时使用{};

3.样式的类名指定不要用class,要用className,属性名采用驼峰命名法 class -> className,for -> htmlFor

4.内联样式,要用style={{ key:value }}的形式去写

5.虚拟DOM只有一个根标签,如果没有根节点,可以使用 <></>(幽灵节点)替代

6.标签必须闭合(input、img标签)成对闭合或者自闭合都可以

7.标签首字母

   (1)如果小写字母开头,则将改标签转为html中的同名元素,如果html中没有该标签对应的同名元素,则报错

    (2)如果大写字母开头,react就去渲染对应的组件,如果组件没有定义,则报错

8.JSX支持多行(换行),如果需要换行,需使用() 包裹,防止bug出现

2.4.3、jsx的练习 — 动态展示列表数据

2.4.3.1、区分js语句(例如 if 判断、for 循环)与 js 表达式

1.表达式:一个表达式会产生一个值,可以放在任何一个需要值的地方

                    表达式栗子:

                        以下的表达式都可以用一个变量来接收它返回的值

                        (1)a  // 这个可以是一个变量名

                        (2)a+b  // 加法表达式

                        (3)demo(1)  // 这个是函数调用表达式

                        (4)arr.map()

                        (5)function.test(){}

2.语句:控制代码走向,并没有返回值

                    语句的栗子:

                        (1)if(){}

                        (2)for(){}

                        (3)switch(){case: xxx}

2.4.3.2、代码实现

 代码如下:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>1</title>
  </head>
  <body>
    <!-- 容器 -->
    <div id="test"></div>

    <!-- react:react核心库 -->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!-- react-dom:用于支持react操作DOM元素 -->
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <!-- babel:用于将jsx转换为js -->
    <script type="text/javascript" src="../js/babel.min.js"></script>

    <script type="text/babel">  
        // 模拟后端传过来的数据
        const data = ['Angular','React','Vue']
        // 创建虚拟DOM
        const VDOM = (
            <div>
                <h1>前端js框架列表</h1>
                <ul>
                    {
                        // 加工数组,key是虚拟DOM的唯一标识符
                        data.map((item,index)=>{
                            return <li key={index}>{item}</li>
                        })
                    }     
                </ul>
            </div>
        )

        // 渲染
        ReactDOM.render(VDOM,document.getElementById("test"))

    </script>
  </body>
</html>

 运行结果:

 三、组件(Component)

 如果我们将一个页面中所有的处理逻辑全部放在一起,处理起来就会变得非常复杂,而且不利于后续的管理以及扩展,但如果,我们将一个页面拆分成一个个小的功能块,每个功能块完成属于自己这部分独立的功能,那么之后整个页面的管理和维护就变得非常容易了。如果我们将一个个功能块拆分后,就可以像搭建积木一下来搭建我们的项目。它接受任意的入参(即 “props”),并返回用于描述页面展示内容的 React 元素。

  • 理解:用来实现局部功能效果的代码和资源的集合(html / css / js / image 等等)

  • 为什么要用组件: 一个界面的功能更复杂

  • 作用:复用编码, 简化项目编码, 提高运行效率

3.1、使用 class 定义组件,叫类样式组件

使用 class 定义组件需要满足两个条件:

  • class 继承自 React.Component

  • class 内部必须定义 render 方法,rende r方法返回代表该组件 UI 的 React 元素

栗子如下:

使用脚手架新建一个简易BBS项目,在这个项目中定义一个组件 PostList,用于展示BBS 的帖子列表;定义一个 PostItem,用于接收父组件传来的值,渲染到页面上

PostList 代码如下:

import React, { Component } from 'react'
import PostItem from './PostItem'

export class PostList extends Component {
  render() {
    const data = [
        {title: "重磅!党和国家机构改革有新进展",author: "小明",date: "2023-03-01"},
        {title: "美环保署前负责人:“毒火车”脱轨地区的环境显然有问题",author: "小虎",date: "2022-01-11"},
        {title: "中央网信办所属部分在京事业单位2023年度公开招聘公告",author: "小钰",date: "2021-02-12"},
        {title: "北京网络辟谣 互联网联合辟谣平台 网络辟谣标签工作专区",author: "小欧",date: "2011-12-12"}
    ]
    return (
      <>
        <h2>贴子列表</h2>
        {data.map(item=><PostItem title={item.title} author={item.author} date={item.date}></PostItem>)}
      </>
    )
  }
}

export default PostList
PostItem 代码如下:
import React, { Component } from 'react'

export class PostItem extends Component {
  render() {
    // 解构
    const {title,author,date} = this.props
    return (
      <>
        <ul>
            <li>
                <div>{title}</div>
                <div>
                    创建人:<span>{author}</span>
                </div>
                <div>
                    创建时间:<span>{date}</span>
                </div>
            </li>
        </ul>
      </>
    )
  }
}

export default PostItem

请注意:使用类样式组件接收父组件传过来的值,用 this.props 就可以直接渲染上去了!!!

3.2、使用函数定义组件,叫函数式组件

import React from "react";
import { Table, Popconfirm, InputNumber } from "antd";

// 函数式组件,在这里接收父组件传过来的值
export default function Tablesss(props) {
    return <h2>Hello {props.name}!</h2>;
}

请注意:使用函数式组件接收父组件传过来的值,需要在函数的参数里面用props来接收 !!!

3.3、组件的规范

  • 组件的名称必须首字母大写,react内部会根据这个来判断是组件还是普通的HTML标签

  • 函数组件必须有返回值,表示该组件的 UI 结构;如果不需要渲染任何内容,则返回 null

  • 组件就像 HTML 标签一样可以被渲染到页面中。组件表示的是一段结构内容,对于函数组件来说,渲染的内容是函数的返回值就是对应的内容

  • 使用函数名称作为组件标签名称,可以成对出现也可以自闭合

3.4、有状态组件与无状态组件

3.4.1、有状态组件

  1. 它属于一个 class 类

  2. 有继承

  3. 可以通过this来接收状态和属性

  4. 如果你想用react的生命周期,想对一些数据进行增删改查的话就要用到有状态组件

3.4.2、无状态组件

  1. 它属于一个函数

  2. 没有继承功能

  3. 没有生命周期

  4. 他的动态数据都是通过父组件传值子组件通过props接收渲染,针对一些简单的逻辑判断等等,用无状态组件

3.4.3、什么时候用有状态组件

  1. 在不确定或者确定要用到数据增删改查模块就可以 

  2. 在需要管理状态,或者需要使用生命周期时再用

3.4.4、注意

大部分建议使用无状态组件,因为如果大量用有状态的组件容易触发生命周期和钩子函数,页面会出面加载慢等问题!!!

四、组件三大核心属性

4.1、state

组件的 state 是组件内部的状态,state的变化最终将反映到组件UI的上。我们在组件的构造方法 constructor 中通过 this.state 定义组件的初始状态,并通过调用 this.setState 方法改变组件状态(也是改变组件状态的唯一方式,进而组件UI也会随之重新渲染。

理解:

  • state是组件对象最重要的属性, 值是对象(可以包含多个key-value的组合)

  • 组件被称为"状态机", 通过更新组件的state来更新对应的页面显示(重新渲染组件)

  • 组件中render方法中的this为组件实例对象

  • 组件自定义的方法中this为undefined,如何解决?

注意:

  • 组件中render方法中的this为组件实例对象

  • 组件自定义的方法中this为undefined,如何解决?

    • 强制绑定this: 通过函数对象的 bind()
    • 箭头函数 
  • 状态数据,不能直接修改或更新

4.1.1、this绑定

1、在 constructor 中使用 bind() 进行硬绑定

constructor() {
  this.handleClick = this.handleClick.bind(this);
}

2、直接在元素上使用 bind() 绑定

<label className={className} onClick={this.handleClick.bind(this)}>

3、ES6 有个很有用的语法糖:Arrow Function(箭头函数)它可以很方便的使 this 直接指向 class SwitchButton

<label className={className} onClick={()=>this.handleClick()}>

4.2、props

props就是属性的简写,是单个值,是在父组件中定义或已经在 state 中的值,并将这些值传递给其子组件。props本身不可变,但可以通过触发state的变化,反过来改变props本身的值。

理解:

  • 每个组件对象都会有props(properties的简写)属性

  • 组件标签的所有属性都保存在props中

作用:

  • 用于接收组件外部的数据

  • 传递数据: 通过给组件标签添加属性

  • 接收数据:函数组件通过 参数 props接收数据,类组件通过 this.props接收数据

特点:

  • 可以给组件传递任意类型的数据

  • props是只读属性,不能对值进行修改

  • 使用类组件时,如果写了构造函数,应该将props传递给super(),否则无法在构造函数中获取到props,其他的地方是可以拿到的

应用场景:

  • 子组件调用父组件的方法

    • 子组件要拿到父组件的属性,需要通过 this.props 方法
    • 同样地,如果子组件想要调用父组件的方法,只需父组件把要被调用的方法以属性的方式放在子组件上, 子组件内部便可以通过“this.props.被调用的方法”这样的方式来获取父组件传过来的方法。

  • 父组件调用子组件的方法

    • 在 ReactJS 中有个叫 ref 的属性。这个属性就像给组件起个引用名字一样,子组件被设置为 ref 之后(比如 ref=“xxx”)。父组件便可以通过 this.refs.xxx 来获取到子组件了。

栗子:React 三大属性之一 props的一些简单理解 - 腾讯云开发者社区-腾讯云 (tencent.com)

编码操作:

  • 内部读取某个属性值
    • this.props.name
  • 扩展属性: 将对象的所有属性通过props传递
    • <Person {...person}/>

  • 默认属性值:
    • Person.defaultProps = {
        age: 18,
        sex:'男'
      }
  • 组件类的构造函数
    • constructor(props){
        super(props)
        console.log(props)//打印所有属性
      }

4.3、refs与事件处理

Refs 是一个 获取 DOM节点或 React元素实例的工具。在 React 中 Refs 提供了一种方式,允许用户访问DOM 节点或者在render方法中创建的React元素

使用情景:

  • 对DOM 元素焦点的控制、内容选择或者媒体播放;

  • 通过对DOM元素控制,触发动画特效;

  • 通第三方DOM库的集成。

编码:

  • 字符串形式的ref(不推荐使用,已过时,存在效率问题)

    • <input ref="input1"/>

  • 回调形式的ref

    • <input ref={(c)=>{this.input1 = c}}

    • <script type="text/babel">
              //创建组件
              class Demo extends React.Component{
                  //展示左侧输入框的数据
                  showData = ()=>{
                      const {input1} = this
                      alert(input1.value)
                  }
                  //展示右侧输入框的数据
                  showData2 = ()=>{
                      const {input2} = this
                      alert(input2.value)
                  }
                  render(){
                      return(
                          <div>
                              <input ref={c => this.input1 = c } type="text" placeholder="点击按钮提示数据"/>&nbsp;
                              <button onClick={this.showData}>点我提示左侧的数据</button>&nbsp;
                              <input onBlur={this.showData2} ref={c => this.input2 = c } type="text" placeholder="失去焦点提示数据"/>&nbsp;
                          </div>
                      )
                  }
              }
              //渲染组件到页面
              ReactDOM.render(<Demo a="1" b="2"/>,document.getElementById('test'))
          </script>
  • createRef创建ref容器

    • myRef = React.createRef() 

      <input ref={this.myRef}/>

事件处理:

  • 通过onXxx属性指定事件处理函数(注意大小写)

    • React使用的是自定义(合成)事件, 而不是使用的原生DOM事件

    • React中的事件是通过事件委托方式处理的(委托给组件最外层的元素)

  • 通过event.target得到发生事件的DOM元素对象

 五、组件的属性校验和默认属性

 5.1、特殊属性——propTypes

 对 Component 设置 propTypes 属性,可以为 Component 的 props 属性进行类型检查

HiBox 父组件:
import React, { Component } from 'react'
import Hi,{ Message } from './Hi'
import Person from './person';

export default class HiBox extends Component {
  render() {
    let age= 19;
    return (
      <div>
        <Hi age={age} hobby={['阅读','运动','听音乐',1,2,3]} onVote={()=>alert("你好,大美女!")} sex="女" info={new Message()} height={180} mobile={13454367654} name="tao"></Hi>
      </div>
    )
  }
}

Hi 子组件:

import React, { Component } from 'react'
import PropTypes from 'prop-types'

class Hi extends Component {
  
  render() {
    return (
      <div>
        <h2 onClick={this.props.onVote} style={{cursor: "pointer"}}>
          Hi,我的姓名为:{this.props.name}
          <p>我的年龄为:{this.props.age}岁;</p>
          <p>爱好为:{this.props.hobby.join("、")}</p>
          <p>性别为:{this.props.sex}</p>
          <p>身高为:{this.props.height}</p>
          <p>手机号为:{this.props.mobile}</p>
        </h2>
        
      </div>
    )
  }
}

// 定义类型 Message
export function Message(){
  this.show = ()=>{
    alert("你好!");
  }
}

// 设置组件Hi的属性
Hi.propTypes = {
  // 约束age属性为number类型
  age: PropTypes.number,

  // 约束hobby属性为数组类型
  hobby: PropTypes.array,
  // 约束hobby属性必须为number类型的数组
  // hobby: PropTypes.arrayOf(PropTypes.number),
  
  // 约束onVote属性必须是一个函数
  onVote: PropTypes.func,

  // 限制性别必须为男或女,只能是数组中的某一项
  sex: PropTypes.oneOf(["男","女"]),

  // info属性的值必须是Message类型
  // info: PropTypes.instanceOf(Message),
  // info可以是number类型,string或者Message类型
  info: PropTypes.oneOfType([
    PropTypes.number,
    PropTypes.string,
    PropTypes.instanceOf(Message),
  ]),

  // 约束身高必须在179到220之间
  height: function(props,propsName,ComponentName){
    // 取出属性值
    let value = props[propsName];
    if (value<170 || value>220) {
      throw new Error("身高必须在179到220之间")
    }
  },

  // 约束手机号必须是以1开头的11位数字
  mobile: function (props,propsName,ComponentName){
    let value = props[propsName];
    if (value) {
      if (!/^1\d{10}$/.test(value)) {
        throw new Error("手机号必须是以1开头的11位数字")
      }
    }
  },

  // name属性必须为string类型而且必须要填写
  name:PropTypes.string.isRequired,

}

export default Hi;

运行结果:

 5.2、限制单个子元素

使用 PropTypes.element 你可以指定只有一个子元素可以被传递给组件。

Person 子组件:
import React, { Component } from 'react'
import PropTypes from 'prop-types'

export default class Person extends Component {
  render() {
    return (
      <div>person</div>
    )
  }
}

Person.propTypes = {
  // 限制子元素必须为number类型,而且必填
  // children: PropTypes.number.isRequired,

  // 限制子元素只能为单个元素
  children: PropTypes.element.isRequired
}
HiBox 父组件:
<Person>
     <span>{19}</span>
</Person>

 5.3、默认标签属性值

当组件引用的时候,没有传入该sex和age属性时,会使用默认值。
        //指定默认标签属性值
        Person.defaultProps = {
            sex:'男',//sex默认值为男
            age:18 //age默认值为18
        }

六、生命周期

其实React组件并不是真正的DOM, 而是会生成JS对象的虚拟DOM,虚拟DOM会经历创建,更新,删除的过程

这一个完整的过程就构成了组件的生命周期,React 提供了钩子函数让我们可以在组件生命周期的不同阶段添加操作

6.1、理解

  1. 组件从创建到死亡它会经历一些特定的阶段。

  2. React组件中包含一系列勾子函数(生命周期回调函数), 会在特定的时刻调用。

  3. 我们在定义组件时,会在特定的生命周期回调函数中,做特定的工作。

6.2、生命周期流程图

 

 解析:

 6.3、react的生命周期大概分为

  • 组件装载(Mount)组件第一次渲染到Dom树

  • 组件更新(update)组件state,props变化引发的重新渲染

  • 组件卸载(Unmount)组件从Dom树删除

6.4、生命周期的三个阶段

1. 初始化阶段: 由ReactDOM.render()触发---初次渲染

  1. constructor():在此初始化state,绑定成员函数this环境,props本地化,constructor 构造函数只在初始化化的时候执行一次
  2. getDerivedStateFromProps():在创建时或更新时的render之前执行,让 props 能更新到组件内部 state中,必须是静态的。它应返回一个对象来更新 state,如果返回 null 则不更新任何内容。
  3. render():渲染函数,唯一的一定不能省略的函数,必须有返回值,返回null或false表示不渲染任何DOM元素。它是一个仅仅用于渲染的纯函数,返回值完全取决于this.state和this.props,不能在函数中任何修改props、state、拉取数据等具有副作用的操作。render函数返回的是JSX的对象,该函数并不因为这渲染到DOM树,何时进行真正的渲染是有React库决定的。
  4. componentDidMount():挂载成功函数。该函数不会再render函数调用完成之后立即调用,因为render函数仅仅是返回了JSX的对象,并没有立即挂载到DOM树上,而componentDidMount是在组件被渲染到DOM树之后被调用的。另外,componentDidMount函数在进行服务器端渲染时不会被调用。

2. 更新阶段: 由组件内部this.setSate()或父组件重新render触发

  1. getDerivedStateFromProps()
  2. shouldComponentUpdate()
  3. render()
  4. getSnapshotBeforeUpdate()
  5. componentDidUpdate()

3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发

  1. componentWillUnmount()

6.5、重要的勾子函数

  1. render:初始化渲染或更新渲染调用

  2. componentDidMount:开启监听, 发送ajax请求

  3. componentWillUnmount:做一些收尾工作, 如: 清理定时器

6.6、即将废弃的勾子

  1. componentWillMount

  2. componentWillReceiveProps

  3. componentWillUpdate

现在使用会出现警告,下一个大版本需要加上UNSAFE_前缀才能使用,以后可能会被彻底废弃,不建议使用。

6.7、示例说明

6.7.1、组件装载(Mount)

import React, { Component } from "react";

class Hi extends Component {
  constructor(props) {
    super(props);
    console.log("constructor()函数被调用,构造函数");
    this.state = {
      n: 100,
    };
  }
  render() {
    console.log("render()函数被调用,返回UI描述");
    return <h2>Hello {this.props.name}!</h2>;
  }

  static getDerivedStateFromProps() {
    console.log(
      "getDerivedStateFromProps()函数被调用,让 props 能更新到组件内部 state中"
    );
    return null;
  }

  componentDidMount() {
    console.log("componentDidMount()函数被调用,组件被挂载到DOM后");
  }
}

export default Hi;

运行结果:

 

6.7.1.1、构造方法 constructor 

说明:

  1. 如果不需要初始化state,或者不进行方法的绑定,则不需要实现constructor构造函数

  2. 在组件挂载前调用构造函数,如果继承React.Component,则必须调用super(props)

  3. constructor通常用于处理了state初始化和为事件处理函数绑定this实例

  4. 尽量避免将props外部数据赋值给组件内部state状态

注意:constructor 构造函数只在初始化化的时候执行一次

栗子:

import React, { Component } from "react";

class Counter extends Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0,
    };
  //解决changeWeather中this指向问题
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    console.log(this);
    this.setState({ count: this.state.count + 1 });
  }

  render() {
    return (
      <div>
        <h2>计算器</h2>
        <button onClick={this.handleClick}>点击加1</button>
        <span> {this.state.count} </span>
      </div>
    );
  }
}

export default Counter;

运行结果:

组件 state 也可以不定义在constructor构造函数中,事件函数也可以通过箭头函数处理 this 问题

因此如果不想使用 constructor 也可以将两者移出

import React, { Component } from "react";

class Counter extends Component {
  //初始化组件状态
  state = {
    count: 0,
  };

  //箭头函数处理this问题,自定义方法——要用赋值语句的形式+箭头函数
  handleClick = () => {
    console.log(this);
    this.setState(() => ({ count: this.state.count + 1 }));
  };

  render() {
    return (
      <div>
        <h2>计算器</h2>
        <button onClick={this.handleClick}>点击加1</button>
        <span> {this.state.count} </span>
      </div>
    );
  }
}

export default Counter;

箭头函数中的this指向了当前组件实例,而普通函数则指向undefined。箭头函数this是词法作用域,由上下文确定。

6.7.1.2、构造方法 constructor 

 

6.7.2、组件更新(update)

6.7.3、组件卸载(Unmount)