React

React

React是什么:

react 是一个用于构建用户界面的JavaScript库

React特点:

1、声明式编码

2、组件化编码 将一个较大复杂的页面拆分成几个可复用的部分封装成多个组件,在组合使用 组件可以被反复使用

3、引入了JSX语法,编写高效,可以封装多个组件,实现页面复用

4、内置diff算法,运行高效

5、一次学习,随处编写 不仅可以开发web应用(reaact-dom),还可以开发原生安卓或ios应用 (react-native)

React 初体验

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./js/react.development.js"></script>
    <script src="./js/react-dom.development.js"></script>
</head>
<body>
    <div id="root"></div>
    <script>
        let html = React.createElement('div',null,React.createElement('h1',{title:"react二次体验"},'react二次体验'),React.createElement('div',{title:'内容'},'就只是下要嵌套一下'))
        ReactDOM.render(html,document.getElementById('root'))
    </script>
</body>
</html>

createElement 可以接受无数个参数 ,从第三个开始都是当作标题体存在(关系是嵌套关系)

createElement 标题的属性可以写对象 ,也可以写null、undefined

React.createElement 创建一个虚拟dom对象

虚拟dom特点

1、属性比较少

2、更新高效 (因为真实dom属性比较多 ,虚拟dom属性比较少 ,所以当挂载的时候对比的就比较少,更新也就更高效)

3、虚拟dom最后也会变成真实dom

使用jsx创建虚拟dom对象

使用jsx 需要在script标签中 配置 type = “text/babel”

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./js/react.development.js"></script>
    <script src="./js/react-dom.development.js"></script>
    <!-- 引入babel 将jsx代码转换成js代码 -->
    <script src="./js/babel.min.js"></script>
</head>
<body>
    <div id="rood"></div>
    <script type="text/babel">
        let jsx = <div>
                <h1 title='标题'>标题</h1>
                <div>hell world!</div>
            </div>
        ReactDOM.render(jsx,document.getElementById('rood'))
    </script>
    
</body>
</html>

jsx

jsx是什么

全称 : javascript xml

作用:在js中更加高效的编写html代码

jsx特点

1、不能用引号包裹 , 但是可以用(小括号) 包裹

2、JSX 与 JS 拼接的时候需要用 {花括号} 表达式包裹

3、JSX 设置class 时 ,需要使用className (因为 class 类是关键字)

4、JSX设置style时 ,需要使用 {{ }} (外面 { } 是表达式的花括号 , 里面 { } 是style的花括号 ,因为style是对象

5、JSX 必须有跟标签包裹

6、JSX中的单标签需要有结束符号

7、JSX中的标签名需要注意 (1)小写字母的标签,会自动找到HTML中对应的标签。 (2)大写字母的标签,会自动找到React中对应的组件,找不到会报错。(3)如果编写的是html中的标签 ,字母需要小写 , 如果编写的是React中的组件, 字母需要大写。

JSX表达式特点

1、书写格式 : { }

2、存放的内容 :

  • (1)字面量数据、 变量、 虚拟dom、
  • (2)string number array 可以直接渲染到页面中(是有意义的)array会自动循环遍历
  • (3)unmber null boolean Symol() 不可以直接渲染到页面 ,但是会有空标签显式 (没有意义)
  • (4)object 不可以放入字面量中 会报错
  • (5)function 需要看返回值 ,返回值如果是 (2) 那就有意义; 返回值如果是 (3) 那么就没有意义;

代码执行顺序

1、顺序结构 2、选择结构 3、循环结构

React条件渲染

条件渲染有三种:

  • 1、if…else
  • 2、三元运算符
  • 3、逻辑与&&
 <div id="root"></div>
    <script type="text/babel">
        let vip = true
        let vdom 
        // if...else
        if (!vip){  //判断如果是vip不显式广告
           vdom = <div className='box'>广告</div>
        }
        // 三元运算符
        vdom = !vip ? null : <div className='box'>广告</div>
        // 逻辑 && 判断
        vdom = !vip &&  <div className='box'>广告</div>
        ReactDOM.render(vdom,document.getElementById('root'))
    </script>

React列表渲染

在JSX中使用循环只能使用 map , 因为它有返回值

<div id="root"></div>
    <script type="text/babel">
      let arr = ['a','b','c',1,2,3];
      let vdom = <div>
            {
                arr.map(item =>{
                    return <div>{item}</div>
                })
            }        
        </div> 
      ReactDOM.render(vdom, document.getElementById("root"));
    </script>

JS中的循环

  • 1、for 已知条件、已知范围
  • 2、while 未知条件、未知范围
  • 3、do whlie 至少执行一次
  • 4、for in 遍历对象
  • 5、for of 遍历数组
  • 6、forEach 循环遍历 (没有返回值)
  • 7、map 循环遍历 (有返回值 且 返回一个新数组 ,对原数组没有影响)

diff算法 (比较算法)

diffing 算法 也叫 比较算法 。 是比较新老dom的结构 对比新dom老dom之间有哪些变动 ,有变动的才改变,没有变动的就不做改变 ,key 表示数据的唯一性 可以使用id当做key值(index 不可以当做key , 因为index会变化)

React 的 事件绑定

  • 1、react 中的事件执行机制和原生js一致
  • 2、react 中的事件是一个合成事件 , 对原始js的事件进行重新封装,解决了不同浏览器的兼容性问题
  • 3、react 中的事件名 必须要使用小驼峰
  • 4、react 中事件必须调用函数
  • 5、react 事件绑定只能使用行内绑定方式
  • 6、react 中的JSX语法使用了严格模式 ,事件回调函数中的this值向了undefind
  • 7、react 中获取事件对象,只能使用 e 或 event
  • 8、react 中阻止默认事件行为 不能使用 return false 只能使用 e.preventDefault()
<div id="root"></div>
    <script type="text/babel">
        function fn(){
            alert("外部函数调用")
        }
        function fn1 (){
            return () => {
                alert('外部函数括号调用')
            }
        }
        let vdom = (
            <div>

     {/* 函数调用 */}
                <button onClick = {function(){
                    alert('普通函数调用')
                }} >普通函数调用</button>
                <button onClick = {()=>{
                    alert("箭头函数调用")
                }}>箭头函数调用</button>
                {/* 外部函数不写 括号 是点击后调用的 , 写 括号 打开浏览器会自动调用 */}
                <button onClick = {fn} >外部函数不写()调用</button>
                {/* 写括号调用 ,调用的是 函数内部返回的 函数方法 的调用 */}
                <button onClick = {fn1()} >外部函数写()调用</button>
          
      {/* 阻止默认事件 */}
                 {/* 点击获取标签文字 , 在jsx中 this 指向 undefined*/}
                <div onClick={(e)=>{
                  alert( e.target.innerHTML)
                 }} >这是一句话 ,点击获取它</div>
               {/* 点击 阻止默认行为 两种方法return false在jsx中无法使用e.preventDefault 可以在jsx中使用*/}
               <a onClick={(e)=>{
                 e.preventDefault()
               }} href="http://baidu.com">百度</a>

               {/* onChange 事件 在js中 失去焦点才触发  , 在jsx中改变就触发*/}
               <input type="text" onChange = {()=>{
                 console.log ('输入框内容发生变化了');
               }} />

               {/* onInput 事件 在js改变就触发  , 在jsx中改变就触发*/}
               <input type="text" onInput = {()=>{
                 console.log ('输入框内容发生变化了');
               }} />
        </div>)
        ReactDOM.render(vdom,document.getElementById('root'))
    </script>

默认行为

  • 1、表单会自动提交
  • 2、点击a链接会自动跳转
  • 3、鼠标右键展示 操作列表

组件

组件

本质:高度封装 js css html

特点: 解决代码耦合 , 实现代码复用

组件在 React 中分为函数组件和类组件

使用: 组件最终是用标签呈现出来的(标签是以大写字母开头)

组件其实就是在自定义标签

执行本质

  • 1、创建一个函数式组件
  • 2、函数组件内部必须返回一个虚拟DOM对象
  • 3、当使用组件标签时,react内部就会找到对应的同名函数,自动返回一个虚拟DOM

组件的区别

  • 1、存放位置
    • 一般组件 存放在 componets 文件夹中
    • 路由组件 存放在pages 文件夹中
  • 2、使用形式
    • 一般组件 <Header></Header>
    • 路由组件 <Route path = “/” element = {<Header></Header>}> </Route>

React中分为函数组件和类组件

1、函数式组件

函数式组件的执行过程 : 当使用组件标签的时候 , react内部会找到对应的同名函数 , 会自动执行这个函数 , 返回一个虚拟dom对象

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>函数式组件</title>
    <script src="./js/react.development.js"></script>
    <script src="./js/react-dom.development.js"></script>
    <script src="./js/babel.min.js"></script>
    
  </head>
  <body>
    <div id="root"></div>
    <script type="text/babel">
    //  函数式组件 
    //1、定义一个函数组件
    function Zujian (){
        return <div>这是一个函数是组件</div>
    }
    function Zujian2 (){
        return <div>这是第二个函数是组件</div>
    }
    // App 是组件的根
    function App (){
        return <div>
            <Zujian/>
            <Zujian2></Zujian2>
            </div>
    }
      ReactDOM.render(<App></App>, document.getElementById("root"));
    </script>
  </body>
</html>

2、类式组件

类式组件的执行过程: 当使用类式组件标签时, react内部会自动找同名的这个类,自动帮你实例化对象【new Fn ()】/ 【new Mengyao ()】 , 得到一个实例化对象后, 在去调用各自内部的 render 方法 , 返回一个虚拟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>
<script src="./js/react.development.js"></script>
<script src="./js/react-dom.development.js"></script>
<!-- 引入 babel  将jsx语法 转换为 js语法 -->
<script src="./js/babel.min.js"></script>
<body>
    <div id="root"></div>
 <script type="text/babel">
    class Fn extends React.Component{
        render(){
            return <div>这是类方法实现的组件</div>
        }
    }
    class Mengyao extends React.Component{
        render(){
            return <div>这是创建了一个自己的组件</div>
        }
    }
    class App extends React.Component{
        render(){
            return <div>
                <Fn></Fn>
                <Mengyao/>
                </div>
        }
    }
    ReactDOM.render(<App/>,document.getElementById("root"))
    
 </script>
</body>
</html>

class 类

1、构造方法 : 初始化实例化对象

  • constructor (构造器)

2、向实力对象添加 : 方法

  • run = ( ) => { }
  • run = function ( ) { }
  • 上面两个都是给实例化对象添加方法 , 箭头函数和普通函数都可以添加

3、向原型对象身上添加方法

  • run ( ) { }

4、向类身上添加静态方法

  • static
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
        class Father{
            //  属性
            uname = "张三";
            age;
             //类的静态成员   类
            static like;

            //构造方法 :  初始化实例对象
            constructor(uname,age,sex){
                this.uname = uname;
                this.age = age;
                this.sex = sex;
            }

            // 向实例对象添加 方法
            speak = ()=>{
                console.log("我是箭头函数");
            }
            run = function(){
                console.log("我是普通函数");
            }

            //向原型对象添加方法
            Cook(){
                console.log("我会做饭");
            }
        }

        class Son extends Father{
            // 属性
            height;

            //构造方法
            constructor(uname,age,sex,height){
                super(uname,age,sex);   //  调用父类的构造函数
                this.height = height;
            }

            //方法
            // 重写父类方法
            speak = ()=>{
                console.log("我是儿子,我是个普通函数");
            }
        }

        //实例化对象
      /*   let f = new Father("张三",18,"男");
        console.log(f);
        // f.speak();
        f.run();
        console.dir(Father); */
        //实例化对象
        let s = new Son("李四",15,"男",150);
        console.log(s);
        let s1 = new Son("王五",17,"男",180);
        console.log(s1);
        // s.speak();
    </script>
</body>
</html>

继承

  • 1、原型继承
  • 2、类继承
  • js默认继承是原型继承
  • 而 class 只是 Es6 中的语法糖而已 (本质还是原型继承)

如何改变this指向

  • 1、call
  • 2、apply
  • 3、bind
  • 4、new 关键字: 在堆中开辟一个空间 , this 指向该空间 , 执行函数代码 , 返回实例化对象的地址

图片的类型和特点

图片有两种 位图和矢量图

  • 1、位图 : png 、 jpg 、 jpeg 、 gif 、特点:缩放会失真 , 可以做色彩丰富的图片
  • 2、矢量图 : svg、 特点: 缩放不会失真 , 可以做色彩简单的图片 如(logo 、小图标)

React脚手架

  • 1、下载安装环境
npm i create-react-app -g
  • 2、创建项目 , 在项目所在的目录下 (项目名称不可以有大写字母和中文)
 create-react-app reactcli
  • 3、启动服务
    npm start

项目目录

  • 1、node-modules 项目依赖包文件
  • 2、public 网站根目录 (静态资源目录)
  • 3、public 文件里面的 index.html文件 (是webpack打包的模板文件)
  • 4、package.json 项目声明文件
  • 5、package-lock.json 依赖包的锁文件
  • 6、src 源代码目录
  • 7、src文件中的 index.js文件 (是项目的入口文件)

react版本

  • 1、最新版本 18.2.0
  • 2、版本分水岭 16.8 之前,官方推荐使用类式组件
  • 3、16.8之后 , 官方推荐函数式组件 + hook
  • 4、讲课用的 17 版本 用的 类式组件
 npm i react@17 react-dom@17

前端的数据来源

  • 1、后台服务器响应返回到的数据
  • 2、前端列表收集到的数据

React 类式组件特点

react类式组件实例化对象三大属性之一 state

state 作用 :在类式组件中存储状态数据

state 值 : state 值是一个对象

state特点 : 数据不可以直接更改 react 不承认

setState( ) : setState( )用来修改/更新state数据 , 这些数据才是响应式数据

import React from "react";
import "./Demo.css"

export default class Demo extends React.Component{

    state = {
        str:"明天自习我不上课",
        num:4
    }

    render(){
        return <div className="demo">
            <h1 >{  this.state.str  }</h1>
            <h1>{ this.state.num }</h1>

            <button onClick={()=>{
                //更新state数据
                this.setState({
                    str:"这才是更新的响应式数据",
                    num:5,
                   
                });
               
            }}>点击更新</button>
        </div>
        
    }
}

react的类式组件实例化对象三大属性之一 props

props 的定义 : 是用来接受外部组件传递的数据 , 只可读 ,不可改

props 的特点 : props接受的数据是只可读 ,不可修改。 数据源在那就在哪里更改。

标签体形式传递 — Childeren

props 的三种传参方式

import  {Component} from "react"
import Header from '../../component/Header/Header'
import Foot from '../../component/Foot/Foot'
import Middle from '../../component/Middle/Middle'
import Mv from '../../component/Mv/Mv'
import News from '../../component/News/News'
export default class Home extends Component{

    state = {
        middleArr: [
            { title: "html", position: "熟练掌握" , bol:false},
            { title: "css", position: "了解", bol:false },
            { title: "js", position: "精通" , bol:false},
            { title: "ES6", position: "了解" , bol:false},
            { title: "git", position: "熟练掌握" , bol:false},
            { title: "node", position: "精通", bol:false },
            { title: "webpack", position: "熟练掌握", bol:false },
            { title: "express", position: "精通" , bol:false},
            { title: "npm", position: "熟练掌握", bol:false },
            { title: "mongod", position: "精通", bol:false },
            { title: "axios", position: "熟练掌握", bol:false },
            { title: "bootstrap", position: "了解" , bol:false},
            { title: "Promise", position: "精通", bol:false },
            { title: "react", position: "熟练掌握" , bol:false},
            { title: "http", position: "精通", bol:false },
            { title: "vue", position: "了解", bol:false },
            { title: "wechat", position: "熟练掌握", bol:false },
            { title: "json", position: "熟练掌握", bol:false }
        ],
        uname:"yaoyao",
        agr: 18,
        bol:true
    };

    // 点击调用,页面更新
    changeMiddle = (i)=>{
        
        return ()=>{
            // 改变数据,返回一个新数组
        let newmiddleArr = this.state.middleArr.map ((item,index) =>{
            if(index === i){
                item.bol = !item.bol
            }
            return item
        })
        // 将原数组替换成新数组   
          this.setState({middleArr : newmiddleArr})
        }
    }

    render (){
        // 第二种传递方法 的结构
        let {middleArr , uname , agr ,bol} = this.state
        return <div>
            <Header></Header>
        {/* 第一种传递方法 ,不推荐 */}
           {/* <Middle>{this.state.middleArr}</Middle> */}
        {/* 第二种传递方法 ,需要哪些数据传递哪些数据 */} 
           <Middle middleArr = {middleArr}  agr = {agr} changeMiddle={this.changeMiddle}></Middle>
        {/*  第三种传递方法 ,使用扩展运算符传递数据 , {}是表达式 , 在react中结构对象可以省略它自己的花括号*/}
           {/* <Middle {...this.state}  changeMiddle={this.changeMiddle}></Middle> */}
           <Mv></Mv>
           <News></News>
           <Foot></Foot>
        </div>
    }
}

props 三种接收方式

props – types 约束 props传递的数据 (isRequired 必传项)

defaultProps 设置默认数据

import {Component} from 'react'
import './Middle.css'
// 约束props数据
import PropTypes from 'prop-types'

export default class Middfle extends Component{

    // 限制传递的类型
    static propType = {
        middleArr:PropTypes.array.isRequired,
        uname:PropTypes.string.isRequired,
        changeMiddle:PropTypes.func.isRequired,
    }
    // 设置默认数据
    static defaultProps = {
        uname:'这是默认数据'
    }

  

    render (){
        // 第一种传递 需要一个一个传递,顺序都不可以发送改变 (不推荐)
        console.log(this.props);
        // let middleArr = this.props.children
        // 第二种传递方法,需要哪些传递哪些
        // let {middleArr , uname , agr } = this.props
        // 第三种 , 使用 ... 扩展运算符
        let {middleArr ,uname, changeMiddle} = this.props
        console.log(uname);
        return (<div className='middle'>
            <h1>{uname}</h1>
            <ul>
            {
                middleArr.map((item,index) =>{
                    return ( 
                    <li onClick = {changeMiddle(index)} key={index}>
                        <div> {item.title}</div>
                        {/* 点击显式熟练程度 */}
                        {
                        item.bol ? <div> {item.position}</div> : null
                        }
                    </li>)
                })
            }
            </ul>
        </div>)
    }
}

类式组件实例化对象三大属性之一 ref

ref 的作用 : 获取真实DOM对象

1、字符串方式的ref

import React, { Component } from 'react';

class StrRef extends Component {


    //获取数据方法
    getValue = () =>{
        // console.dir(this.refs.username.value);
        //获取输入框数据
        let {value} = this.refs.username;

        //  获取div对象并展示
        this.refs.con.innerHTML = value;

        //清空数据
        this.refs.username.value = "";
    }




    render() {
        return (
            <div>
                <h1>字符串形式的ref</h1>
                <input ref="username" type="text" />
                <button onClick={this.getValue}>获取数据</button>
                <div ref="con" style={{width:300,height:300,border:"1px solid red"}}></div>
            </div>
        );
    }
}

export default StrRef;

2、回调函数形式的ref

当react 在 执行虚拟DOM这行代码时,会将真实DOM对象作为实参 去执行后面的回调函数 , 将真实DOM对象赋值给了 实例化对象的属性

import React, { Component } from 'react';

class FnRef extends Component {

    //获取数据方法
    getValue = () =>{
        // console.dir(this.con);

        //获取数据
        let value = this.input.value;

        // 展示到div
        this.con.innerHTML = value;

        //清空
        this.input.value = "";
    }



    render() {
        return (
            <div>
                <h1>回调函数形式的ref</h1>
                {/* 会将真实dom对象 作为参数去执行后边的回调函数 */}
                <input  type="text"  ref={ (el) => { this.input = el }   }  />

                <button onClick={this.getValue}>获取数据</button>
                <div ref={ el => this.con = el }   style={{width:300,height:300,border:"1px solid red"}}></div>
            </div>
        );
    }
}

export default FnRef;

3、使用crearteRef( ) 创建 ref 容器

 import React, { Component,createRef } from 'react';

    class CreateRef extends Component {

        // 1. 使用 createRef()  创建 ref 容器
        input =  React.createRef();
        con = createRef();

        //获取数据方法
        getValue = () =>{
            console.dir(this);
            //获取数据
            let {value} = this.input.current;

            // 展示数据
            this.con.current.innerHTML = value;

            //清空数据
            this.input.current.value = "";



        }


        render() {
            return (
                <div>
                    <h1>createRef()</h1>
                    <input ref={this.input} type="text" />
                    <button onClick={this.getValue}>获取数据</button>
                    <div ref={this.con} style={{width:300,height:300,border:"1px solid red" }}></div>
                </div>
            );
        }
    }

    export default CreateRef;

非受控组件

概念 : 前端表单收集的数据 不被 state 所管理 。 是通过ref 获取真实Dom对象获取的。

import React, { Component,createRef } from 'react';

    class CreateRef extends Component {

        // 1. 使用 createRef()  创建 ref 容器
        input =  React.createRef();
        con = createRef();

        //获取数据方法
        getValue = () =>{
            console.dir(this);
            //获取数据
            let {value} = this.input.current;

            // 展示数据
            this.con.current.innerHTML = value;

            //清空数据
            this.input.current.value = "";



        }


        render() {
            return (
                <div>
                    <h1>createRef()</h1>
                    <input ref={this.input} type="text" />
                    <button onClick={this.getValue}>获取数据</button>
                    <div ref={this.con} style={{width:300,height:300,border:"1px solid red" }}></div>
                </div>
            );
        }
    }

    export default CreateRef;

受控组件

概念 : 前端表单收集的数据被 state 所管理

import React, { Component } from 'react';

class Control extends Component {

    //声明状态
    state = {
        username:"",
        pwd:""
    }


    //获取用户名
    saveUsername = (e) =>{
        // console.log(e.target.value);
        //更新数据
        this.setState({
            username:e.target.value
        })
    }

    // 获取密码
    savePwd = (e) => {
        //更新数据
        this.setState({
            pwd:e.target.value
        })
    }

    //登录方法
    login = () =>{
        alert(`账号是:${this.state.username},密码是:${this.state.pwd}`);
    }

    //清空
    reset = () =>{
        this.setState({
            username:"",
            pwd:""
        })
    }


    render() {
        return (
            <div style={{width:300,margin:"200px auto"}} >
                <h1>受控组件</h1>
                <input type="text" placeholder='账号' onChange={this.saveUsername} value={this.state.username} /><br /><br />
                <input type="text" placeholder='密码' onChange={this.savePwd} value={this.state.pwd} /><br /><br />

             
                <button onClick={this.login}>登录</button>
                <button onClick={this.reset}>清空</button>
            </div>
        );
    }
}

export default Control;

React.Fragment

作用 :包裹内部组件标签的 。 (因为所有div包裹, 发现有很多的无用层级,可以使用 <React.Fragment></React.Fragment>包裹 ,可以减少无用层级。)

特点 : 在浏览器中不加载这个内置组件

简写形式 : <></>

生命周期

概念 : 组件从创建到销毁的一系列的过程就叫组件的生命周期。 在这个过程中,会自动调用一些函数 , 这个函数就叫生命周期钩子函数

component 组件 ; mount 挂载

  • 1、componentDidMount 、组件完成挂载后执行
    • 在这个阶段做一些初始化的工作
    • (1) 创建定时器
    • (2) 发送ajax请求
    • (3) 订阅消息
    • (4) 操作Dom对象
  • 2、componentDidUpdate、组件完成更新后执行
    • (1) 操作新的Dom对象
  • 3、componentwillUnmount、组件将要销毁(卸载)时执行
    • 做一些收尾工作
    • (1) 清除定时器
    • (2) 取消订阅
  • 4、render 渲染虚拟Dom对象
    • 注意点:render方法中不能更新数据 , 不能调用setState()
    • render 执行的次数 ( 1 + n 次)

React 函数式组件特点

hooks

本质 : 本质就是react内置的一些函数

作用 : 只可以在函数时组件中使用

hook 注意点

  • hook 不能在条件判断或者循环中
  • hook 只能在函数式组件内部使用

useState( )

作用:在函数式组件中声明状态 是一个数组

存放 :在useState中可以存放数据 , 但是基于状态的最小原则,已有数据能够计算出新的结果 ,就没有必要在存放在sueState中

import React ,{useState}from 'react'

export default function UserState() {

    
    let [bol,setBol] = useState(true);

    let  click =  () => {
        setBol(!bol)

    }

  return (
    <>
        <h1>UserState ,hooks 中的函数 </h1> 
        <hr />
    
        <div>明天学习吗?{bol ? "学习" : '不学习'}</div>
        <button onClick={click}>点击</button>
    </>
  )
}

useRef( )

作用:在函数式组件中操作真实Dom对象

import React, { useRef } from 'react'
import './UseRef.css'
export default function UseRef() {
    let input = useRef()
    let box = useRef()


    let click = () =>{
        console.log(input.current.value);  
        box.current.innerHTML =  input.current.value
        input.current.value = ''
    }
  return (
    <div  className='useRef'>
        <input ref={input} type="text" />
        <button onClick={click}>提交</button>
        <div ref={box}></div>

    </div>
  )
}

useEffect( )

作用 :在函数式组件中模拟生命周期钩子函数

1、模拟componentDidMount 和 componentdidUpdate

  useEffect(()=>{
        console.log("模拟 componentDidMount 和 componentDidUpdate");
    }) 

2、useEffect可以多次调用

3、单独模拟componentDidMount

 useEffect(()=>{
        console.log("单独模拟 componentDidMount");
    },[])

4、模拟componentDidMount 和 监听某个声明的状态变化后的更新

 useEffect(()=>{
        console.log("模拟 componentDidMount  和监听某个状态变化后的更新");
    },[bol])

5、模拟componentWillunmount

   useEffect(()=>{
        return ()=>{
            console.log("单独模拟 componentWillUnmount");
        }
    },[])

useContext( )

隔代组件数据传递

前后台数据通讯

  • 1、xmlHttpRequest (原生js)
  • 2、ajax (jqyery)
  • 3、axios (react)
  • 4、fetch (ES6)

axios

  • 1、纯粹 , 只做数据交互
  • 2、基于 Promise
    • promise的状态 : pending (等待), resolve(成功) , reject(失败)
  • 3、方法
    • axios.get(url , config)获取数据
    • axios.post(url , data , config)添加数据
    • axios.put(url , data , config)全局更新数据
    • axios.patch(url , data , config)局部更新数据
    • axios.delete(url , config)删除数据

组件数据传递

1、props (父子件传递)

作用:父子组件中传递参数

如果在父组件中自定义属性传递数据 , 子组件中props接受数据 , 则是父亲向儿子传递数据

如果在父组件中自定义属性传递方法,子组件中props接收方法,子组件调用方法并传递数据,则是儿子向父亲传递参数

// 在父组件中传递参数
<TodoMain todolist={todolist} />
// 在子组件中使用props接受参数
export default function TodoMain(props) {

    //解构
    let {todolist} = props;

2、useContext (隔代组件数据传递)

 // 1. 在 祖先 组件中
    // 1. 创建  context 对象
export let countContext = React.createContext();

export default function A() {

    //声明状态
    let [count,setCount] = useState(100);


    return (
        <>
        {/* 2.  用 countContext.Provider  包裹所有的后代组件, 使用 value 属性传递数据*/}
           <countContext.Provider value={count}>
                <h1>AAAAAAAAAAAAAAA</h1>
                <B/>
            </countContext.Provider>
        </>
    )
}
 // 在 任意的 后代组件中
    import React, { useContext } from 'react'

// 3. 导入 context 上下文对象
import { countContext } from "../A/A"

export default function C() {
    // 4. 使用 useContex hook  接受传递的数据
    let count = useContext(countContext);

    // console.log(count);

    return (
        <>
            <h3>CCCCCCCCCCCCCCC</h3>
            <h3>展示A组件中的数据:{count}</h3>
        </>
    )
}

3、pubsub-js (发布订阅) ,任意组件传递

1、下载 第三方库 ,实现发布订阅机制

// --force 强制下载   -发简写
npm i pubsub-js  --force

2、导入 在要使用是页面导入

import PubSub from "pubsub-js"

3、使用 (发布消息) (订阅消息)

// 在获取数据页面发布数据
PubSub.publish("频道名称" , 要发布的数据)
// 在使用数据页面订阅数据
PubSub.subscribe("频道名称" , (message , data) => {
// message 频道名称 ,
// data 订阅的数据
})
// 在销毁阶段 取消订阅
PubSub.unsubscribe("频道名称")

4、redux (集中式状态管理工具)

5、本地存储

6、路由传参

路由传参

SPA

概念 :单页面应用 , 整个项目只有一个页面

优点 : 加载速度块 , 不会出现页面空白

路由

概念 : key ===》 value 映射

  • key 地址
  • value 函数 或 组件
// 后台 路由
app.get ("/home" ,(req , res)=>{
  end("首页")
})

// 前端 路由
<Route path="/home" element={<Home/>}></Route>

react – router – dom

1、安装

npm i react-router-dom  --force

2、导入

// 在App 根组件中导入
import {BrowserRouter , Routes , Route} from "react-router-dom"

react 路由初体验

 //在 App根组件中
    export default class App extends React.Component{
    render(){
        return <>
            <BrowserRouter>
                <Routes>
                    <Route  path="/home"  element={ <Home/>}/>
                    <Route  path="/ref"  element={ <Ref/>}/>
                    <Route  path="/hooks"  element={ <Hooks/>}/>

                    <Route  path="/heros"  element={ <Heros/>}/>
                    <Route  path="/duanzi"  element={ <Duanzi/>}/>
                </Routes>
            </BrowserRouter>
        </>
    }
}

react-router-dom中的组件

  • 1、BrowserRouter 最外层的一个路由组件,用来包裹所有的路由
  • 2、Routes 是指所有的路由组件, 用来包裹当路由组件
  • 3、Route 单个路由组件
    • path 路由地址
    • element 路由组件
    • index 入口
  • 4、Link 链接组件
  • 5、NavLink 导航链接
  • 6、Outlet 子路由组件的占位
  • 7、Navigate 路由重定向组件
  • 8、HashRouter 最外层的一个路由组件 , 用来包裹所有的路由组件

react – router – dom Hook

  • useRoutes() 创建路由对象
  • useParams() 获取地址栏params参数
  • useSearchParams() 获取地址栏query参数
  • uselocation() 获取 state 参数
  • useNavigate () 编程式路由导航

params 传递参数

1、当地址栏 是 ( /heros )时,展示英雄列表页面

2、当地址栏 是 (herosDetail/1)时 展示英雄详情页面

// 1. 配置路由
    {
        path:"/herosdetail/:id/:name",
        element:<HerosDetail/>,
        meta:{   // 路由元信息
            title:"英雄详情",
            isHidden:true
        }
    },


    // 2. 在  英雄列表页面 使用 Link 跳转
     <li key={item.id}>

         <Link to={`/herosdetail/${item.id}/${item.name}`}>
           <img src={`http://cdn.xiaohigh.com${item.image}`} alt="" />
           <h3>{item.name}</h3>
         </Link>
     </li>


    // 3. 在 英雄详情组件
     // 获取地址栏 params 参数
    let {id,name} = useParams();

query 传递参数

1、当地址栏 是 ( /heros ) 时,展示英雄列表也面

2、当地址栏 是 ( /herosDetail?id=1&name= ) 时,展示英雄详情也面

// 1. 配置路由
    {
        path:"/herosdetail",
        element:<HerosDetail/>,
        meta:{   // 路由元信息
            title:"英雄详情",
            isHidden:true
        }
    },


    // 2. 在  英雄列表页面 使用 Link 跳转
     <li key={item.id}>

        <Link to={`/herosdetail?id=${item.id}&name=${item.name}`}>
               <img src={`http://cdn.xiaohigh.com${item.image}`} alt="" />
               <h3>{item.name}</h3>
        </Link>
     </li>


    // 3. 在 英雄详情组件
    
    // 获取地址栏 query 参数
    let [search,setSearch] = useSearchParams();
    // console.log(arr)
    // 获取id
    let id = search.get("id");
    let name = search.get("name");

state 传递参数

1、当地址栏 是 ( /heros )时, 展示英雄列表页面

2、当地址栏 是 ( /hoersDetail )时 , 展示英雄列表页面

// 1. 配置路由
    {
        path:"/herosdetail",
        element:<HerosDetail/>,
        meta:{   // 路由元信息
            title:"英雄详情",
            isHidden:true
        }
    },


    // 2. 在  英雄列表页面 使用 Link 跳转
     <li key={item.id}>

      <Link to="/herosdetail" state={{id:item.id,name:item.name}}  >
          <img src={`http://cdn.xiaohigh.com${item.image}`} alt="" />
          <h3>{item.name}</h3>
      </Link>
     </li>


    // 3. 在 英雄详情组件

    // 获取 state 参数
    let {state:{id,name}} = useLocation();
    

路由传参综合 (params , query , state)

  • 1、params
    • 优点
      • 地址栏格式化比较清晰优雅
      • 在其他浏览器打开数据不会丢失
    • 缺点
      • 不安全
      • 传递的参数的个数和顺序必须固定
  • 2、query
    • 优点
      • 在其他浏览器打开数据不会丢失
      • 传递的参数的个数和顺序可以不固定
    • 缺点
      • 不安全
      • 地址栏格式不友好
  • 3、state
    • 优点
      • 传递的参数个数和顺序可以不固定
      • 比较安全,地址栏看不到传递数据
    • 缺点
      • 在其他浏览器打开数据(会) 丢失
      • state数据保存在浏览器的历史纪录中 (history)

useNavigate 编程式路由导航

路由懒加载

解决的问题 : 首屏加载速度慢的问题

 // 1. 导入 lazy 函数 Suspense【组件】
    import { lazy, Suspense } from "react"

    // 一般情况: 首页 404  登录 注册 不需要懒加载
    import NotFound from "../pages/NotFound";

    // 2. 使用 lazy 函数导入 组件
    let Home = lazy(() => import("../pages/Home/Home"));
    let Ref = lazy(() => import("../pages/Ref/Ref"));
    let Hooks = lazy(() => import("../pages/Hooks/Hooks"));
    let TodoList = lazy(() => import("../pages/TodoList/TodoList"));
    let Heros = lazy(() => import("../pages/Heros/Heros"));
    let Duanzi = lazy(() => import("../pages/Duanzi/Duanzi"));
    let News = lazy(() => import("../pages/News"));
    let Guonei = lazy(() => import("../pages/News/Guonei"));
    let Junshi = lazy(() => import("../pages/News/Junshi"));
    let HerosDetail = lazy(() => import("../pages/HerosDetail/HerosDetail"));


    //3.  封装一懒加载组件的函数
    let load = (Comp) =>{
        return (
            <Suspense fallback={<div>正在加载中...</div>}>
                <Comp />
            </Suspense>
        )
    }

        // 4. 使用懒加载组件
    {
            path: "/",   //  路由地址
            element: load(Home),  //路由组件
            meta: {   // 路由元信息
                title: "首页"

            }
        },

哈希路由和BrowserRouter(history)

  1. 形式上 BrowserRouter 格式比较清晰,优雅 localhost:3000/home HashRouter 格式比较怪异 localhost:3000/#/home
  2. 兼容性 BrowserRouter 兼容 IE9 以上 HashRouter 兼容 IE8 以上
  3. 项目部署 BrowserRouter 项目打包编译后,必须有后台人员更改服务器配置,才可运行 HashRouter 项目打包编译后,可直接放在服务器上运行。

HashRouter原理

    <!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>HashRouter</title>
</head>
<style>
    #content{
        width: 300px;
        height: 300px;
        border: 1px solid red;
    }



</style>
<body>
    <a href="#home">首页</a>
    <a href="#ref">ref</a>
    <div id="content"></div>

    <script>

        // HashRouter 的原理 借助 hashchange 事件来完成的。
        // hashchange 当浏览器地址栏的hash【锚点】值发生变化时 ,自动执行

        window.addEventListener("hashchange",function(){
            // location    浏览器地址栏对象
            // console.log(location);
            let {hash} = location;

            switch(hash){
                case "#home":
                    content.innerHTML = "首页 首页  首页  首页  首页";
                    break;
                case "#ref":
                    content.innerHTML = "ref ref  ref  ref  ref";
                    break;
                default :
                    content.innerHTML = "404";
            }
        })



    </script>

</body>
</html>

BrowserRouter原理

  <!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>BrowserRouter</title>
</head>
<style>
    #content{
        width: 300px;
        height: 300px;
        border: 1px solid red;
    }



</style>
<body>
    <a href="/home">首页</a>
    <a href="/ref">ref</a>

    <div id="content"></div>

    <script>

        // 获取所有的a链接
        let links = document.querySelectorAll('a');

        links.forEach(link => {
             //为 每个 a 链接绑定点击事件
             link.addEventListener("click",function(e){
                // 阻止默认行为
                e.preventDefault();

                // history   浏览器历史记录对象
                // pushState()  向浏览器的记录的末尾 新增一条新的纪录
                // pushState(要新增的数据,新纪录的标题,url地址)
                history.pushState({id:123,name:"张三"},null,e.target.href);

                // console.log(location);

                console.log(history);

                //获取地址栏路径
                let {pathname} = location;

                //判断
                switch(pathname){
                    case "/home":
                        content.innerHTML = "首页 首页  首页  首页  首页";
                        break;
                    case "/ref":
                        content.innerHTML = "ref ref  ref  ref  ref";
                        break;
                    default :
                        content.innerHTML = "404";
            }
             })   
        })


    </script>

</body>
</html>

Redux

作用 :集中式状态管理工具

1、安装

 npm i @reduxjs/toolkit --force

2、使用

  // 1.  导入
    //  createSlice创建 切片对象
    //  configureStore  创建 store 对象
    import { createSlice,configureStore } from "@reduxjs/toolkit"


    
// 2. 创建切片对象
let countSlice  = createSlice({
    //数据别名 
    name:"count",
    //为数据赋值,推荐值为对象
    initialState:{
        value:100
    },
     // action 描述一个行为/动作。值为一个对象 {type:"类型",payload:"要操作的新的数据" }
    // {  type:"打",payload:"张三"  }  {type:"打",payload:"酱油"}  {type:"学习",payload:"react"}
    // {type:"incre",payload:1}    {type:"decre",payload:2}
    reducers:{
        //加
        incre:function(state,action){
            //  必须对数据进行直接更改
            state.value += action.payload;
        },
        //减
        decre(state,{payload}){
             //  必须对数据进行直接更改
             state.value -= payload;
        }
    }
  
});


    // 3. 提取action 方法
    let { incre , decre} = countSlice.actions;


    // 4. 创建仓库
    let store = configureStore({
        reducer:countSlice.reducer
    });


    
    // 5. 读取仓库数据
    console.log(store.getState());

    // 6. 调用action 方法
    store.dispatch(incre(1));

store中的方法

  • store.getState() 读取store 数据
  • store.dispatch() 分发行为/动作
  • store.subscribe() 订阅 store中的数据,当store 中的数据发生变化时,自动调用。

redux三大核心概念

  • store 存储一些公共的状态,项目中只能有一个store 仓库,是和组件通信的桥梁。
  • action 描述一个行为/动作,[对数据的具体的操作],值应为一个对象 {type:”类型”,payload:”要更新的数据”}
  • reducer 是一个函数,接收 state 和 action, 计算返回新的 state

store.dispatch()

dispatch() 参数为对象 同步更新数据

//  分发行为
    store.dispatch({type:"incre",payload:1});
    store.dispatch({type:"decre",payload:2});

    // 在老版  
    //封装 action 函数 返回 action 对象
    //封装加方法
    function incre(payload){
        return {type:"incre",payload};
    }

    //封装减方法
    function decre(payload){
        return {type:"decre",payload};
    }


    // 分发行为
    store.dispatch(incre(1));
    store.dispatch(decre(2));

dispatch() 参数为函数 异步更新数据

 //调用store方法
        store.dispatch(() =>{
            setTimeout(() => {
                // 调用同步方法更新数据
                store.dispatch(incre(payload));
            }, 3000);
            
        });



    //在store 中封装方法
    //创建异步加方法
    export let asyncIncre  = (payload) =>{
        return () =>{
            setTimeout(() => {
                // 调用同步方法更新数据
                store.dispatch(incre(payload));
            }, 3000);
            
        }
    }

    // 在组件中调用异步方法
 store.dispatch(asyncIncre(3));

封装reducer

在 store 下创建 slices 目录 及 countSlice.js 文件

   // 导入
    import {createSlice} from "@reduxjs/toolkit"

// 创建切片对象
let countSlice  = createSlice({
    //数据别名 
    name:"count",
    //为数据赋值,推荐值为对象
    initialState:{
        value:100
    },
  
    // action 描述一个行为/动作。值为一个对象 {type:"类型",payload:"要操作的新的数据" }
    // {  type:"打",payload:"张三"  }  {type:"打",payload:"酱油"}  {type:"学习",payload:"react"}
    // {type:"incre",payload:1}    {type:"decre",payload:2}


    //   配置 reducer 函数,也在创建 action 函数
    reducers:{
        //加
        incre:function(state,action){
            //  必须对数据进行直接更改
            state.value += action.payload;
        },
        //减
        decre(state,{payload}){
             //  必须对数据进行直接更改
             state.value -= payload;
        }
    }
});


//  提取action 方法
export let { incre , decre} = countSlice.actions;



//创建异步加方法
export let asyncIncre  = (payload) =>{
    return dispatch =>{
        setTimeout(() => {
            // 调用同步方法更新数据
            dispatch(incre(payload));
        }, 1000);
        
    }
}

//创建异步减方法
export let asyncDecre = (payload) =>{
    return dispatch =>{
        setTimeout( () => {
            // 调用同步方法更新数据
            dispatch(decre(payload));
        }, 1000);
    }
}

//暴露
export default countSlice.reducer

在 store index.js 中导入并使用

//  configureStore  创建 store 对象
import { configureStore } from "@reduxjs/toolkit"

import countReducer from "./slices/countSlice";


// 4. 创建仓库
let store = configureStore({
    reducer:countReducer
});



//暴露store
export default store;

react-redux

安装

npm i react-redux --force

导入

  // 1. 在 项目的入口文件
    import {Provider} from "react-redux"
    // 2. 将所有的组件 包裹在 Provider 组件中并传递store 数据
    //渲染
    ReactDOM.render(<Provider store={store}><BrowserRouter><App/></BrowserRouter></Provider>,document.getElementById('root'));


    // 3. 在对应的组件中导入 两个hook
    import {useSelector,useDispatch} from "react-redux"
     //useSelector  hook  获取到 store 仓库中的所有的状态
      // useDispatch 能获取 到 dispatch 函数


    // 4. 在函数式组件的内部 使用  useSelecter 来获取 store 所有 的数据
    // 1. 形式
    // let state = useSelector(state => state);

    // 2. 形式
    let {count,zan} = useSelector(state => state);

    // 3. 形式
    // let count = useSelector(state => state.count);

    // 5. 直接使用数据
     <h1> store中的数据是:{count.value}</h1>
      <h1>当前点赞数:{zan.value}  </h1>

    // 6. 使用useDispatch 能获取 到 dispatch 函数
    let dispatch = useDispatch();

    // 7. 调用 store 中的action 方法来更新数据
     //加
    let add = () => {
        dispatch(incre(1));
       

    }


    //减操作
    let sub = () => {
        // 调用store 中的 decre 方法
        dispatch(decre(2));

       
    }

    //异步加
    let asyncAdd = () => {
        //调用store方法
        dispatch(asyncIncre(3));
       
    }

    //异步jian 
    let asyncSub = () => {
        //调用store方法
        dispatch(asyncDecre(4));

      
    }

欢迎转载,请注明来源!

评论

  1. jj
    Windows Chrome
    2 年前
    2023-6-13 20:29:36

    w(゚Д゚)w

    • 博主
      jj
      Windows Chrome
      2 年前
      2023-6-14 11:12:11

      \( ̄︶ ̄*\))

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇