React笔记---kalrry

React笔记---kalrry

前言

Win配置记录
Mac配置记录

文档

react文档

一、react

1. 什么是react

react是FaceBook推出一款用于构建用户界面的javascript库

2. 特点

声明式设计—我们只需要关心显示内容 react就会自动的把内容展示到页面上

高效

灵活-- react可以吧已知的第三库进行很好的整合

组件化

单向数据流— 主要是指 数据从父节点传递到子节点

3. react的发展史

2013年6月推出的

2013年9月 就受到追捧

2015年 推出了一个reactNative的跨平台开发

4. cra—create-react-app

在前两天 create-react-app更新了

他里面对node的版本有要求了 node的版本不能低于14了

win7系统node的版本不能大于12 npm install -g create-react-app@4

1.全局下载: npm install -g create-react-app

2.查看版本: create-react-app --version

3.cd 到指定文件夹下

4.创建项目 create-react-app 项目名

5.cd到你创建的项目下

6.npm start 启动项目

5. 拿到空项目怎么办?

1.在src下新建一个文件夹 components

2.删除app.js与app.css

3.在components文件夹中创建属于我们自己的组件 xxx.jsx

4.在文件中创建一个基本的组件

import React, { Component } from 'react'

export default class home extends Component {
    render() {
        return (
            <div>
                我是一个组件
            </div>
        )
    }
}

5.在index.js下进行引用使用

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import Home from './components/home.jsx';//把路径切换成我们自己创建的组件
import reportWebVitals from './reportWebVitals';

ReactDOM.render(
  <React.StrictMode>
    {/* 修改使用 */}
    <Home />
  </React.StrictMode>,
  document.getElementById('root')
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

5. jsx

jsx== javascript and xml 他是一个新的语法扩展 在react中使用jsx的语法来进行页面内容的描述

jsx 当遇见< 当html解析遇见{}当js解析

import React, { Component } from 'react'

export default class home extends Component {

    render() {
        let text="你好"//创建了一个变量
        return (
            <div>
                我是一个组件---{text}
            </div>
        )
    }
}

5.1. jsx语法注意

1.jsx中html的标签必须严格按照w3c的规范来编写
			<div>
               <input type="text" />
            </div>
2.jsx中怎么注释

注释是

{/*我是注释的内容*/}

 			<div>
                {/* 我是一个注释 */}
                
               <input type="text" />
            </div>
3.多行html

在react的组件中多行html必须有一个父容器包裹

import React, { Component } from 'react'

export default class demo extends Component {
    render() {
        return (
            // 多行标签必须有一个父容器包裹
            <div>
                <h1>你好我是一个标签</h1>
                <h1>你好我是一个标签</h1>
                <h1>你好我是一个标签</h1>
                <h1>你好我是一个标签</h1>
                <h1>你好我是一个标签</h1>
            </div>
          
        )
    }
}

空标签

大家发现刚才上面写的内容 我们的最外层的div是多余的

空标签 在页面是不进行展示的 它的作用仅仅就是用来描述多行标签的一个包裹作用

写法1:

<></>

import React, { Component } from 'react'

export default class demo extends Component {
    render() {
        return (
            // 空标签1
            <>
                <h1>你好我是一个标签</h1>
                <h1>你好我是一个标签</h1>
                <h1>你好我是一个标签</h1>
                <h1>你好我是一个标签</h1>
                <h1>你好我是一个标签</h1>
            </>
          
        )
    }
}

写法2:

Fragment空标签

import React, { Component,Fragment } from 'react'

export default class demo extends Component {
    render() {
        return (
            // 空标签2
            <Fragment>
                <h1>你好我是一个标签</h1>
                <h1>你好我是一个标签</h1>
                <h1>你好我是一个标签</h1>
                <h1>你好我是一个标签</h1>
                <h1>你好我是一个标签</h1>
            </Fragment>
          
        )
    }
}

4 {}中还可以写什么?

我们可以在jsx的{}中放置任何表达式

import React, { Component } from 'react'

export default class demob extends Component {
    render() {
        let a=3
        let b=5
        let fun=()=>{
            return "我是一个函数"
        }
        let bool=false
        return (
            <div>
                <h1>我是测试大括号的例子</h1>
                <h1>进行运算符的操作--{a+b}</h1>
                <h1>进行函数的调用 --- {fun()}</h1>
                <h1>进行复杂的运算符 --- {bool?"你好":"你坏"}</h1>
            </div>
        )
    }
}

5.属性插变量
import React, { Component } from 'react'

export default class democ extends Component {
    render() {
        let text="点我去百度"
        let ahref="http://www.baidu.com"
        return (
            <>
                <h1>属性插变量</h1>
                {/* react中属性插变量   属性={你要插的值} */}
                <a href={ahref}>{text}</a>
            </>
        )
    }
}

6.怎么设置行内样式
import React, { Component } from 'react'

export default class demod extends Component {
    render() {
        return (
            <div>
                {/* jsx中写行内样式 style={{对象的key:val}} */}
                {/* 如果样式是多个单词 那么必须使用小驼峰命名把  把-去掉 后面的单词大写 */}
                <h1 style={{color:'red',backgroundColor:'yellow'}}>设置行内样式</h1>
            </div>
        )
    }
}

便利列表

使用 原生js的数组 map方法

import React, { Component } from 'react'

export default class demoe extends Component {
    render() {
        let arr=["小明","小白","小黑","小花"]
        let obj=[
            {name:"xixi1",age:181},
            {name:"xixi2",age:182},
            {name:"xixi3",age:183},
            {name:"xixi4",age:184},
            {name:"xixi5",age:185}
        ]
        return (
            <div>
                <h1>便利数据</h1>
                <ul>
                    {
                        arr.map((v,i)=>{
                            return (
                                <li key={i}>{i}----{v}</li>
                            )
                        })
                    }
                </ul>
                <hr />
                <h1>便利表格</h1>
                <table border="1">
                    <tbody>
                  {
                      obj.map((v,i)=>{
                          return (
                              <tr key={i}>
                                  <td>{v.name}</td>
                                  <td>{v.age}</td>
                              </tr>
                          )
                      })
                  }
                    </tbody>
                </table>
            </div>
        )
    }
}

6. 组件

6.1. 概念扩展

  1. 模块与模块化

    模块:用来封装可以重复使用的js代码块

    模块化:整个项目都是使用模块的方式来完成的

  2. 组件与组件化

    组件: 用来封装重复使用的ui代码块

    组件化:整个项目都是使用组件的方式来完成的

6.2. 组件的基本改变

组件就是把ui部分拆分成一个个独立的并且可以重复使用的部件 在吧这些部件拼装在一起 形成一个页面

6.3. 组件的分类

6.3.1 函数组件-无状态组件

1.函数组件的首字母必须大写首字母必须大写首字母必须大写首字母必须大写首字母必须大写首字母必须大写

2.函数中必须有一个 return return 一段 jsx

// 我是函数组件的例子
function Fun(){
    return (
        <>
            <h1>我是一个函数组件</h1>
        </>
    )
}
export default Fun
父子组件

组件的使用

1.引用

2.使用

// 父组件
// 1.引用子组件
import Zi from "./zi.jsx"
function Fu(){
    return (
        <div>
            我是一个父组件
            {/* 2.使用 */}
            <Zi/>
        </div>
    )
}
export default Fu
//=======================================
// 子组件
function Zi(){
    return (
        <>
            <h1>我是一个子组件</h1>
        </>
    )
}
export default Zi

6.3.2 类组件

1.在类中必须必须必须有一个render的方法 其中必须要有一个return 一段jsx

2.这个类要继承React.Component

import React, { Component } from 'react'

export default class demob extends Component {
    render() {// 就是渲染的意思  执行这个render他就会执行jsx
        return (
            <div>
                
            </div>
        )
    }
}

父子组件
//父组件
import React, { Component } from 'react'
// 1.引用子组件
import Zi from "./zi.jsx"
export default class fu extends Component {
    render() {
        return (
            <div>
                我是一个父组件
                {/* 2.使用 */}
                <Zi/>
            </div>
        )
    }
}
//============================================

//子组件
import React, { Component } from 'react'
export default class zi extends Component {
    render() {
        return (
            <div>
                我是一个子组件
            </div>
        )
    }
}

6.4. 组件传值

6.4.1 props

props就是组件对外的接口 可以使用props 从 组件外部内部 进行数据的传递 从而完成父子组件的传值

props是只读的 他不能被修改

1. 函数组件

1.把props当成函数的形参传递

2.在子组件设置接收数据

3.在父组件的传递

子组件

// 1.在子组件的形参中 注入props
function Zi(props){
    return (
        <>
                        {/* 2.使用props */}
            <h1>我是一个子组件---{props.title}</h1>
        </>
    )
}
export default Zi
父组件


import Zi from "./zi.jsx"
function Fu(){
    return (
        <div>
            我是一个父组件
            {/* 3.在父组件给子组件的props传递数据 */}
            <Zi title="我是正向传值的数据"/>
        </div>
    )
}
export default Fu
2. 类组件

1.子组件使用this.props.xxx接收

import React, { Component } from 'react'

export default class zi extends Component {
    render() {
        return (
            <div>
                {/* 1.直接使用this.props来进行接收 */}
                我是一个子组件---{this.props.num}
            </div>
        )
    }
}

2.父组件传递

import React, { Component } from 'react'
import Zi from "./zi.jsx"
export default class fu extends Component {
    render() {
        return (
            <div>
                我是一个父组件
                {/* 2.给子组件传递数据 */}
                <Zi num="我是num"/>
            </div>
        )
    }
}

6.4.2. props验证

注意:

自 React v15.5 起,React.PropTypes 已移入另一个包中。请使用 prop-types 代替。

在cra脚手架中 prop-types的库默认是帮助我们下载好的 我们可以直接使用

1. 函数组件

1.子组件引用prop-types库 并且设置验证

// 1.引用
import PropTypes from 'prop-types';
function Zi(props){
    return (
        <div>
            我是子组件--{props.name}
        </div>
    )
}

// 设置   注意单词大小写
Zi.propTypes={
    name:PropTypes.string
}
export default Zi

2.父组件正常传递

import Zi from "./zi.jsx"
function Fu(){
    let num="666"
    return (
        <div>
            我是父组件
            <Zi name={num}/>
        </div>
    )
}
export default Fu
2. 类组件
子组件

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

export default class zi extends Component {
    static propTypes = {
        name: PropTypes.string
    }

    render() {
        return (
            <div>
                ziziziz--{this.props.name}
            </div>
        )
    }
}


父组件
import React, { Component } from 'react'
import Zi from "./zi.jsx"
export default class fu extends Component {
    render() {
        return (
            <div>
                fuffufufu
                <Zi name={"666"}/>
            </div>
        )
    }
}


6.4.3. props公司写法
import React, { Component } from 'react'

export default class zi extends Component {
    render() {
        // 因为你会发现在页面中有大量的this.props看上去非常的麻烦 很冗余
        // 所以我们需要想办法把这个this.props省略掉
        // this.props既然能.出name age等内容  那么久表示这个this.props
        // 是一个对象  既然是个对象 那么我们就可以使用es6解构赋值的语法
        // 快速的取出每个值
       let {name,age,sex,love}=this.props
        return (
            <div>
                zizizizizizizi
                {/* <h1>{this.props.name}</h1>
                <h1>{this.props.age}</h1>
                <h1>{this.props.sex}</h1>
                <h1>{this.props.love}</h1> */}
                {/* 解构赋值减少了代码量页面中也没有那么多this。props */}
                <h1>{name}</h1>
                <h1>{age}</h1>
                <h1>{sex}</h1>
                <h1>{love}</h1>
            </div>
        )
    }
}

父组件传递的时候使用扩展运算符简化了传递的过程

import React, { Component } from 'react'
import Zi from "./zi.jsx"
export default class fu extends Component {
    render() {
        let obj={
            name:"xixi1",
            age:181,
            sex:"男1",
            love:"女"
        }
        return (
            <div>
                fuffufufufu
                {/*
                 这种写法 就是子组件需要多少个参数  那么父组件就给他
                传递多少个  没问题 这种写法没问题  但是不够巧妙 
                并且页面中有很多的冗余代码 
                
                为了解决这个问题  我们就可以使用es6  ...扩展运算符的方式

                */}
                {/* <Zi name="xixi" age="18" sex="男" love="女" /> */}
                <Zi {...obj}/>
            </div>
        )
    }
}

6.5. react中插槽实现

思考一个问题

在react中组件的本质是自定义标签 在自定义标签的开关标签中我们能插入新的内容吗?

默认情况下 在组件被调用的时候 是不能插入内容的 你写了他也不显示

 				默认页面是不能插入的 
                <Zib>
                    <h1>你好我是一个站位的标签</h1>
                    <h1>你好我是一个站位的标签</h1>
                    <h1>你好我是一个站位的标签</h1>
                    <h1>你好我是一个站位的标签</h1>
                    <h1>你好我是一个站位的标签</h1>
                </Zib>
6.5.1 this.props.children

对应vue中的slot插槽

他表示的就是当前组件的所有子节点

//在组件中设置this.props。children之后
<div>
  zibbbbbbb
  {this.props.children}
</div>


//就可以向组件内部插入新内容了
<Zib>
  <h1>你好我是一个站位的标签</h1>
  <h1>你好我是一个站位的标签</h1>
  <h1>你好我是一个站位的标签</h1>
  <h1>你好我是一个站位的标签</h1>
  <h1>你好我是一个站位的标签</h1>
</Zib>

7. state—状态机

状态(数据/变量)机(机制)

react中创建数据 都是在state中进行创建的

在react中我们开发者只会关心数据 数据改变了页面也随之发生改变

7.1. 创建状态

思考 函数组件能使用状态吗?

函数组件不能使用状态(后面咱们到react高级部分学到hook就可以让函数组件使用状态 但是现在不行)

import React, { Component } from 'react'

export default class demoi extends Component {
    // 我们要使用状态  必须把状态创建在class的constructor中
    constructor(){
        //es6的继承规则中  不管子类写不写constructor 在实例化的时候都会补上constructor
       // 如果我们写了constructor  那么在其中必须写super() 调用父类的构造方法  那么这个时候子类才会有			//自己的this
        super()

        // 创建状态
        this.state={
            name:"xixi",
            arr:[1111,22222,3333],
            obj:{name:"hehe",age:99}
        }
    }
    render() {
        return (
            <div>
                <h1>状态的使用</h1>
            </div>
        )
    }
}

7.2. 使用状态

this.state.xxx

  render() {
        return (
            <div>
                {/* 状态的使用直接使用  this.state.xxx */}
                <h1>状态的使用---{this.state.name}</h1>
            </div>
        )
    }

7.3. 修改状态

在react中状态的修改必须必须必须使用 this.setState({你要修改谁:修改成什么})

import React, { Component } from 'react'

export default class demoi extends Component {
    // 我们要使用状态  必须把状态创建在class的constructor中
    constructor(){
        super()

        // 创建状态
        this.state={
            name:"xixi",
            arr:[1111,22222,3333],
            obj:{name:"hehe",age:99}
        }
    }

    fun=()=>{
        // 修改状态
        this.setState({
            name:"我变了"
        })
    }

    render() {
        return (
            <div>
                {/* 状态的使用直接使用  this.state.xxx */}
                <h1>状态的使用---{this.state.name}</h1>
                <button onClick={this.fun}>点我修改</button>
            </div>
        )
    }
}

修改数据的时候能不使用setState吗?

不能不使用setState 因为不使用他修改 页面是不会变得

import React, { Component } from 'react'

export default class demoa extends Component {
    constructor(){
        super()

        this.state={
            text:"你好"
        }
    }
    fun=()=>{
        // 使用这种等于号的方式  是可以修改数据的  但是页面不会发生改变
      this.state.text="我没有使用setState修改"
      console.log(this.state.text)
    }
    render() {
        return (
            <div>
                <h1>{this.state.text}</h1>
                <button onClick={this.fun}>修改state的数据</button>
            </div>
        )
    }
}

当我调用了setState之后发生了什么事情
1.setState是异步的
import React, { Component } from 'react'

export default class demob extends Component {
    constructor(){
        super()

        this.state={
            text:"你好"
        }
    }
    fun=()=>{
        // setState是异步的
      this.setState({
          text:"我被改了"
      })
    //   如果setState修改是同步的那么下面的console应该打印出来的就是修改之后的数据
    //   如果setState是异步的那么打印出来的应该就是原始数据
      console.log(this.state.text)
    }
    render() {
        return (
            <div>
                <h1>{this.state.text}</h1>
                <button onClick={this.fun}>修改state的数据</button>
            </div>
        )
    }
}

2 调用了setState之后重新触发了render渲染
import React, { Component } from 'react'

export default class demob extends Component {
    constructor(){
        super()

        this.state={
            text:"你好"
        }
    }
    fun=()=>{
        // setState是异步的
      this.setState({
          text:"我被改了"
      })
    //   如果setState修改是同步的那么下面的console应该打印出来的就是修改之后的数据
    //   如果setState是异步的那么打印出来的应该就是原始数据
      console.log(this.state.text)
    }
    render() {
        // 调用了setState之后页面为什么能改变  原因就是render方法被setState调用了
        console.log("我是render方法")
        return (
            <div>
                <h1>{this.state.text}</h1>
                <button onClick={this.fun}>修改state的数据</button>
            </div>
        )
    }
}

3 因为setState是异步的 那么我们怎么在修改完成之后打印修改之后的结果
this.setState({
          text:"我被改了"
      },()=>{
        //   setState修改成功之后的回调函数
        console.log(this.state.text)
      })

8. 扩展----强制刷新

在react中我们创建变量 并且修改这个变量现在页面做出反应 那么默认情况下我们必须要在state上进行完成 并且修改数据必须要使用setState

我们有没有其他的方式让数据不在state之上并且修改数据还想让页面页同时改变的方法呢?

import React, { Component } from 'react'

export default class demob extends Component {
    constructor(){
        super()
        this.state={
            text:"我是状态的数据"
        }

        // 不想把变量创建在state之上
        this.num=666
    }
    fun=()=>{
        this.setState({
            text:"我被改了"
        })
    }

    funb=()=>{
        this.num=9527
        console.log(this.num);

        // 强制刷新---强制触发render渲染
        this.forceUpdate()
    }
    render() {
        return (
            <div>
                <h1>强制刷新</h1>
                <h1>传统的state-----{this.state.text}</h1>
                <button onClick={this.fun}>点我修改</button>

                <hr />
                <h1>不在state上创建变量----{this.num}</h1>
                <button onClick={this.funb}>点我修改</button>
            </div>
        )
    }
}

9. 事件处理

事件绑定

在react中事件的绑定 使用小驼峰命名法

例:onclick 在react中 onClick

绑定完事件之后在调用函数的时候不加()不加() 因为加了函数就自动调用了

绑定完事件之后在调用函数的时候不加()不加() 因为加了函数就自动调用了

绑定完事件之后在调用函数的时候不加()不加() 因为加了函数就自动调用了

绑定完事件之后在调用函数的时候不加()不加() 因为加了函数就自动调用了

事件处理中的一些操作

阻止事件默认行为 阻止冒泡

同原生js一样没有任何改变

阻止事件的默认行为 原生 preventDefault() react也是使用相同的方式完成

函数实参传递

因为在react中函数调用的时候不加() 那我我们如果要传递函数的实参怎么传递?

1.使用bind方式进行传递

 <button onClick={this.fun.bind(this,"我是实参1","我是实参2")}>点我传递函数实参</button>

2.使用箭头函数调用函数进行传递

<button onClick={()=>{this.funb(1111,2222)}}>点我传递实参2</button>

整体代码

import React, { Component } from 'react'

export default class demoe extends Component {
    fun=(num,text)=>{
        console.log(num)
        console.log(text)
    }
    funb=(num,text)=>{
        console.log(num)
        console.log(text)
    }
    render() {
        return (
            <div>
                <h1>函数实参传递</h1>
                {/* bind方式传递实参 */}
                <button onClick={this.fun.bind(this,"我是实参1","我是实参2")}>点我传递函数实参</button>
                
                {/* 使用箭头函数调用函数进行传递 */}
                <button onClick={()=>{this.funb(1111,2222)}}>点我传递实参2</button>
            </div>
        )
    }
}

修改this 指向

1.通过创建箭头函数的方式来修改

funa=()=>{
        this.setState({
            text:"我改了创建箭头函数"
        })
    }

2.通过bind绑定绑定this

<button onClick={this.funb.bind(this)}>通过bind绑定绑定this</button>

3.把函数的调用变成箭头函数调用

 <button onClick={()=>{this.func()}}>把函数的调用变成箭头函数调用</button>
import React, { Component } from 'react'

export default class demof extends Component {
    constructor(){
        super()
        this.state={
            text:'我是text'
        }
    }
    // 现在我们写了一个普通匿名函数
    fun(){
        this.setState({
            text:"我改了"
        })
    }

    // 创建箭头函数
    funa=()=>{
        this.setState({
            text:"我改了创建箭头函数"
        })
    }
    // 通过bind绑定绑定this
    funb(){
        this.setState({
            text:"通过bind绑定绑定this"
        })
    }
    // 把函数的调用变成箭头函数调用
    func(){
        this.setState({
            text:"把函数的调用变成箭头函数调用"
        })
    }
    render() {
        return (
            <div>
                <h1>this指向</h1>
                <h2>{this.state.text}</h2>

                <button onClick={this.fun}>错误的写法</button>
                <button onClick={this.funa}>创建箭头函数来修改</button>
                <button onClick={this.funb.bind(this)}>通过bind绑定绑定this</button>
                <button onClick={()=>{this.func()}}>把函数的调用变成箭头函数调用</button>
            </div>
        )
    }
}

10. ref

ref 标识组件内部的元素 就给组件的dom元素起个名字

函数组件是不能直接使用ref的

ref 写法的分类

1.字符串(官方已经不推荐使用了)
2.回调函数(官方推荐方式)

回调函数的方式 就是在dom节点上挂载一个函数 函数的入参 就是dom节点

import React, { Component } from 'react'

export default class demoj extends Component {
    fun=()=>{
        console.log(this.demoinput.value)
    }
    render() {
        return (
            <div>
                <h1>ref的例子</h1>
                {/* 回调函数的方式创建ref */}
                {/* <input type="text" ref={(text这个变量代表的就是当前这个input标签)=>{this.demoinput(随便创建一个变量)=text}}/> */}
                <input type="text" ref={(text)=>{this.demoinput=text}}/>
                <button onClick={this.fun}>点我得到输入框的值</button>
            </div>
        )
    }
}

3.React.createRef()(16.8版本新增的 官方推荐)

react 16.8新增的一种方式 我们通过初始化createRef从而得到ref对象 插入到页面中

import React, { Component } from 'react'

export default class demoh extends Component {
    constructor(){
        super()
        // 1.创建createRef
        this.inputref=React.createRef()
    }
    fun=()=>{
        // 3.使用
        console.log(this.inputref.current.value)
    }
    render() {
        return (
            <div>
                <h1>createRef</h1>
                {/* 2.绑定 */}
                <input type="text" ref={this.inputref}/>
                <button onClick={this.fun}>点我得到输入框的值</button>
            </div>
        )
    }
}

11. 生命周期

react从创建到销毁的过程那个生命周期。

1.挂载阶段

​ constructor() react数据初始化

​ componentWillMount() 用的一般比较少 dom渲染之前调用

​ render() 执行dom渲染的一个钩子函数

​ componentDidMount() dom渲染之后调用

2.更新阶段

​ componentWillReceiveProps() 在组件接收一个新的props的时候被调用 初始化不触发

​ shouldcomponentUpdate() 判断组件是否要更新 主要是在性能优化的时候使用的 如果在代码中写这个钩 子了 那么必须在其中有一个return 的布尔值的返回值 否则会出问题

​ componentWillUpdate ()准备更新

​ render()正在更新

​ componentDidUpdate()更新完毕

3.销毁阶段

componentWillUnmount()

ajax是在那个钩子中发送的请求

是在componentDidMount()中发送请求 因为这个时候dom已经加载完毕了 所以我们在这个时候可以保证数据可以成功的渲染到页面上 并且如果要修改数据使用setState的时候 也不用怕dom没有加载完毕从而报错

在react16之后 componentWillMount()可能会被执行多次 所以不建议在这个钩子中发送请求

12. 条件渲染

在开发中 创建不同的组件来封装我们需要完成的各个内容 但是我们需要根据程序的状态变化来渲染其中一部分内容

if语句来看进行条件渲染

在react中if条件渲染是最简单的 但是但是但是但是 注意 在jsx不允许出现if

import React, { Component } from 'react'
export default class demo extends Component {
    render() {
        let newhtml=""
        let num=1

        if(num==1){
            newhtml= <h1>吃饭</h1>
        }else if(num==2){
            newhtml= <h1>睡觉</h1>
        }else{
            newhtml=<h1>上厕所</h1>
        }
        return (
            <div>
                    {newhtml}
                
            </div>
        )
    }
}

三元运算符

13. 使用图片

1.把图片放到public文件夹中 直接使用图片名

2.不在public下 我们可以使用require()来进行引用

<img src={require("../assets/2.webp")} />

3.不在public下 我们也可以使用导入式

import React, { Component } from 'react'

// 1.引入图片
import imgA from "../assets/2.webp"
export default class demob extends Component {
    render() {
        return (
            <div>
                {/* <img src="img/1.webp" /> */}



                {/* <img src={require("../assets/2.webp")} /> */}

                {/* 2.使用 */}
                <img src={imgA} />
            </div>
        )
    }
}

14. 组件传值

14.1. 正向传值

详见上面的props内容

14.2. 逆向传值

子组件
import React, { Component } from 'react'

export default class zi extends Component {
    render() {
        return (
            <div>
                zizizizziziz
                {/* 1.子组件通过一个事件触发逆向传值 */}
                {/* 2.在事件中接收一个父组件传递过来的函数 */}
                {/* 4.我们通过函数传递实参的方式把数据传递到父组件 */}
                <button onClick={this.props.fufun.bind(this,"我是数据")}>点我进行逆向传值</button>
            </div>
        )
    }
}



父组件
import React, { Component } from 'react'
import Zi from "./zi.jsx"
export default class fu extends Component {
    // 5.设置形参接收数据
    demo=(text)=>{
        console.log("父组件",text)
    }
    render() {
        return (
            <div>
                fuffufufufuf
                {/* 3.父组件给子组件传递一个函数 */}
                <Zi fufun={this.demo}/>
            </div>
        )
    }
}


14.3. 同胞传值/兄弟组件传值

同胞传值 要使用一个插件 叫pubsub-js来完成同胞传值

下载: npm install --save pubsub-js

1.我们需要在传递数据的组件中抛出数据(自定义事件)抛出 publish(“事件名”,数据)

import React, { Component } from 'react'
// 引入pubsub
import PubSub from "pubsub-js"
export default class zia extends Component {
    fun=()=>{
        // 抛出数据
        PubSub.publish("apao","我是zia组件的数据")
    }
    render() {
        return (
            <div>
                ziaaaaaaaaaa
                <button onClick={this.fun}>点我传递数据到我兄弟b哪里去</button>
            </div>
        )
    }
}

在想接收数据的组件中使用subscribe()监听事件 来进行数据的接收

import React, { Component } from 'react'
// 引用pubsub
import PubSub from "pubsub-js"
export default class zib extends Component {
    // 接收数据 我们是需要自动接收的
    componentDidMount() {
        // PubSub.subscribe("你监听的事件",(你监听的事件,数据)=>{})
        PubSub.subscribe("apao",(a,b)=>{
            console.log(a)
            console.log(b)
        })
    }
    
    render() {
        return (
            <div>
                zibbbbbbb
            </div>
        )
    }
}

14.4. 跨组件传值

1. 用原生上下文对象context

react的组件中的数据流转 是单向传递的 也就是说数据 是通过props一层一层的进行传递 传递到子组件 孙组件中的 这样子传递很麻烦 如果我们想跃层传值 那么就要使用context上下文对象来进行

context:上下文对象 就可以非常简单的解决跨组件传值的复杂度 高效的完成跨组件的数据通信

1.如果要使用上下文对象 我们必须使用createContext这个方法 来创建出上下文context对象

2.我们就可以使用上下文对象给我们的两个方法

Provider 用来生产数据------》生产者

Consumer 使用数据------------》消费者

1.创建上下文对象文件夹与文件并且写入如下内容

import React, { Component } from 'react'

class MyContext extends Component {
    render() {
        return (
            <div>
                
            </div>
        )
    }
}
export {MyContext}

2.设置上下文对象变成所有组件的父组件

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './components/demoe/ye.jsx';
import reportWebVitals from './reportWebVitals';

// 引用上下文对象 
import {MyContext} from "./context/index.js"

ReactDOM.render(
  // 让上下文对象的组件变成所有组件的老大
  <MyContext>
    <App />
  </MyContext>,
  document.getElementById('root')
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

3.在上下文对象中设置子节点的接收

import React, { Component } from 'react'

class MyContext extends Component {
    render() {
        return (
            <div>
                {/* 使用this.props.children接收所有的子节点 */}
                {this.props.children}
            </div>
        )
    }
}
export {MyContext}

4.开始创建上下文对象

import React, { Component,createContext } from 'react'
// 创建上下文对象
let context=createContext()
// 取出生产者与消费者
// let Provider=context.Provider
// let Consumer=context.Consumer

   let {Provider,Consumer}=context;


class MyContext extends Component {
    render() {
        return (
            <div>
                 {/* 使用provider生产数据 使用value属性生产数据*/}
                <Provider value={{name:"xixi"}}>
               
                    {/* 使用this.props.children接收所有的子节点 */}
                    {this.props.children}

                </Provider>
            </div>
        )
    }
}
// 暴露消费者   其他想使用的组件才能调用
export {MyContext,Consumer}

5.在想使用数据的组件中直接使用

import React, { Component } from 'react'
// 引用消费者
import {Consumer} from "../../context/index.js"
export default class zi extends Component {
    render() {
        return (
            <div>
                zziziziziziziz
                {/* 使用消费者来获取数据 */}
                <Consumer>

                    {
                        // (形参就是数据)=>{}
                        (value)=>{
                            return (
                                <>
                                <h1>{value.name}</h1>
                                <h1>{value.age}</h1>
                                </>
                                
                            )
                        }
                    }

                </Consumer>
            </div>
        )
    }
}

2. redux

在传统的跨层级传值中 如果我们需要进行数据的传递 那么我们就需要把数据先通过逆向传值一层层往上传递 在通过正向传值一层层的向下传递

redux就是javascript点的一个状态管理工具 集中的进行react中多个组件的状态管理 方便我们在跨层级使用数据的便捷性

redux是一个专门进行状态管理的js库 但是他是一个第三方的插件 所以 他不是react独享的 他可以在任意地方使用 vue中也可以 只是vue中有vuex 没有人使用redux罢了

redux的三大原则

1.单一数据源 整个redux引用是一个一个store对象

2.state是只读的 不能直接修改 必须通过redux当中的action来进行修改

3.使用纯函数来执行修改 我们如果要修改数据 那么我们就要编写一个个的函数来承载着每一个修改操作

redux 执行流程

1.用户在页面操作需要修改数据状态 发送dispatch 触发修改动作

2.派发动作触发

3.开始进行修改 携带者原始的state状态 在action中进行数据的处理 通过reducer来生成新的结果

4.把修改的状态state返回出来

5.在组件中进行新状态的展示

redux实践

1.下载 npm install --save redux

2.创建对应的文件夹(store)与文件(xxx.js)

基本的redux
数据的创建
// 1.引用redux  并且解构出创建redux的方法 createStore
import {createStore} from "redux"
// 5.创建数据
let data={
    name:"xixi",
    age:18
}
// 4.创建reducer是一个方法  方法中包含了数据 与修改数据的动作
// state数据 action就是今后数据的修改动作
// 6.使用函数形参默认值的方式 把上面的data赋值给state即可
let reducer=(state=data,action)=>{
    return state
}
// 2.创建redux对象
// 7把reducer注入 stote中
let store=createStore(reducer)

// 3.暴露redux对象
export default store
数据的读取
import React, { Component } from 'react'
// 1.引用store对象
import store from "../store/index.js"
export default class demo extends Component {
    constructor(){
        super()
        // 2.把数据redux的数据挂载到state之上   因为方便后期的修改
        // 读取redux的数据使用getState()来进行redux的数据获取
        this.state={
            name:store.getState().name
        }
    }
    render() {
        return (
            <div>
                {/* 3.使用 */}
                <h1>基本的redux数据读取---{this.state.name}</h1>
            </div>
        )
    }
}

数据的修改

1.在组件中通过事件调用一个函数触发修改redux数据的动作

import React, { Component } from 'react'
import store from "../store/index.js"
export default class demob extends Component {
    constructor(){
        super()
        this.state={
            xiaoming:store.getState().age
        }
    }
    fun=()=>{
        // 2.触发redux中修改动作dispatch()调用修改的动作
        // store.dispatch({type:你要触发的修改动作名})
        // 在工作的时候这个动作名大家为了方便和变量进行区分 
        // 所以 工作中一般都是大写 
        store.dispatch({type:"UPDATE_NUM"})
    }
    render() {
        return (
            <div>
                <h1>redux的数据修改--{this.state.xiaoming}</h1>
                {/* 1.事件调用一个函数触发redux的数据修改 */}
                <button onClick={this.fun}>点我修改redux中的age数据</button>
            </div>
        )
    }
}

2.redux中创建对应的修改动作

import {createStore} from "redux"
let data={
    name:"xixiahah",
    age:18
}
let reducer=(state=data,action)=>{
    // action就是数据的修改动作集合  判断action.type是那个名字
    // 从而就知道你要调用那个修改的动作
    switch (action.type) {
        case "UPDATE_NUM":
            console.log({...state,age:666})
            return {...state,age:666}
            break;
    
        default:
            return state
            break;
    }

   
}
let store=createStore(reducer)

export default store

大家发现上面的两步操作 完成之后数据变了 但是组件中的内容并没有改变

原因 虽然redux中的数据改变了 但是组件中的render方法并没有执行 那么页面就不会重新渲染 数据当然不会在页面改变了

3.使用subscribe() 一个监听器 会监听store中的数据 当store中的数据改变的时候 subscribe就会触发

import React, { Component } from 'react'
import store from "../store/index.js"
export default class demob extends Component {
    constructor(){
        super()
        this.state={
            xiaoming:store.getState().age
        }
    }

    // 3.设置监听监听store中的数据改变触发页面中的render渲染
    componentDidMount() {
        store.subscribe(()=>{
            // 会在store数据改变的时候自动触发
            this.setState({
                xiaoming:store.getState().age
            })
        })
    }
    


    fun=()=>{
        // 2.触发redux中修改动作dispatch()调用修改的动作
        // store.dispatch({type:你要触发的修改动作名})
        // 在工作的时候这个动作名大家为了方便和变量进行区分 
        // 所以 工作中一般都是大写 
        store.dispatch({type:"UPDATE_NUM"})
    }


    render() {
        return (
            <div>
                <h1>redux的数据修改--{this.state.xiaoming}</h1>
                {/* 1.事件调用一个函数触发redux的数据修改 */}
                <button onClick={this.fun}>点我修改redux中的age数据</button>
               
            </div>
        )
    }
}

数据修改参数传递

1.在dispatch触发的时候传递第二个参数

 store.dispatch({type:"UPDATE_ADD_NUM",num:10})

2.在redux的action就可以使用 action.xxx来进行接收

  case "UPDATE_ADD_NUM":
            console.log("aaaaaa")
            return {...state,age:state.age+action.num}
            break;
redux拆分的写法

之前的写法都是把内容冗余在一起 导致项目的可维护性就非常低

工作之后都会吧这些内容拆分成一个个的独立文件 方便后期维护与管理

使用actionCreator统一的创建action

今后会有很多个修改的动作在组件里面通过dispatch进行创建 那么项目复杂的时候比较难以维护

可以使用一个独立的文件来管理我们的派发动作

1.在store文件夹下新建一个文件 actionCreator.js

// 封装派发动作

export let UPDATE_NUM=()=>{
    return {type:"UPDATE_NUM"}
}

2.在使用的组件中引用使用

import React, { Component } from 'react'
import store from "../store/oldindex.js"
// 1.引用封装的派发动作
import {UPDATE_NUM} from "../store/actionCreator.js"

export default class demob extends Component {
    constructor(){
        super()
        this.state={
            xiaoming:store.getState().age
        }
    }

    componentDidMount() {
        store.subscribe(()=>{
            this.setState({
                xiaoming:store.getState().age
            })
        })
    }
    
    fun=()=>{
        // 2.使用封装
        store.dispatch(UPDATE_NUM())
    }

    add=()=>{
        store.dispatch({type:"UPDATE_ADD_NUM",num:10})
    }
    
    render() {
        return (
            <div>
                <h1>redux的数据修改--{this.state.xiaoming}</h1>
                {/* 1.事件调用一个函数触发redux的数据修改 */}
                <button onClick={this.fun}>点我修改redux中的age数据</button>
                <button onClick={this.add}>点我让num+1</button>
            </div>
        )
    }
}

拆分动作名

封装的本质就是把 重复使用的东西剥离出来单独创建方便复用

因为在派发动作和store的action中这个动作名出现了多次 为了方便后期好维护所以我们 拆分动作名

1.在store文件夹下创建一个actionType.js的文件用来容纳动作名

// 用来容纳动作名
export const UPDATE= "UPDATE_NUM_A"
export const ADD= "UPDATE_ADD_NUM"

2.在页面调用的动作哪里时候替换我们封装的动作

// 封装派发动作
// 1.引用封装的名字
import {UPDATE,ADD} from "./actionType.js"

export let UPDATE_NUM=()=>{
    // 2.替换原有的字符串
    return {type:UPDATE}
}

export let UPDATE_ADD_NUM=(num)=>{
    return {type:ADD,num}
}

3.在action的switch中还需要替换

import {createStore} from "redux"
// 1.引用封装的动作名
import {UPDATE,ADD} from "./actionType.js"
let data={
    name:"xixiahah",
    age:18
}
let reducer=(state=data,action)=>{
    switch (action.type) {
        // 2.替换动作名的字符串
        case UPDATE:
            console.log({...state,age:666})
            return {...state,age:666}
            break;
        case ADD:
            console.log("aaaaaa")
            return {...state,age:state.age+action.num}
            break;
    
        default:
            return state
            break;
    }

   
}
let store=createStore(reducer)

export default store
reducer-拆分reducer和和并reducer

到此大家发现redux 的index.js中还是包含着项目的所有数据和所有的修改动作

组件多起来之后 还是会发现 数据全部在一起 后期难以维护

我们就要把数据和修改动作拆分成一个个独立的模块

1.新建一个文件夹 reducers 其中创建一个xxx.js用来存放我们的模块

2.把原来写在一起的数据 与数据的修改动作 放置其中

// 创建数据
import {UPDATE_NAME} from "../actionType.js"
let data={
    name:"xixi",
    age:18
}

// 创建数据与数据的修改动作
let demoreducer=(state=data,action)=>{
    // 判断action的type名字
    switch (action.type) {
        case UPDATE_NAME:
            return {...state,age:666}
            break;
    
        default:
            break;
    }
    return state
}


export default demoreducer

3.因为我们把每个组件的数据与修改动作单独管理了 所以我们会有很多个独立小模块的reducer 那么我们就需要把这些独立的小模块的reducer合并成一个

combineReducers()来对多个reducer模块进行合并操作

// 把上面多个reducer模块进行合并的文件
// 1.引用你所要合并的reducer模块
import demoreducer from "./reducers/demom.js"
// 2.创建出合并的方法
import {combineReducers} from "redux"
// 3.开始合并
let reducer=combineReducers({
    demoreducer
})

// 暴露
export default reducer

4.在创建store对象的文件 引用合并reducer文件 把合并之后的reducer传递到 store的创建当中

import {createStore} from "redux"

// 引用合并reducer的文件
import reducer from "./reducer.js"


let store=createStore(reducer)

export default store

5.读取

   constructor(){
        super()
        this.state={
            // 当我们查分模块之后  读取数据应该是 
            // store.getState().模块名.数据
            text:store.getState().demoreducer.age
        }
    }
    componentDidMount() {
        // 监听store的变化  当store改变的时候我们触发一个render方法重新渲染
        store.subscribe(()=>{
            this.setState({
                text:store.getState().demoreducer.age
            })
        })
    }
3. react-redux

之前上面的redux大家会发现一个问题 就是redux与react之间的耦合度非常高 代码不简洁(组件中有大量的第三方redux的东西 store)

为了解决这个问题 我们可以使用react-redux 这是一个专门为react开发的状态管理工具 使用他就可以非常方便的解决react与redux’之间的耦合度过高的问题

既然我们要学习react-redux 问题 我们之前学的redux’是不是就不用了 是不是就白学了

1.下载 npm install --save react-redux

2.Provider 提供store数据的 通过provider来传递store到所有的子组件身上 去index.js使用provider来进行数据的传递

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './components/rddemo.jsx';
import reportWebVitals from './reportWebVitals';

// 1.引用provider
import {Provider} from "react-redux"
// 3.引用store对象
import store from "./store/index.js"

ReactDOM.render(
  // 2.使用provider传递store对象
  // 4.传递
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

3在组件中连接react-redux 并且使用数据 connect(他是连接react与react-redux的一个方法 通过这个方法就可以把组件与react-redux连接到一起方便使用数据)

import React, { Component } from 'react'
// 1.引用连接
import {connect} from "react-redux"
class rddemo extends Component {
    render() {
        return (
            <div>
                <h1>react-redux的使用</h1>
                {/* 3.使用数据             */}
                {/* <h1>{this.props.state.模块名.数据}</h1> */}
                <h1>{this.props.state.rddemoreducer.name}</h1>
            </div>
        )
    }
}
// 2.修改暴露到最下面  使用connect来进行连接操作
// state就是react-redux会把store中的数据自动注入到形参中
export default connect(state=>({state}))(rddemo)







数据修改

原来在redux’中数据修改 不仅仅是单纯的修改 我们还需要 使用subscribe来进行监听非常麻烦

但是在react-redux中我们可以直接修改

import React, { Component } from 'react'
// 1.引用连接
import {connect} from "react-redux"
class rddemo extends Component {
    fun=()=>{
        // 4数据的修改
        this.props.dispatch({type:"upname"})
    }
    render() {
        return (
            <div>
                <h1>react-redux的使用</h1>
                {/* 3.使用数据             */}
                {/* <h1>{this.props.state.模块名.数据}</h1> */}
                <h1>{this.props.state.rddemoreducer.name}</h1>
                <button onClick={this.fun}>点我修改</button>
            </div>
        )
    }
}
// 2.修改暴露到最下面  使用connect来进行连接操作
// state就是react-redux会把store中的数据自动注入到形参中
export default connect(state=>({state}))(rddemo)







15. 数据请求

15.1分类

1. jquery 或者是原生的ajax请求

(1)下载 npm install --save jquery

import React, { Component } from 'react'
// 1.引用jquery
import $ from "jquery"
export default class jquerydemo extends Component {
    componentDidMount() {
        $.ajax({
            url:"/api/ceshi",
            type:"GET",
            success:(ok)=>{
                console.log(ok)
            }
        })
    }
    
    render() {
        return (
            <div>
                <h1>使用jquery请求</h1>
            </div>
        )
    }
}

2. axios的使用

axios是一个第三方的数据请求库 所以在react中使用的方式和vue中使用的方式是一模一样的

详见vue的笔记

3. fetch请求

fetch是原生js新推出的一个请求方式 他和jqueryajax 原生ajax axios 最大的区别是 他不在使用xhr 对象

他是一个新的请求方式

但是 由于是比较新的请求方式所以对老旧浏览器的兼容性就不好

import React, { Component } from 'react'

export default class fetdemo extends Component {
    componentDidMount() {
        fetch("/api/ceshi",{method:"GET"})
        .then(req=> req.json())
        .then((ok)=>{console.log(ok)})
    }
    
    render() {
        return (
            <div>
                <h1>使用fetch来进行数据请求</h1>
            </div>
        )
    }
}

15.2. 扩展

1. fetch vs ajax vs axios区别

原生的ajax 就是对于XMLHttpRequest对象进行数据请求ongoing的一个技术 他是属于原生js的一个技术 对个请求之间如果有先后的顺序 那么就会出现回调地狱

jqueryajax 就是对原生ajax进行一个一个封装 减低了兼容性与语法难度

axios 他是基于promise 本质上就是对XHR对象进行了封装 减低了兼容性与语法难度 但是他是使用promise来进行封装的实现 让他更加符合当下的语法

fetch 他不是依赖于XHR 他是原生js给我们提供的一个最新的数据请求的标准 但是兼容性一般

2. 跨域

通过devServer完成代理跨域

1.找到配置文件来进行跨域

node_modules/react-scripts/config/webapckDevServer.config.js 文件下进行配置

2.找到proxy节点替换如下

  // `proxy` is run between `before` and `after` `webpack-dev-server` hooks
    proxy:{ //设置devServe解决跨域问题
      "/api":{
          // 我们需要告诉devserver帮我们解决那个地址的跨域
          target:"http://localhost:3000/",
          changeOrigin:true,
          ws:true,
          // 替换地址
          pathRewrite:{
              "^/api":""
          }
      }
  },

3.修改组件中的请求地址为/api

4.重启项目

修改跨域的文件层级太深了 有点麻烦 有没有办法解决?

使用弹射即可解决

3. 弹射

免责声明

弹射是不可逆的 就是去公司了 不要随便弹射 如果你要弹射 先和项目经理沟通下 在进行

弹射就是把隐藏层级很深的配置文件 通过弹射放到项目的根路径下(好处就是哪些配置文件查找起来方便了 但是缺点 就是这些文件会导致项目的层级看起来很乱)

使用npm run eject 来进行弹射

会出现如下错误

his git repository has untracked files or uncommitted changes:

package.json
M src/index.js
src/api/
src/components/
src/store/
src/util/

Remove untracked files, stash or commit any changes, and try again.npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! myapp@0.1.0 eject: `react-scripts eject`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the myapp@0.1.0 eject script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     C:\Users\xixi\AppData\Roaming\npm-cache\_logs\2022-01-04T03_31_21_115Z-debug.log

出现错误的原因是因为 他需要让我们在git上面进行一次提交

所以我们使用git 先git add . 在 git commit -m 提交一次 在重新npm run eject

16. json-server模拟数据

仅限于2022年1月5日上课的这个时间段内出现的下载失败大家可以切换以下 当前这个修改npm 下载路径从淘宝镜像源切换到npm默认路径 npm config set registry http://registry.npmjs.org/

切换回淘宝镜像 npm config set registry http://registry.npm.taobao.org/

json-server 可以在react中进行模拟数据的实现 同时vue 小程序 都可以完成模拟数据的实现 并且语法都是一样的

1.下载 npm install -g json-server

2.查看版本 json-server --version

3.需要在项目下创建一个mock的文件夹用来容纳模拟数据 在其中创建模拟数据的xxx.json 写入如下内容

{
    "one":[
        111,2222,3333,444,55
    ]
}

4.启动模拟数据

cd到 mock文件夹下

输入 json-server --watch xxx.json --port 端口号

大家会发现基本的模拟数据已经成功了 但是 我们就一个接口 如果进行有很多个接口怎么办?

我们如果需要多个 不能新建很多个json 并且启动很多个服务

就是在你新建的这个json文件里面做手脚

{
    "one":[
        111,2222,3333,444,55,66,77,88,99,321,123
    ],
    "two":{
        "name":"xixi",
        "age":18
    },
    "three":{
        "title":"我是第三个"
    }
}

17. styled–components

什么是 CSS-in-JS?

“CSS-in-JS” 是指一种模式,其中 CSS 由 JavaScript 生成而不是在外部文件中定义。

style-components 是一个js库 它的主要作用就是使用js写css 因为这样一来就可以解决一些原有css不具备的能力 不能创建变量 函数 循环等等

传统的css痛点

1.全局污染

2.命名混乱

3.样式重用困难

4.代码冗余

使用

1下载 npm install --save styled-components

2设置样式

// css-in-js的方式完成样式的设置
// 1.引用styled
import styled from "styled-components"
// 这里的变量名首字母必须大写
let Democstyled=styled.div`
//想怎么写样式就怎么写
            h1{
                color: red;
            }
`
export default Democstyled

3.使用

import React, { Component } from 'react'
// 1.引用样式
import Democstyled from "./xiaoming/democstyled.js"
export default class democ extends Component {
    render() {
        return (
            <div>
                {/* 2.使用 */}
                <Democstyled>
                    <h1>我是测试styled-componets的组件</h1>
                   
                    <div className='demoh'>
                        <h1>我是占位的</h1>
                    </div>
                </Democstyled>
            </div>
        )
    }
}

18. 扩展

18.1. 性能优化

未优化例子

下面就是性能非常浪费的代码大家不同联系直接放到你们项目中即可

父组件

import React, { Component } from 'react'
import Zi from "./zi.jsx"
export default class fu extends Component {
    constructor(){
        super()
        this.state={
            arr:[
                {title:"111111",ck:false},
                {title:"111112",ck:false},
                {title:"111113",ck:false},
                {title:"111114",ck:false},
                {title:"111115",ck:false},
                {title:"111116",ck:false},
                {title:"111117",ck:false}
   //             大家自行赋值上面的数据几千航即可
            ]
        }
    }
    fu=(num)=>{
        console.log(num)
        let newarr=this.state.arr

        newarr[num].ck=!newarr[num].ck

        this.setState({
            arr:newarr
        })
    }
    render() {
        return (
            <div>
                {
                    this.state.arr.map((v,i)=>{
                        return (
                            <Zi text={v.title} bool={v.ck} index={i} fun={this.fu}/>
                        )
                    })
                }
                
            </div>
        )
    }
}


子组件
import React, { Component } from 'react'

export default class zi extends Component {
   
    render() {
        console.log("我是子组件")
        let {text,bool,index,fun}=this.props
        return (
            <div>
                <input type="checkbox" onChange={fun.bind(this,index)}/>
                <span style={{backgroundColor:bool?'red':''}}>{text}</span>
            </div>
        )
    }
}

解决性能浪费–性能优化

1.生命周期 — shouldComponentUpdate() 判断组件是否要更新 减少了不必要的组件渲染 提高了性能

import React, { Component } from 'react'

export default class zi extends Component {

    // 性能优化
    shouldComponentUpdate(nextProps,nextState){
        // 当新传递进来的props更新了  当前组件就重新更新
        // 但是剩下的没有更新  那么当前组件就不渲染更新

        return nextProps.bool!=this.props.bool
        
       
    }
   
    render() {
        console.log("我是子组件")
        let {text,bool,index,fun}=this.props
        return (
            <div>
                <input type="checkbox" onChange={fun.bind(this,index)}/>
                <span style={{backgroundColor:bool?'red':''}}>{text}</span>
            </div>
        )
    }
}

2.纯组件—PureComponent方式优化性能

纯组件是react中性能优化最终要的方法 当组件发生改变更新的时候 组件的props和state如果没有改变 那么当前render就不更新 他只会比较props和state的内存地址 如果地址相同 标识没有改变 那么他就自动让shouldcomponentupdate return false

// 纯组件优化性能
import React, { Component,PureComponent } from 'react'

export default class zi extends PureComponent {

    // 性能优化
    // shouldComponentUpdate(nextProps,nextState){
    //     // 当新传递进来的props更新了  当前组件就重新更新
    //     // 但是剩下的没有更新  那么当前组件就不渲染更新

    //     return nextProps.bool!=this.props.bool
        
       
    // }
   
    render() {
        console.log("我是子组件")
        let {text,bool,index,fun}=this.props
        return (
            <div>
                <input type="checkbox" onChange={fun.bind(this,index)}/>
                <span style={{backgroundColor:bool?'red':''}}>{text}</span>
            </div>
        )
    }
}

3.函数组件中可以使用 React.memo() 来进行性能优化 但是当前这个方法是在函数组件中使用的

18.2. cra脚手架如何修改端口

找到 node_modules/react-scripts/scripts/start.js

修改如下内容

const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 你的端口;
const HOST = process.env.HOST || '0.0.0.0';

19. HOC 高阶组件

在react组件开发项目的过程中 常常有这样一个场景 一些功能需要被复用 这个时候在react中我们就可以使用高阶组件来完成这样的一个需求

HOC高阶组件 ----- 参数是一个组件 返回值还是一个组件

1.创建高阶组件

// 高阶组件
// HOC 高阶组件---- 参数是一个组件  返回值还是一个组件


import React, { Component } from 'react'

let List=(Com)=>{
    return class index extends Component {
        render() {
            return (
                <div>
                    <Com/>
                      <h1>by 2112</h1>
                </div>
            )
        }
    }

}

export default List



2.使用高阶组件

import React, { Component } from 'react'
// 1.引用
import List from "../hoc/index.js"
class demo extends Component {
    render() {
        return (
            <div>
                <h1>我是测试高阶组件的例子</h1>
              
            </div>
        )
    }
}
// 2.暴露高阶组件
export default List(demo)

20. 路由

根据url地址栏的不同来切换不同的组件页面 实现spa单页面应用 整个项目只有一个完整的页面 同时在切换页面的时候不会刷新地址栏 没有切换白屏的问题 用户体验更好

20.1. 路由的分类

1.react-router 仅仅给我们提供了一些基本的核心路由功能

2.react-router-dom 除了基本的核心路由功能之外 还加入了很多便捷性的api 方便我们在实现路由功能的时候有更多的辅助性的api

20.2. 路由模式

HashRouter url中带# 并且上线后刷新不会丢失 兼容性好

BrowerRouter url中不带# 上线后刷新会丢失 兼容性一般

20.3. 路由的基本使用

1.下载路由模块

npm install --save react-router-dom@5.2.0

2.设置路由模式

在index.js下设置路由模式

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './components/demo.jsx';
import reportWebVitals from './reportWebVitals';
// 1.引用路由模式
import {BrowserRouter} from "react-router-dom"
ReactDOM.render(
  // 2.包裹根组件
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  document.getElementById('root')
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

3.创建组件页面

在src下新建一个pages的文件夹 用来新建组件页面

4.配置路由规则与出口的设置
1.在src下新建一个router的文件夹
2.创建路由配置文件
3.路由规则与出口
import React, { Component } from 'react'
// 1.引用你的路由页面
import Home from "../pages/home.jsx"
import Phone from "../pages/phone.jsx"
import User from "../pages/user.jsx"
import Shop from "../pages/shop.jsx"
// 2.创建出路由配置  使用Route来进行规则与出口的设置

import {Route} from "react-router-dom"

export default class index extends Component {
    render() {
        return (
            <div>
                {/* 3.使用route配置 */}
                {/* <Route path="/路径" component={你要使用的组件页面}/> */}
                <Route path="/home" component={Home}/>
                <Route path="/phone" component={Phone}/>
                <Route path="/user" component={User}/>
                <Route path="/shop" component={Shop}/>
            </div>
        )
    }
}

4.设置成根组件

在index.js中吧我们刚才创建好的路由配置文件变成根组件即可

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
// 把路由的配置文件设置成根组件
// 把路由的配置文件设置成根组件
// 把路由的配置文件设置成根组件
// 把路由的配置文件设置成根组件
// 把路由的配置文件设置成根组件
import App from './router/index.js';
import reportWebVitals from './reportWebVitals';
// 1.引用路由模式
import {BrowserRouter} from "react-router-dom"
ReactDOM.render(
  // 2.包裹根组件
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  document.getElementById('root')
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

20.4. 路由导航

20.4.1. 声明式
Link

使用to属性来设置你的路径的

NavLink

使用to属性来设置你的路径的 会自动在选中的导航中添加一个样式的类名 active

但是这个active的类名 今后可能会在很多地方都会出现这个名字 出现的频率太高了 可能我们在设置样式的时候 会污染其他的内容

可以进行这个动态选中类名的修改 使用activeClassName来进行修改

    			 <NavLink to="/home" activeClassName='xiaoming'>点我home</NavLink>
                <NavLink to="/phone" activeClassName='xiaoming'>点我phone</NavLink>
                <NavLink to="/user" activeClassName='xiaoming'>点我user</NavLink>
                <NavLink to="/shop" activeClassName='xiaoming'>点我shop</NavLink>

注意注意

navlink的声明式 导航 在各位同学课下联系的时候可能会出现 这个类名不出来的情况

如果出现类名出不来的情况 那么 在启动项目的时候 大家不要在vscode内置终端中启动 而是在外部的cmd中来进行项目的启动即可解决

20.4.2. 编程式

this.props.history.push("/去哪里")

import React, { Component } from 'react'

export default class home extends Component {
    fun=()=>{
        // 编程式导航
        this.props.history.push("/phone")
    }
    render() {
        return (
            <div>
                home
                <button onClick={this.fun}>点我去phone</button>
            </div>
        )
    }
}

编程式导航常见问题

我们会发现 代码是没有错的 但是当我调用 编程式导航 会在浏览器的控制台出现 如下错误

Uncaught TypeError: Cannot read property 'push' of undefined

j出现上述错误的原因是因为 我们在使用编程式导航的时候 因为这个代码出现在了 没有被路由所管理的页面中 那么这个编程式导航就不能正常使用

如果我就是想在被路由所管理的页面中使用编程式导航怎么办?

那么我们就需要使用withRouter 这个高阶组件来进行完成

1.在不是被路由所管理的页面中引用withRouter这个高阶组件

import React, { Component } from 'react'
// 1.引用withRouter
import {withRouter} from "react-router-dom"
 class demob extends Component {
    fun=()=>{
        this.props.history.push("/home")
    }
    render() {
        return (
            <div>
                <button onClick={this.fun}>点我去home</button>
            </div>
        )
    }
}
// 2.使用高阶组件
export default withRouter(demob)
20.4.3. withRouter是什么?

withRouter是一个高阶组件 它的作用就是因为不是被路由所管理的页面是没有路由个跳转的属性 通过withrouter就可以给当前页面添加路由跳转属性 从而可以使用路由跳转的各种方法

20.4.4. 更多的编程式导航

push()

this.props.history.replace()// 替换

this.props.history.goBack()//后退

this.props.history.goForward()//前进

20.4.5. 路由更多设置
20.4.6. 路由-404页面

就是当用户在访问我们项目的时候 由于在url中输入了我们并没有的页面的时候出现白屏 为了解决这个问题 当用户在url中输入没有的页面时候 我们可以用一个404的错误提示页面告知用户当前路径有问题

1.创建404页面组件

2.配置404页面

 			    <Route path="/home" component={Home}/>
                <Route path="/phone" component={Phone}/>
                <Route path="/user" component={User}/>
                <Route path="/shop" component={Shop}/>

                {/* 路由配置的最下面来进行404页面的配置 */}
                <Route component={No}/>

当我们配置完404之后会发现 这个404页面在所有的页面中都会显示

解决404页面重复出现可以使用下面的技术

20.4.7. Switch 唯一渲染

为了解决 react 的路由在匹配到内容之后还会向下继续匹配 渲染多个内容的问题 所以我们使用swtich来进行唯一渲染 让react的路由在匹配到指定内容之后不继续向下渲染的问题

1.在配置页面引用Switch

2.使用Switch包裹所有的路由配置从而实现唯一渲染

   {/* 唯一渲染 */}
               <Switch>
                    <Route path="/home" component={Home}/>
                    <Route path="/phone" component={Phone}/>
                    <Route path="/user" component={User}/>
                    <Route path="/shop" component={Shop}/>

                    {/* 路由配置的最下面来进行404页面的配置 */}
                    <Route component={No}/>
                </Switch>
20.4.8. 重定向与精准匹配

redirect来实现重定向

<Redirect from="/" to="/去哪里"/>
 			 <Switch>
                    <Route path="/home" component={Home}/>
                    <Route path="/phone" component={Phone}/>
                    <Route path="/user" component={User}/>
                    <Route path="/shop" component={Shop}/>
                    {/* 设置重定向 */}
                    <Redirect from="/" to="/home"/>
                    {/* 路由配置的最下面来进行404页面的配置 */}
                    <Route component={No}/>
                </Switch>

但是我们设置好了重定向之后 会发现404页面的功能就没有了

原因 因为重定向的from里面填写的是/ 这个/会匹配所有路径所以导致404这个匹配项进步去

解决 精准匹配exact 来帮助我们只是/的时候被重定向拦截 其余都不进入重定向

<Redirect from="/" to="/home" exact/>
 				{/* 唯一渲染 */}
               <Switch>
                    <Route path="/home" component={Home}/>
                    <Route path="/phone" component={Phone}/>
                    <Route path="/user" component={User}/>
                    <Route path="/shop" component={Shop}/>
                    {/* 设置重定向 */}
                    <Redirect from="/" to="/home" exact/>
                    {/* 路由配置的最下面来进行404页面的配置 */}
                    <Route component={No}/>
                </Switch>
20.4.9. 二级/多级路由

react中二级路由没有更多的语法 就是把一级路由的写法在重新写一遍

1.创建二级路由的组件页面

2.在对应的一级路由页面中 直接使用Route进行二级路由的配置

import React, { Component } from 'react'
// 1.引用二级路由页面
import Era from "./era.jsx"
import Erc from "./erc.jsx"
import Erd from "./erd.jsx"
// 2.引用Route
import {Route,NavLink} from "react-router-dom"
export default class phone extends Component {
    render() {
        return (
            <div>
                phone
                {/* 4.设置二级路由的路由导航 */}
                <NavLink to="/phone/era">era</NavLink>&nbsp;&nbsp;
                <NavLink to="/phone/erc">erc</NavLink>&nbsp;&nbsp;
                <NavLink to="/phone/erd">erd</NavLink>&nbsp;&nbsp;

                {/* 3.使用Route来进行二级路由的配置 */}
                {/* <Route path="/一级路由/二级路由" component={你要引用的二级路由页面}/> */}
                <Route path="/phone/era" component={Era}/>
                <Route path="/phone/erc" component={Erc}/>
                <Route path="/phone/erd" component={Erd}/>
            </div>
        )
    }
}

20.5. 路由传参

20.5.1. params方式
  1. 在路由规则上配置接收参数
  {/* params传参第一步配置接收参数 */}
 <Route path="/shop/:xiaoming" component={Shop}/>
  1. 发送数据
1. 声明式
<Link to="/去哪里的路径/你要传递的参数">xxxx</Link>
2. 编程式

this.props.history.push("/去哪里的路径/你要传递的参数")

import React, { Component } from 'react'
import Demob from "../components/demob.jsx"

import {Link} from "react-router-dom"
export default class user extends Component {
    render() {
        return (
            <div>
                user
                {/* 发送数据 */}
                <Link to="/shop/我是传递的数据">点我发送数据给shop页面接收</Link>
                <Demob/>
            </div>
        )
    }
}

3. 接收数据

this.props.match.params.xxx 数据的接收

// 接收数据
    componentWillMount() {
        console.log(this.props.match.params.xiaoming)
    }
4. 总结

优势: params方式进行参数传递的时候 刷新地址栏 数据依然存在

缺点: 只能传递字符串 并且参数传递过多的时候 url会非常的难看

20.5.2. state方式
1.发送数据
import React, { Component } from 'react'

export default class user extends Component {
    fun=()=>{
        // state方式发送数据
        // this.props.history.push({pathname:"/路径",state:{发送数据的key:发送数据的value}})
        this.props.history.push({pathname:"/shop",state:{text:"我是传递的数据state"}})
    }
    render() {
        return (
            <div>
                user
                <button onClick={this.fun}>点我传递数据到shop中</button>
            </div>
        )
    }
}

2.接受数据

this.props.location.state.xxx

import React, { Component } from 'react'

export default class shop extends Component {

    render() {
        return (
            <div>
                shop--{this.props.location.state.text}
            </div>
        )
    }
}

3. 总结

有点 在url地址栏中不显示传递的数据 并且传递数据的时候可以传递对象

缺点 刷新地址栏数据会丢失

20.6. 扩展 — 路由的render写法

就是在路由跳转显示的时候 可以对显示的路由进行一些验证上的写法

import React, { Component } from 'react'
import Home from "../views/home.jsx"
import Phone from "../views/phone.jsx"
import User from "../views/user.jsx"
import Shop from "../views/shop.jsx"
import No from "../views/no.jsx"



import {Route,NavLink,Switch,Redirect} from "react-router-dom"
export default class index extends Component {
    render() {
        return (
            <div>
                <NavLink to="/home">home</NavLink>&nbsp;
                <NavLink to="/phone">phone</NavLink>&nbsp;
                <NavLink to="/user">user</NavLink>&nbsp;
                <NavLink to="/shop">shop</NavLink>&nbsp;
                
                <Switch>
                    <Route path="/home" component={Home}/>
                    {/* <Route path="/phone" component={Phone}/> */}
                    {/* render写法在路由跳转之前可以做一些验证 */}
                    <Route path="/phone" render={()=>{
                        if(false){
                            return <Phone/>
                        }else{
                            return <Home/>
                        }
                    }}/>


                    <Route path="/user" component={User}/>
                    <Route path="/shop" component={Shop}/>
                    <Redirect from="/" to="/home" exact/>
                    {/* 404 */}
                    <Route component={No}/>
                </Switch>
            </div>
        )
    }
}

21. 状态提升

1. 基本概念

多个组件要反应相同的数据变化的时候 我们就可以使用状态提升(一条数据在改变之后要影响多个组件的展示结果)

如果多个组件要反映出相同的数据变化 那么在这个时候 我们可以把这一个数据方法这些组件的父组件之上 用状态来标识 当状态修改了 通过正向传值把这些数据传递到需要反映变化的组件之上 那么这些组件数据变化就想相同了

父组件
import React,{useState} from "react"
import Zia from "./zia"
import Zib from "./zib"
let Fu:React.FC<{}>=()=>{
    let [text,setText]=useState("你好")

    let fun=()=>{
        setText("你坏")
    }
    return (
        <div>
            <h1>fufufufuff--{text}</h1><button onClick={fun}>点我修改</button>
            <Zia title={text}/>
            <Zib title={text}/>
        </div>
    )
}
export default Fu


子组件
import React from "react"

interface IProps{
    title:String
}
let Zia:React.FC<IProps>=(props)=>{
    return (
        <div>
            <h1>zizizaaaaaaaa---{props.title}</h1>
        </div>
    )
}
export default Zia

二、ts+react

1. 创建

使用 cra来进行创建 create-react-app 项目名 --template typescript

2. 组

创建组件使用.tsx结尾 组件中的内容没有区别
但是在引用组件的时候 不加.tsx结尾的后缀名

3. 传值

3.1. 正向传值-this.props

需要使用接口与泛型配合使用定义数据类型

父组件
import React, { Component } from 'react'
import Zi from "./zi"
export default class demo extends Component {
    render() {
        return (
            <div>
                我是一个组件
                <Zi title="我是正向传值的数据" age="6666"/>
                
            </div>
        )
    }
}


子组件
import React, { Component } from 'react'
// 定义props的数据类型
interface IProps{
    title:String,
    age:string
}

export default class zi extends Component<IProps> {
    render() {
        return (
            <div>
                zzizizizzizizi--{this.props.title}--{this.props.age}
            </div>
        )
    }
}

3.2. 逆向传值-state

创建状态

import React, { Component } from 'react'
import Zi from "./zi"
export default class demo extends Component {

    public constructor(props:any){
        super(props)
        // 创建状态
        this.state={
            text:"我是state的数据"
        }
    }

    public render() {
        return (
            <div>
                我是一个组件
                <Zi title="我是正向传值的数据" age="6666"/>
                
            </div>
        )
    }
}

import React, { Component } from 'react'
import Zi from "./zi"

interface IState{
    text?:string
}
// 我们使用泛型已经把state的接口类型传递进来了但是为什么还报错呢?
// 原因是因为  当前这个组件  会把泛型中的第一个数据当成props的数据类型
// 而第二个才会当成state的数据类型 
export default class demo extends Component<{},IState> {

    public constructor(props:any){
        super(props)
        // 创建状态
        this.state={
            text:"我是state的数据"
        }
    }

    public render() {
        return (
            <div>
                {/* 使用状态 */}
                我是一个组件---{this.state.text}
                <Zi title="我是正向传值的数据" age="6666"/>
                
            </div>
        )
    }
}

// 函数组件的首字母要大写
// let Fun=()=>{
//     return (
//         <div>xxxxxx</div>
//     )
// }

// export default Fun

import React from "react"
// 函数组件的首字母要大写
// FC 是functionComponent的简写
let Fun:React.FC<{}>=()=>{
    return (
        <div>
            你好么么哒
        </div>
    )
}
export default Fun

3.3. props-待验证ing

父
// 函数组件的首字母要大写
// let Fun=()=>{
//     return (
//         <div>xxxxxx</div>
//     )
// }

// export default Fun

import React from "react"
import Zi from "./zi"
// 函数组件的首字母要大写
// FC 是functionComponent的简写
let Fun:React.FC<{}>=()=>{
    return (
        <div>
            你好么么哒
            <Zi title="你好我是正向传值"/>
        </div>
    )
}
export default Fun



子
import React from "react"

interface IProps{
    title:String
}

// 传递接口指定类型
let Zi:React.FC<IProps>=(props)=>{
    return (
        <div>
            zizizizizzizizizi--{props.title}
        </div>
    )
}

export default Zi 

5. HOOK

5.1. 简介

react中核心概念就是组件 在react中16.8版本之后 函数组件又重登舞台 因为函数组件默认不能使用状态 ref 生命周期等内容,但是16.8版本之后 react 有一个新的概念叫 HOOK 通过 HOOK 可以让 react 使用上述不能使用的内容

react HOOK 是react16.8新增的一个特性 主要作用 就是让无状态组件/函数组件 可以使用状态 ref等一些特性

HOOK 不能在 class组件中使用

5.2. useState—可以让函数组件使用状态

useState 是reactHOOK给我们提供的 最基本最常用的一个HOOK 主要作用就是用来管理当前本地的状态

useState() 返回值是一个数组(长度为2)数组的第一项标识的是当前的值 数组的第二项标识的时候修改这个值的函数

let [xiaoming , setXiaoming]=useState(初始值)

import React,{useState} from "react"


let Fun:React.FC<{}>=()=>{
    // 函数组件使用useState来进行状态的创建
    let [xiaoming,setXiaoming]=useState("你好")

    let demoFun=()=>{
        // 修改useState创建的状态
        setXiaoming("你坏")
    }
    return (
        <div>
            {/* 使用useState创建的变量 */}
            <h1>useState-----{xiaoming}</h1>
            <button onClick={demoFun}>点我修改xiaoming这个状态</button>
        </div>
    )
}

export default Fun

5.3. 多个状态呢?

需要给useState()传递一个对象

import React,{useState} from "react"


let Fun:React.FC<{}>=()=>{
    // 创建多个状态
    let [xiaoming,setXiaoming]=useState({
        name:"xixi",
        age:18,
        sex:"nan"
    })

    let upFun=()=>{
        // 修改状态
        setXiaoming({...xiaoming,name:"哈哈"})
    }
  
    return (
        <div>
            {/* 读取 */}
           <h1>useState创建多个状态----{xiaoming.name}---{xiaoming.age}</h1>
           <button onClick={upFun}>点我修改状态</button>
        </div>
    )
}

export default Fun

5.4. useRef

hook中让函数组件可以使用ref

import React,{useRef} from "react"
let Fun:React.FC<{}>=()=>{
    // 创建ref
    let u:any=useRef(null)

    let clickFun=():void=>{
        u.current.style.color="red";
        console.log(u.current)
    }
    return (
        <div>
            <h1>useRef的使用</h1>
            {/* 使用ref */}
            <h1 ref={u}>修改我的颜色</h1>
            <button onClick={clickFun}>点我修改上面的颜色</button>
        </div>
    )
}
export default Fun

5.5. useEffect

使用useEffect来对 react 的生命周期进行替代 componentDidMount 与 componentDidUpdate和componentWillUnmount 三个生命周期的组合

import React,{useEffect} from "react"
let Fun:React.FC<{}>=()=>{
    useEffect(()=>{
        console.log("我被调用了")
    })


    return (
        <div>
            <h1>useEffect</h1>    课件休息课件休息课件休息课件休息课件休息课件休息
        </div>
    )
}
export default Fun

5.6. useReducer

useReducer和redux没有关系的

主要就是修改数据的 他和useState最大的区别就是在处理多个数据修改的时候降低了复杂度(对一个数据有多个修改的时候 使用useReducer)

基本的数据创建

import React,{useReducer} from "react"

let Fun:React.FC<{}>=()=>{
    let reducer:any=(state:number,action:object)=>{
        return state
    }

    // 1.初始化数据
    // let [代表数据的变量,触发修改动作]=useReducer(reducer(修改数据的动作方法),初始值)
    let [state,dispatch]=useReducer(reducer,66)
    return (
        <div>
            <h1>useReducer---{state}</h1>
        </div>
    )
}
export default Fun

修改数据

import React,{useReducer} from "react"

let Fun:React.FC<{}>=()=>{
    let reducer:any=(state:number,action:any)=>{
        switch(action.type){
            case "ADD":
                return state+1;
            case "DEL":
                return state-1;
            default :
            return state

        }
        
    }

    // 1.初始化数据
    // let [代表数据的变量,触发修改动作]=useReducer(reducer(修改数据的动作方法),初始值)
    let [state,dispatch]:any=useReducer(reducer,66)

    let add=():void=>{
        dispatch({type:"ADD"})
    }
    let del=():void=>{
        dispatch({type:"DEL"})
    }
    return (
        <div>
            <h1>useReducer---{state}</h1>

            <button onClick={add}>+1</button>
            <button onClick={del}>-1</button>
        </div>
    )
}
export default Fun

https://www.jianshu.com/p/2e177ad95594

6. 虚拟DOM

React的高性能体现就是虚拟DOM

浏览器出现的原因就是为了把数据(图片文字等内容展现给用户) 但是随着技术的发展浏览器现在的功能强度和功能性已经和设计之处完全不同了 在浏览器中出现了大量的特效需要展示 游戏需要运行 各种各样的复杂功能接踵而至

慢慢的开发者就觉的浏览器的效率就已经不足以支撑我们当前要运行的内容了 有很大部分原因 就是dom操作影响了性能

6.1. 传统的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>Document</title>
</head>
<body>
    <div>
        
    </div>
    <script>
        document.getElementsByTagName("div")[0].innerHTML="你好"
        // 从上面的代码大家会发现  浏览器如果要修改一个简单的内容  那么就需要进行一次dom操作
        // 如果要修改很多个  那么就会源源不断的进行dom的修改 一直迭代下去
    </script>
</body>
</html>

6.2. react中

对上面大量的dom操作进行了修改和优化

具体实现

react会把浏览器上渲染的dom(真实dom)转换成一个js对象(虚拟dom virtualDOM)react会拿着这个虚拟dom进行数据的修改与数据的更新 把所有要修改 或者是需要更新的内容进行统一的操作 然在一起进行页面的更新

6.3. react高性能的原理

在web开发中 我们需要把数据的变化反映到页面中 那么这个时候就不得不进行dom的操作 而负载的dom操作又是影响浏览器性能的最大原因

react中就引用了虚拟dom virtualDOM 可以在浏览器中用js来实现一套DOM的api react在开发的过程中对所有的dom操作都是用这个虚拟dom进行 当数据变化的时候 react就会重新构建dom树 然后react会比较新修改的和原始的dom树得到新的比较结果从而根据这个结果来进行内容的修改(比较的过程就是用diff算法 来尽可能少的比较出差异 对这个差异进行修改) 最终进行页面的render更新

6.4. 扩展–react最新的核心算法

在react16x之后 发布了一张全新的react算法 叫做 react Fiber 是对核心算法的重新实现 之前使用的都是diff算法 整个更新都是同步的 这样可能也是会造成性能上的损耗 当react要加载或者更新整个组件数据的时候 会多出很多的事情 比如 要调用每个组件的生命周期函数 计算和对比虚拟dom 最后更新 整个过程都是同步的 加载事件耗时太长

Fiber 实现: 分片 会把一个消耗事件很长的过程拆分成一个个的小片端 每个小片段的事件很短 虽然总时间 没有太大变化 但是在每个小片段执行完 都会给其他的任务一个执行的机会 这样一来 线程就不会被独占 其他的任务依然可以有运行机会

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kalrry

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值