React基础知识学习笔记

本文围绕React展开,介绍其特点,如组件化、虚拟DOM和Diffing算法。通过Hello_React案例演示,讲解虚拟DOM创建、JSX语法。阐述组件创建方式、实例核心属性,包括state、props、refs。还涉及生命周期函数、Diffing算法、路由使用、antd按需引入及Redux状态管理等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

React 组件

1.React 特点

1.采用组件化模式,声明式编码,提高开发效率及组件复用率
2.在React Native中使用React语法进行移动端开发
3.使用虚拟DOM+优秀的Diffing算法,尽量减少与真实DOM的交互
注:React 高效的原因:使用虚拟DOM;Diffing算法:最小化页面重绘

2.Hello_React 小案例

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>JSX 练习</title>
    </head>
    <body>
        <!-- 准备好一个“容器” -->
        <div id="test"></div>        
        
        <!-- 引入react核心库 -->
        <script type="text/javaScript" src="../../react/js/react.development.js"></script>
        <!-- 引入react-dom,用于支持react操作DOM -->
        <script type="text/javaScript" src="../../react/js/react-dom.development.js"></script>
        <!-- 引入babel,用于将jsx转为js -->
        <script type="text/javaScript" src="../../react/js/babel.min.js"></script>
        
        <script type="text/babel"> // 此处一定要写bael
        //1.创建虚拟DOM
        const VDOM=(<div>新年好</div>); // 此处一定不要写引号,因为不是字符串

        //2.渲染虚拟DOM到页面
        ReactDOM.render(VDOM,document.getElementById('test'));
        </script>
    
    </body>
</html>

3.虚拟DOM的两种创建方式

1.使用jsx创建虚拟DOM

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>JSX 练习</title>
    </head>
    <body>
        <!-- 准备好一个“容器” -->
        <div id="test"></div>        
        
        <!-- 引入react核心库 -->
        <script type="text/javaScript" src="../../react/js/react.development.js"></script>
        <!-- 引入react-dom,用于支持react操作DOM -->
        <script type="text/javaScript" src="../../react/js/react-dom.development.js"></script>
        <!-- 引入babel,用于将jsx转为js -->
        <script type="text/javaScript" src="../../react/js/babel.min.js"></script>
        
        <script type="text/babel"> // 此处一定要写bael
        //1.创建虚拟DOM
        const VDOM=(<div id="test"><span>新年好</span></div>); // 此处一定不要写引号,因为不是字符串

        //2.渲染虚拟DOM到页面
        ReactDOM.render(VDOM,document.getElementById('test'));
        </script>
    
    </body>
</html>

2.使用js创建虚拟DOM(一般不用)

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>用jsx创建虚拟DOM</title>
    </head>
    <body>
        <!-- 准备好一个“容器” -->
        <div id="test"></div>        
        
        <!-- 引入react核心库 -->
        <script type="text/javaScript" src="../../react/js/react.development.js"></script>
        <!-- 引入react-dom,用于支持react操作DOM -->
        <script type="text/javaScript" src="../../react/js/react-dom.development.js"></script>

        <script type="text/javaScript"> 
        //1.创建虚拟DOM
       // const VDOM=React.createElement(标签名,标签属性,标签内容);
       const VDOM=React.createElement('h1',{id:'title'},React.createElement('span',{},'Hello,React');
        //2.渲染虚拟DOM到页面
        ReactDOM.render(VDOM,document.getElementById('test'));
        </script>
    
    </body>
</html>

*****中途想查看一下npm 镜像源,百度了一下:npm config get registry

3.比较VDOM与TDOM

 console.log(VDOM); //Object
 console.log(document.getElementById('test')) //<div id="test">...</div>

关于虚拟DOM:
(1)本质是Object类型的对象(一般对象)
(2)虚拟DOM比较‘轻’,真实DOM比较‘重’,因为虚拟DOM是react内部使用的,无需真实DOM那么多的属性
(3)虚拟DOM最终会被React转为真实的DOM,呈现在页面上

4.JSX语法规则

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>JSX 语法</title>
    </head>
    <style>
    .title{
        background-color: orange;
    }
    </style>
    <body>
        <!-- 准备好一个“容器” -->
        <div id="test"></div>        
        
        <!-- 引入react核心库 -->
        <script type="text/javaScript" src="../../react/js/react.development.js"></script>
        <!-- 引入react-dom,用于支持react操作DOM -->
        <script type="text/javaScript" src="../../react/js/react-dom.development.js"></script>
        <!-- 引入babel,用于将jsx转为js -->
        <script type="text/javaScript" src="../../react/js/babel.min.js"></script>
        
        <script type="text/babel"> // 此处一定要写bael
        const idtest="lOvEmYSelf"
        const content="愿你不负自己"
        //1.创建虚拟DOM
        const VDOM=(
            <div>
              <h1 className="title" id={idtest.toUpperCase()}>
                <span style={{color:"white"}}>
                  {content.toLowerCase()}
                </span>
              </h1>
              <input type="text" />

            </div>
        ); // 此处一定不要写引号,因为不是字符串

        //2.渲染虚拟DOM到页面
        ReactDOM.render(VDOM,document.getElementById('test'));

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

语法规则
(1)创建虚拟DOM时不能加引号
(2)标签中混入js表达式时要用{}
(3)样式的类名指定不要用class,要用className
(4)内联样式,要用style={{key:value}}的形式去xie
为什么是双括号:最外层是参考第二条,第二层是当使用键值对形式时需要{}
(5)只有一个根标签
(6)标签必须闭合
(7)标签首字母
. …若小写字母开头,则转为HTML中同名标签,若HTML中无该对应的标签,则报错
…若大写字母开头,则react就去渲染对应的组件,若组件没有定义,则报错
自述:前面敲代码时有几个问题:
1.忘记CSS最外层包裹标签为什么了:<style></style>
2.不记得js转字母转大写、小写的方法:toUpperCase(),toLowerCase()

5.JSX练习

<script type="text/babel"> // 此处一定要写bael
         var arrays=['Angular','Vue','Reace']
        //1.创建虚拟DOM
        const VDOM=(
            <div>
             <h1>前端js框架列表</h1>
             <ul>
               arrays.map((items)=>{
                   return <li>{items}</li>
               })
             </ul>
            </div>
        );
        //2.渲染虚拟DOM到页面
        ReactDOM.render(VDOM,document.getElementById('test'));

        </script>

报错Uncaught SyntaxError: Inline Babel script: Unexpected token (9:19)
解决方案:

 {
    arrays.map(function name(items) {
        return <li>{items}</li>
   })
 }

<ul></ul>标签内部的map方法少最外层{}。违反了jsx语法规则2
报错:react.development.js:276 Warning: Each child in a list should have a unique "key" prop.
解决方案:为li标签设定key唯一值(但设定索引值会造成不可预估的错误,不推荐,先暂时解决)

 <ul>
     {
       arrays.map((items,index)=>{
          return <li key={index}>{items}</li>
      })
      }
</ul>

6.js语句(代码)与js表达式区别

区分【js语句(代码)与js表达式】
1、表达式:一个表达式会产生一个值,可以放在任何一个需要值的地方
下面这些都是表达式
(1).a // 变量
(2).a+b // 变量+变量
(3)demo(1) // 方法(传的参数)
(4)arr.map() // 数组遍历
(5)function test(){} // 直接在内部写方法
2、语句(代码)
下面这些都是语句(代码)
(1).if
(2).for(){}
(3)switch(){case:xxx}

4.模块,模块化,组件,组件化概念

模块:1个js为一个模块
组件:1个简单的html、css、js所构成的一个页面为一个组件(实现局部页面)
模块化:应用以js为模块来编写
组件化:多个组件构成的应用为组件化

5.React面向组件编程

1.下载react开发者调试工具:React Developer Tools

2.创建组件的两种方式:函数式与类式组件

2.1.函数式组件(适用于简单组件)
 <script type="text/babel"> // 此处一定要写bael
        //1.创建函数式组件
        function Demo(){
           console.log(this);// undefined 原因:babel编译后开启ES5严格模式:禁止自定义函数的this指向window
            return <h1>要开心,要快乐,要努力!</h1>
        }
        //2.渲染虚拟DOM到页面
        ReactDOM.render(<Demo/>,document.getElementById('test'));
         /*
         执行完 ReactDOM.render(<Demo/>....发生了什么
         1.React解析组件标签,找到Demo组件
         2.发现组件是函数定义的,调用该函数,将虚拟的DOM转为真实DOM,呈现页面        
           */
        </script>
2.2类式组件
2.2.1.类的基本知识
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>类的基本知识</title>
</head>
<body>
    <script type="text/javaScript">
    /*
    总结:
     1.类中的构造器不是必须写的,要对实例进行一些初始化操作,如添加指定属性时才写。
     2.如果A类继承了B类,且A类中写了构造器,那么A类构造器中的super是必须调用的
     3.类中所定义的方法,都是放在了类的原型对象上,供实例使用
    
    */
    class demo{
        // 构造器方法
       constructor(name,age){
           // 构造器的this是谁--类的实例对象
         this.name=name;
         this.age=age;
       }

       // 一般方法
       speak(){
           // speak 方法放在哪了?--类的原型对象上,供实例使用
           //通过demo实例调用speak时,speak中的this就是demo实例
           console.log( `${this.name}${this.age}`);
       }
    }

   //创建一个demo1继承于demo类
    class demo1 extends demo{

       constructor(name,age,content){
        super(name,age);
        this.content=content;
       }

      // 重写从父类继承过来的方法
       speak(){
           console.log( `${this.name}${this.age}${this.content}`);
       }
       study(){
           // speak 方法放在哪了?--类的原型对象上,供实例使用
           //通过demo实例调用speak时,speak中的this就是demo实例
           console.log("世人笑我太疯癫,我笑别人看不穿!")
       }
    }
    const d1=new demo("葛朋飛","小垃圾");
    const d2=new demo1("我","你","的誰");
    d1.speak();
    d2.speak();
    d2.study();
    </script>
</body>
</html>
2.2.2. 类的demo
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>类式组件</title>
    </head>
    <body>
        <div id="test"></div>
        <script type="text/javaScript" src="../../../react/js/react.development.js"></script>
        <script type="text/javaScript" src="../../../react/js/react-dom.development.js"></script>
        <script type="text/javaScript" src="../../../react/js/babel.min.js"></script>
        
        <script type="text/babel">

        // 1.创建类式组件
        class MyComponent extends React.Component{            
            render(){
                // render是放在哪里的?--MyComponent的原型对象上,供实例使用
                // render中的this是谁?--MyComponent的实例对象<==>MyComponent组件实例对象
                console.log("render中的this"+this);// MyComponent{props:{},context:{},refs:{},state:null...}
                return <h1>适用于复杂组件(需要改变状态的组件为复杂组件)</h1>
            }
        }
        //2.渲染组件到页面
        ReactDOM.render(<MyComponent/>,document.getElementById('test'))
        /*
        执行了 ReactDOM.render(<MyComponent/>...之后发生了什么?
          1.React解析组件标签,找到了MyComponent组件
          2.发现组件是通过类定义的,随后new出来该类的实例,并通过该实例调用到原型上的render方法。
          3.将render返回的虚拟DOM转为真实DOM,随后呈现在页面上
        */
        </script>
    
    </body>
</html>
2.2.3. 简单组件与复杂组件

简单组件:无State
复杂组件:无State

6.组件实例的三大核心属性

1.state-改变数值

1.1 原生事件绑定–三种
<body>
    <button id="btn1">按钮1</button>
    <button id="btn2">按钮2</button>
    <button onclick="demo()">按钮3</button>
    <script type="text/javaScript">
    const btn1=document.getElementById('btn1');
    btn1.addEventListener('click',()=>{
        console.log("按钮1被点击了")
    })

    const btn2=document.getElementById('btn2');
    btn2.onclick=()=>{
        console.log("按钮2被点击了");
    }

    function demo(){
        console.log("按钮3被点击了");
    }
    </script>
</body>
1.2 demo

今天天气很凉爽

<!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>
  <!-- 准备好一个容器 -->
  <div id="test"></div>

  <!-- 引入react核心库 -->
  <script src="../js/react-development.js" type="text/javascript"></script>
  <!-- 引入react DOM 用于支持react 操作DOM -->
  <script src="../js/react-dom.development.js" type="text/javascript"></script>
  <!-- 引入babel 用于将jsx转为js -->
  <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>

  <script type="text/babel">
     class IsWeather extends React.Component{

        //构造器调用几次:1次,初始化的时候
        constructor(props){
          super(props);
         // console.log(this);
          // 初始化状态
          this.state={is:true}
          this.isWeather=this.isWeather.bind(this);
          // bind做两件事:(1)生成新的函數(2)改變this指向
          }
        // render调用几次1+n次:1是初始化那次,n是状态更新的次数
        render(){
            //   console.log(this)
            //   return <h1 onClick={this.isWeather}>今天天气很{this.state.is==true?'炎热':'凉爽'}</h1>
             // 或者 就不用写this.state
             const {is}=this.state; // 读取状态
             return <h1 onClick={this.isWeather}>今天天气很{is==true?'炎热':'凉爽'}</h1>
        }

        // isWheather 点几次调用几次
        isWeather(){
        // isWeather放在哪里?--StateClass的原型对象上,供实例使用
        // 由于isWeather是作为onClick的回调,所以不是通过实例调用的,是直接调用
        //类中的方法默认开启了局部的严格模式,所以isWeather中的this为undefined
       // console.log(this);
        const is=this.state.is
      //   this.setState=({is:!is},()=>{
      //       console.log(this.state.is)
      //   })--这是我当时困惑的为什么this中出现setState,导致点击不起作用。
        this.setState({is : !is }, () => {
          console.log(this.state.is);
          });
       }
     }
     ReactDOM.render(<IsWeather/>,document.getElementById('test'));

     /*自述:为什么isWeather中的this为undefined
       因为当你写一个方法,
       function demo(){},调用demo()时,函数内部是指向windows。
       class demo1{
         study(){
              console.log(this);
              console.log("不负韶华");
          }
        }
        const d1=new demo1();
        d1.study();//demo1 {} --实例调用
        const d2=d1.study;
        d2();//undefined--直接调用
      */

  </script>
  </body>
</html>
1.3state的简写方式
class IsWeather extends React.Component{

        //构造器调用几次:1次,初始化的时候
       // constructor(propos){
         // super(propos);
          //this.state={isHot:true};//在外头定义即可
         // this.isWeather=this.isWeather.bind(this); //方法用箭头函数
       // }
       //1.初始化状态
        state={isHot:true};
        render(){
            return <div onClick={this.isWeather}>今天天气很{this.state.isHot?'炎热':'凉爽'}</div>
        }

        //自定义方法
        //name=()=>{}箭头函数没有this会自动选择上一层级的this,所以它会找到IsWeather的this
        // 下面这方法是赋值语句+箭头函数的形式。
        isWeather=()=>{
            const isHot=this.state.isHot;
           this.setState=({isHot:!isHot});
        }
     }
     ReactDOM.render(<IsWeather/>,document.getElementById('test'));


** 注意:**
1.组件中render方法中的this为组件实例对象
2.组件中自定义方法中的this为undefined,如何解决
①.强制绑定this:通过函数对象的bind()
②.箭头函数 name=()=>{}
3.状态数据不能直接修改或者更新,必须借助this.setState=({isHot:!isHot})

2.props-组件之间传值

2.1.用法demo
<script type="text/babel">
     
     class DemoProds extends React.Component{
       render(){
         console.log(this);
         const{name}=this.props
         return (
           <ul>
             <li>姓名:{name}</li>
            </ul>
         )
       }
     }
     const p={name:'小幸运'}
     //下面两种方法均可
    // ReactDOM.render(<DemoProds name='小幸运'/>,document.getElementById('test'))
     ReactDOM.render(<DemoProds {...p}/>,document.getElementById('test'))

  </script>
2.2.展开运算符复习(可用于展开数组,连接数组)
<script type="text/javaScript">
     let arr1=[1,2,3,4,5];
     let arr2=[6,7,8,9,0];
     console.log(...arr1);//展开一个数组 1 2 3 4 5
     let arr3=[...arr1,...arr2];// 连接数组  [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
     console.log(arr3)

     // 在函数中使用
     function sum(...numbers){
       console.log(numbers);// [1, 2, 3, 4]
       // 数组求和 
       return numbers.reduce((pre,nex)=>{
              console.log(pre+"---"+nex)//1---2---3---4
              return pre+nex;
       })
     }
    console.log(sum(1,2,3,4));

    // 构造字面量使用的展开语法
    let person={name:'无话',age:18};
    let person2={...person};
  //  console.log(...person);//报错,展开运算符不能展开对象 报错信息: Uncaught TypeError: Found non-callable @@iterator
    person.name='jerry';
    console.log(person);//{name: "jerry", age: 18}
    console.log(person2);//{name: "无话", age: 18}

    //合并
    let person3={...person,name:'jack',age:'88'};
    console.log(person3);//{name: "jack", age: "88"}

</script>
2.3 用例(设定参数类型、必要性的限制、默认值)
 <script type="text/babel">
        class DemoProds extends React.Component{
            constructor(props){
                //构造器是否接收props,是否传递给super,取决于:是否希望在构造器中通过this访问props
                super(props);
                console.log(this.props);    
            }
            // 指定默认标签属性值
                static defaultProps = {
                    sex:'男',//性别默认值为男
                    age:19//年龄默认值为19
                }
               // 对标签属性进行类型、必要性的限制
               static propTypes= {
                name : PropTypes.string.isRequired,//限制name为必传,且为字符串
                age : PropTypes.number,//限制age为数字类型
                sex:PropTypes.string,//限制sex为数字类型
                speak:PropTypes.func//限制speak为函数类型
                 };
                render(){
                    console.log(this);
                    const{name,age,sex}=this.props;
                    // props的值只能为只读
                   // this.props.name="jack";//此行代码会报错,因为props的值为只读
                  
                    return (
                    <ul>
                        <li>姓名:{name}</li>
                        <li>性别:{sex}</li>
                        <li>年龄:{age+1}</li>
                    </ul>
                    )
                }                
            }
            
            const p={name:'幸运',sex:'女',age:18};
            //下面两种方法均可
            ReactDOM.render(<DemoProds name='何其' sex=" 男" age={21} speak={speak}/>,document.getElementById('test1'))
            ReactDOM.render(<DemoProds {...p}/>,document.getElementById('test'))

            function speak(){
                console.log("我说话了");
            }

  </script>

3.refs-双向绑定

3.1字符串形式的ref
<script type="text/babel">
           // 创建组件
           class Demo extends React.Component{
               render(){
                   return (
                       <div>
                         <input  type="text" ref="input1" placeholder="点击按钮提示数据"/>
                         <button type="button" onClick={this.showData}>点我提示左侧的数据</button>
                         <input type="text" onBlur={this.showData2} ref="input2" placeholder="失去焦点提示数据"/>
                       </div>
                   )
               }
               showData=()=>{
                   console.log(this);
                   const {input1} =this.refs;//<input type="text" placeholder="点击按钮提示数据">
                   console.log(input1.value)//输入的值
               }
               showData2=()=>{
                   console.log(this);
                   const {input2} =this.refs;//<input type="text" placeholder="点击按钮提示数据">
                   console.log(input2.value)//输入的值
               }
           }
           ReactDOM.render(<Demo />,document.getElementById('test'))
        </script>
3.2 回调函数形式的ref (常用)

ref={( c)=>{this.input1=c}}

<script type="text/babel">
           // 创建组件
           class Demo extends React.Component{
               render(){
                   return (
                       <div>
                         <input  type="text" ref={(c)=>{this.input1=c}} placeholder="点击按钮提示数据"/>
                         <button type="button" onClick={this.showData}>点我提示左侧的数据</button>
                         <input type="text" onBlur={this.showData2} ref={(c)=>{this.input2=c}} placeholder="失去焦点提示数据"/>
                       </div>
                   )
               }
               showData=()=>{
                   const {input1} =this;
                   console.log(input1.value)//输入的值                
               }
               showData2=()=>{
                   const {input2} =this;
                   console.log(input2.value)//输入的值
               }
           }
           ReactDOM.render(<Demo />,document.getElementById('test'))
        </script>
3.3关于回调函数的一些问题

如果ref回调函数是以内联函数的方式定义的,在更新过程中会被调用两次,第一次传入参数为null,然后第二次会传入参数DOM元素。这是因为在每次渲染时会创建一个新的函数实例,所以Ref清空旧的ref,并设置新的,通过将ref的回调函数定义成class的绑定函数,可避免此类问题,但在大多数情况下他是无关紧要的。

<script type="text/babel">
           // 创建组件
           class Demo extends React.Component{
               state={
                   isHot:true
               }
               render(){
                   const {isHot}=this.state;
                   return (
                       <div>
                       <span>今天天气很{isHot?'炎热':'凉爽'}</span>
                         <input  type="text" ref={(c)=>{
                             this.input1=c;
                             console.log("@",this.input1)
                          }} placeholder="点击按钮提示数据"/>
                         <button type="button" onClick={this.showData}>点我提示左侧的数据</button>
                         <button type="button" onClick={this.changeWeather}>改变天气按钮</button>                       
                       </div>
                   )
               }
               showData=()=>{
                   const {input1} =this;
                   console.log(input1.value)//输入的值                
               }
               changeWeather=()=>{
                   const {isHot}=this.state;
                   this.setState({isHot:!isHot})
               }
           }
           ReactDOM.render(<Demo />,document.getElementById('test'))
        </script>

注:当改变天气时(更新数据状态),ref的回调函数会执行两次:打印结果如下:

@ null
@ <input type=​"text" placeholder=​"点击按钮提示数据">​

** 解决方案

 <input  type="text" ref={this.saveInput}/>
// render外写:
 saveInput=(c)=>{                
         this.input1=c;
         console.log("@",this.input1)                
 }
3.4createRef (推荐)
<script type="text/babel">
           // 创建组件
           class Demo extends React.Component{
               /*
               React.createRef调用后可以返回一个容器,
               该容器可以存储被ref所标识的节点,该容器是专人专用
               */
               myRef=React.createRef();
               myRef2=React.createRef();               
               render(){                  
                   return (
                       <div>                      
                         <input  type="text" ref={this.myRef}/>
                         <button type="button" onClick={this.showData}>点我提示左侧的数据</button>
                         <input  type="text" onBlur={this.showData2} ref={this.myRef2} />                      
                       </div>
                   )
               }
               showData=()=>{                  
                   console.log(this.myRef.current.value)//输入的值                
               }
               showData2=()=>{                  
                   console.log(this.myRef2.current.value)//输入的值                
               }
           }
           ReactDOM.render(<Demo />,document.getElementById('test'))
        </script>

4.React中的事件处理

4.1 概念

(1).通过onXXX指定事件处理函数(注意大小写->react中用驼峰,如onClick)
a.React使用的是自定义(合成)事件,而不是使用的是原生DOM事件–为了更好的兼容性。
b.React中的事件是通过事件委托的方式处理的(委托给组件最外层的元素)–为了高效
(2).通过event.target得到发生事件的DOM元素对象—不要过度使用ref


当事件就在获取值的同一个标签中,可以去除ref,在事件传递参数即可

 <input  type="text" onBlur={this.showData2} /> 
 showData2=(event)=>{
 //event.target 获取的是<input  type="text" onBlur={this.showData2} />                  
  console.log(event.target.value)//输入的值                
 }

5 收集表单数据

5.1 非受控组件
通过ref接收原生dom,通过dom属性中的value值接收参数

原始form表单->

action:指定点击提交后跳转的路径;
onSumbit:执行button type="submit" 的按钮
event.preventDefault();:禁止action路径跳转
form表单提交时,执行action方法时跳转会携带表单中带name属性的值
<script type="text/babel">
           class Login extends React.Component{
                handelSubmit=(event)=>{
                   event.preventDefault(); // 取消页面刷新,阻止表单跳转至action指定路径
                   const {username,password} =this;
                   //用户名为[object HTMLInputElement],账户密码未233
                   alert(`用户名为${username},账户密码未${password.value}`)
                } 
               render(){
                   return (
                       <form onSubmit={this.handelSubmit}>
                            <input type="text" name="username" ref={c=>this.username=c}/>
                            <input type="password" name="password" ref={c=>this.password=c}/>
                            <button type="submit">提交</button>
                       </form>
                   )
               }
           }
           ReactDOM.render(<Login/>,document.getElementById("test"))
        </script>
5.2 受控组件
使用state接收前台参数
 <script type="text/babel">
           class Login extends React.Component{
               state={
                   username:""
               }
                handelSubmit=(event)=>{
                   event.preventDefault();
                   const {username} =this.state;
                   alert(`用户名为${username}`)
                } 
                saveUserName=(event)=>{
                    console.log(event.target.value);
                    this.setState({username:event.target.value})
                }
               render(){
                   return (
                       <form onSubmit={this.handelSubmit}>
                            <input type="text" name="username" onChange={this.saveUserName}/>
                            <button type="submit">提交</button>
                       </form>
                   )
               }
           }
           ReactDOM.render(<Login/>,document.getElementById("test"))
        </script>
5.3勿过度使用refs,所以尽量使用受控组件

6.高阶函数_函数柯里化

6.1 使用高阶函数_函数柯里化
0.概念

(1).高阶函数:如果一个函数符合如下两个规范中的任何一个,那该函数就是高阶函数
1.1 、若A函数接收的参数是一个函数
1.2、若A函数调用的返回值依然是一个函数
常见高阶函数:Promise,arr.map(),setTimeOut等等
(2)函数柯里化:通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式
function sum(a){
return (b){
return ( c){
return a+b+c;
}
}
}
sum(a)(b)( c) // 调用

1.不同input框中的值使用同一个函数进行获取值
         onChange={this.saveFormData('username')}
2.函数内部使用return 函数
  其实onChange={this.saveFormData} 返回的是一个函数
   onChange={this.saveFormData('username')}  返回undefined,所以需要              函数内部return 回调函数
3.dataType:event.target.value 是将值赋给dataType字符串

this.setState({[dataType]:event.target.value}) 是将值赋给dataType变量

 <script type="text/babel">
           class Login extends React.Component{
            
            saveFormData=(dataType)=>{
              return (event)=>{
                 console.log(dataType,event.target.value);
                 this.setState({[dataType]:event.target.value})
              }
            }
            render(){
                return (
                    <form onSubmit={this.handleSubmit}>
                        <input type="text" name="username" onChange={this.saveFormData('username')}/>
                        <input type="text" name="password" onChange={this.saveFormData('passowrd')}/>
                        <button type="submit">提交</button>
                    </form>
                )
            }
           }
           ReactDOM.render(<Login/>,document.getElementById("test"))
        </script>

记住:必须将一个函数返回给onChange等事件

6.2 不使用高阶函数_函数柯里化
<input type="text" name="username" onChange={(event)={this.saveFormData('username',event)}/}/>

saveFormData=(dataType,event)=>{
  this.setState({[dataType]:event.target.value})
}

7.生命周期函数

生命周期函数==》生命周期钩子函数==》生命周期回调函数==》生命周期钩子

7.1例子
1.隐藏组件:ReactDOM.unmountComponentAtNode
2.初始化后执行方法:componentDidMount
3.关闭组件之前:componentWillUnmount
<script type="text/babel">
           class Login extends React.Component{ 
               state={opacity:1}        
                death=()=>{
                    // 清除定时器 this.timer 为定时器id,说明是哪个定时器
                    clearInterval(this.timer);
                    // 卸载组件--点击button按钮使类式组件消失
                    ReactDOM.unmountComponentAtNode(document.getElementById("test"));
                }
                // 组件挂载完毕后执行的方法,即执行完render
                componentDidMount() {
                    this.timer =setInterval(()=>{
                        let {opacity} =this.state;
                        opacity-=0.1;
                        if(opacity<=0){
                            opacity=1;
                        }
                        this.setState({opacity})     
                        },200)
                }
                // 组件将要被卸载
                componentWillUnmount(){
                  // 在这里关闭定时器也可
                }
                // 初始化渲染,状态更新之后
                render(){
                    return (
                    <div>
                            <h1 style={{opacity:this.state.opacity}}>学不会react,我怎么办</h1>
                            <button onClick={this.death}>不活了</button>
                    </div>
                    )
                }
           }
           ReactDOM.render(<Login/>,document.getElementById("test"))
        </script>
7.2 生命周期函数执行顺序

1.初始化时执行的顺序:constructor–>componentWillMount–>render–>componentDidMount–>componentWillUnmont
2.数值更新的顺序:setState–>shouldComponentUpdate–>componentWillUpdate–>render–>componentDidMount–>componentWillUnmont
3.数值强制更新:this.forceUpdate–>componentWillUpdate–>render–>componentDidMount–>componentWillUnmont
4.父子组件通信:componentWillReceiveProps–>shouldComponentUpdate–>componentWillUpdate–>render–>componentDidMount–>componentWillUnmont

组件名称功能使用频率使用场景
constructor构造器
componentWillMount组件生成之前执行的函数
render组件生成的函数常用初始化渲染调用或更新渲染调用
componentDidMount组件生成后执行的函数常用开启定时器,消息订阅,发送网络请求–开启监听,发送ajax请求
componentWillUnmont组件卸载前执行的函数
this.setState数值更新执行函数
this.forceUpdate数值强制更新
shouldComponentUpdate组件是否进行更新函数,若不写,则默认为true,若写,则根据 return true or false 进行判断。
componentWillUpdate若shouldComponentUpdate 返回true ,则更新之前执行的函数
componentDidMount若shouldComponentUpdate 返回true ,则更新之后执行的函数
componentWillReceiveProps父子组件通信时,子组件接收父组件数据之前执行的函数(但第一次赋值是不执行的)
ReactDOM.unmountComponentAtNode(document.getElementById(“test”))之前 执行componentWillUnmount卸载组件常用关闭定时器,取消消息订阅–做一些收尾动作

推荐使用BootCDN-前端核心JS库-下载JS

7.3 新的生命周期与旧的生命周期区别

新的生命周期与旧的生命周期并无多少区别,只在于:
1.废弃了三个钩子
componentWillMount–》UNSAFE_componentWillMount
componentWillUpdate–》UNSAFE_componentWillUpdate
componentWillReceiveProps–》UNSAFE_componentWillReceiveProps

React 官网提示,过时的组件生命周期往往会带来不安全的编码实践,所以以后可能会废弃

2.新增了两个钩子
getDeriveStateFromProps
(0)函数必须为静态,并且必须有返回值为null,或者为props对象{count:1}
(1)功能:将props里的值赋给state,
(2)使用场景:当state的值在任何时候都取决于props时

// 功能:将props里的值赋给state,
// 使用场景:当state的值在任何时候都取决于props时
static getDeriveStateFromProps(props,state){
   return props;
}
ReactDOM.render(<Demo count="1"/>,document.getElementById("test"));

getSnapshotBeforeUpdate
使用场景如下:

<body>
    <div id="test"></div>
    <script src="../../react/js/17.0.2/react.development.js" type="text/javaScript"></script>
    <script src="../../react/js/17.0.2/react-dom.development.js" type="text/javaScript"></script>
    <!-- Cannot read property 'keys' of undefined    babel 版本过高,还原以前版本-->
    <script src="../../react/js/babel.min.js" type="text/javaScript"></script>
    <script src="../../react/js/17.0.2/prop-types.js" type="text/javaScript"></script>
    <style>
     .list{
         width:200px;
         height: 150px;
         background-color:green;
         overflow: auto;
     }
     .news{
         height:40px;

     }
    </style>
    <script type="text/babel">
    
    class List extends React.Component{
        state={
            arr:[]
        }
        // 组件生成后执行的钩子
        componentDidMount(){
            setInterval(() => {
                const {arr}=this.state;
                const newarr="新闻"+(arr.length+1);
                this.setState({arr:[newarr,...arr]});
            }, 1000);
        }
        // 记录滚动条内容长度
        getSnapshotBeforeUpdate(){
            return this.refs.list.scrollHeight;
        }
         // 数据更新后,累加;height是getSnapshotBeforeUpdate return 值              
        componentDidUpdate(preProps,preState,height){
           this.refs.list.scrollTop+=this.refs.list.scrollHeight-height;
        }
        render(){
            return (
                <div className="list" ref="list">
                  {
                      this.state.arr.map((temp,index)=>{
                          return  <div className="news" key={index}>{temp}</div>
                      })
                  }
                 
                </div>
            )
        }
    }
   ReactDOM.render(<List/>,document.getElementById("test"));
    
    </script>
</body>
7.4 总结

在这里插入图片描述

8. Diffing 算法原理

8.1 原理与优势

原理:React的Diffing 原理是比较将旧的虚拟DOM与新的虚拟DOM做比较,比较粒度为标签,若旧的虚拟DOM与新的虚拟DOM相同,则使用旧的虚拟DOM。若不同则使用新的。
优势:大大提高页面映射效率。

8.2 React 中key值设定

1.使用索引 index 进行设定。
2.数据中添加id(唯一标记属性值)进行设定。—推荐使用
面试常考题如下:
在这里插入图片描述

9.路由

9.1 示例

在这里插入图片描述
注:
1.原生html中用 <a>跳转不同页面

<a href="./home.html" ></a>

在React中靠路由连接实现切换组件–编写路由连接

<Link to="/home">home</Link>

React 中注册路由

<Route path="/home" component={Home}>home</Route>
9.2 <MyNavLink>的封装与Switch的使用

1.MyNavLink的封装

<MyNavLink to="/home"></MyNavLink>

MyNavLink Component:

<Link {...this.props} />

2.Switch的使用

<Switch> // 功能是当匹配了某条route,则不再往下匹配
	<Route path="/home" component={Home} />
	<Route path="/about" component={About} />
</Switch>

3.模糊查询与精准查询
模糊查询:

//是可以找到home组件的
<MyNavLink to="/home/a/b"></MyNavLink>
<Route path="/home" component={Home} />
//无法找到home组件
<MyNavLink to="/home"></MyNavLink>
<Route path="/home/a/b" component={Home} />

精准查询:添加exact --严格匹配优势会造成二级路由无法查询

<Route path="/home" exact component={Home} />
9.3 Redirect 的使用

1.一般编写在路由的最下方,当所有路由无法匹配时将会跳转到Redirect 指定路由
2.具体编码

<Switch>
	<Route path="/home" component={Home} />
	<Route path="/about" component={About} />
	<Redirect to="/about" />
</Switch>
9.4二级路由的使用

App.js

 <BrowserRouter>
    <div style={{width:'25%',float:'left'}}>
       <Link to="/home">home</Link>
       <br/>
       <Link to="/about">about</Link>
    </div>
    <div style={{width:'75%',float:'right'}}>
       <Route path="/home" component={Home} />
       <Route path="/about" component={About} />
       <Redirect to="/home" />
    </div>
 </BrowserRouter>

Home.js

<div>
   <Link to="/home/news">News</Link>&nbsp;&nbsp; 
   <Link to="/home/message">Message</Link>
   <Route path="/home/news" component={News}></Route>
   <Route path="/home/message" component={Message}></Route>
   <Redirect to="/home/news" />
 </div>

HOME/NEWS/index.jsx

<div>
  <ul>
     <li>君不见黄河之水天上来</li>
     <li>奔流到海不复回</li>
  </ul>
</div>

HOME/Message/index.jsx

<div>
  <ul>
    <li>君不见高堂明镜悲白发</li>
    <li>朝如青丝暮成雪</li>
  </ul>
</div>
9.5 向路由组件传递参数
9.5.1 params参数

路由:

// 路由传递参数:${msgobj.id}
<Link to={`/home/message/detail/${msgobj.id}/${msgobj.text}`}>{msgobj.text}</Link>
// 声明接收参数
<Route path="/home/message/detail/:id/:text" component={Detail} />

接收路由组件

  const {id,text}=this.props.match.params;

/Home/Message

import React, { Component } from 'react';
import {Link,Route} from 'react-router-dom'
import Detail from './Detail';

class index extends Component {
     state={
            messageList:[
                {id:1,text:"君不见高堂明镜悲白发"},
                {id:2,text:"朝如青丝暮成雪"},
                {id:3,text:"人生得意须尽欢"},
                {id:4,text:"莫使金樽空对月"}
            ]
        }        
    render() {
        const {messageList}=this.state;       
        return (
            <div>
                <ul>
                {
                    messageList.map((msgobj)=>{
                        return (
                            <li key={msgobj.id}>
                            {/* 向组件传递params参数 */}
                                <Link to={`/home/message/detail/${msgobj.id}/${msgobj.text}`}>{msgobj.text}</Link>
                            </li>
                        )
                    })
                }
                </ul>
                <hr />
                {/*声明接收Params参数 */}
                <Route path="/home/message/detail/:id/:text" component={Detail} />
            </div>
        );
    }
}

export default index;

/Home/Message/Detail

import React, { Component } from 'react';

const detailData=[
    {id:1,content:"你好,地球"},
    {id:2,content:"你好,月球"},
    {id:3,content:"你好,火星"},
    {id:4,content:"你好,中国"},
]
class index extends Component {
    render() {
        console.log(this.props);
        const {id,text}=this.props.match.params;
        // 获取路由传递参数id与detailData中id一致的内容
        // 从路由传过来的id为字符串
        const findResult=detailData.find((detailObj)=>{
            return detailObj.id.toString()===id;
        })
        return (
            <div>
                <ul>
                  <li>ID:{id}</li>
                  <li>TITLE:将进酒</li>
                  <li>CONTENT:{text}</li>
                  <li>WELCOME:{findResult.content}</li>
                </ul>
            </div>
        );
    }
}

export default index;
9.5.2 search参数

路由组件:

<Link to={`/home/message/detail?id=${msgobj.id}&text=${msgobj.text}`}>{msgobj.text}</Link>
<Route path="/home/message/detail" component={Detail} />

接收路由参数:

import qs from 'querystring'
const {id,text}=qs.parse(this.props.location.search.slice(1))

备注:获取到的search是urlencoded编码字符串,需要借助querystring解析
知识点:qs.parse(?id=1&text=中国)==={id:1,text:中国}

9.5.3 state参数

路由组件:

 <Link to={{pathname:"/home/message/detail",state:{id:msgobj.id,text:msgobj.text}}}>{msgobj.text}</Link>

<Route path="/home/message/detail" component={Detail} />

接收路由参数:

const {id,text}=this.props.location.state||{};
// 从路由传过来的id为字符串
const findResult=detailData.find((detailObj)=>{
    return detailObj.id.toString()===id;
})||{}

备注:刷新也可以保留住参数

9.5.4 总结

在这里插入图片描述

9.5.6 路由的push与replace

push:进行路由的压栈操作(默认)
replace:路由替换操作,使history时返回不进行替换路由的返回

<Link  replace to="/home/message">Message</Link>
9.5.7 编程式路由跳转 --通过点击按钮/图片等进行跳转

利用props中history.push方法和replace方法进行
参数携带方式可使用params,search,state方式接收
this.props.history
push:路由压栈跳转
replace:路由替换跳转
go:前进或后退几个路由,go(0)刷新
goForward:前进
goBack:回退

pushRouter=(id,title)=>{
   this.props.history.push(`/home/message/detail/${id}/${title}`);
 }    
 replaceRouter=(id,title)=>{
   this.props.history.replace(`/home/message/detail/${id}/${title}`);
 } 
 {
    messageList.map((msgobj)=>{
          return (
              <li key={msgobj.id}>
                  <Link to={{pathname:"/home/message/detail",state:{id:msgobj.id,text:msgobj.text}}}>{msgobj.text}</Link>
                  <button onClick={()=>{this.pushRouter(msgobj.id,msgobj.text)}}>push路由參數</button>
                  <button onClick={()=>{this.replaceRouter(msgobj.id,msgobj.text)}}>replace路由參數</button>
              </li>
          )
      })
   }
9.6 向一般组件传递参数-withRouter

用法:
原本一般组件的props={}
但将组件withRouter加工过后就会有路由组件的参数

import React, { Component } from 'react';
import {withRouter} from 'react-router-dom'

class index extends Component {
    render() {
        console.log(this.props)
        return (
            <div>
                <h1>React Demo</h1>
            </div>
        );
    }
}
export default withRouter(index);
9.7 BrowserRouter与HashRouter的区别

1.底层原理不一样
BrowserRouter使用的是H5的history API,不兼容IE9及以下版本
HashRouter使用的是URL的哈希值
2.url表现形式不一样
BrowserRouter路径中没有#,例:http://localhost:8000/test/demo
HashRouter路径中有#,例:http://localhost:8000/#/test/demo
3.刷新后对路由state参数的影响
BrowserRouter没有任何影响,因为state保留在history对象中
HashRouter刷新后会导致路由state参数的丢失
4.HashRouter可以用于解决一些路径错误相关的问题

10.antd

10.1antd的按需引入+自定义主题

官网中有方法,左侧—在create-react-app中使用

11.Redux

定义:是一个专门用来做状态管理的JS库(不是react插件库)
作用:集中式管理react应用中多个组件共享的状态
使用情况:
(1)某个组件的状态,需要其他组件随时拿到----共享
(2)一个组件需要改变另一个组件的状态----通信
(3)总体原则:能不用就不用,如果通信非常费劲才要使用
在这里插入图片描述

11.1 redux的三个核心概念

action
1.动作的对象
2.包含两个属性

  • type:标识属性,值为字符串,唯一,必要属性
  • data:数据属性,值任意类型,可选属性
    reducer
    1.用于初始化状态,加工状态
    2.加工时根据旧的state和action,产生新的state的纯函数
    store
    1.将state,action,reducer连接在一起的对象
    2.如何得到此对象
    import {createStore} from ‘redux’
    import {reducer} from ‘./reducers’
    11.2 redux 的用法
    1.yarn add redux
    2.示例-- 精简版(无action creator):
    在这里插入图片描述
    store.js
/*
該文件專門用來暴露一個store對象,整個應用只有一個store對象
*/

// 引入createStore, 專門用於創建redux中最為核心的store對象
import { createStore } from 'redux'

//引入為Count組件服務的reducer --不帶{}為默認暴露,說明那個文件里只有一個export
import countReducer from './count_reducer' 

// 暴露store
export default createStore(countReducer);

count_reducer

/*
1.該文件是用於創建一個Count組件服務的reducer,reducer 本質就是一個函數
2.reducer 函數可以接收兩個參數,分別為:之前的狀態(preState),動作對象(action)
*/

// 初始化時preState=undefined action.type=@@Init 無action.data
const initState = 0; // 初始化狀態
export default function countReducer(preState=initState,action) {
    // if(preState==undefined) preState = 0;
    // 從action 對象中獲取:type,data
    const {type,data}=action;
    // 根據type決定如何加工數據
    switch (type) {
        case 'increment': // 如果是加
            return preState+data  
        case 'decrement': // 如果是減
            return preState+data  
    
        default:
            return preState
            break;
    }
}

Count

import React, { Component } from 'react';
// 引入store 
import store from '../../redux/store.js'

class Count extends Component {
    state={count:0}

    componentDidMount(){
      // 監測redux中狀態的變化,只要變化就調用render
      store.subscribe(()=>{
        this.setState({});
      })
    }

    // 加法
    increment=()=>{
      const {value} =this.selectNumber;
      store.dispatch({type:'increment',data:value*1})
      // const {count} =this.state;
      // this.setState({count:count + value*1})
    }
    // 减法
    decrement=()=>{
      const {value} =this.selectNumber;
       store.dispatch({type:'decrement',data:value*1})
      // const {count} =this.state;
      // this.setState({count:count - value*1})
    }
    // 奇数加
    incrementIfOdd=()=>{
      const {value} =this.selectNumber;
      // const {count} =this.state;
      const count =store.getState();
      if(count % 2 !== 0){
        store.dispatch({type:'increment',data:value*1})
        // this.setState({count:count + value*1})
      }      
    }
    // 异步加
    incrementAsync=()=>{
       const {value} =this.selectNumber;
      // const {count} =this.state;
      setTimeout(() => {
        // this.setState({count:count+value*1})
        store.dispatch({type:'increment',data:value*1})
      }, 500);
    }
    render() {
        // const {count} =this.state;
        return (
            <div>
                {/* <h1>当前求和为{count}</h1> */}
                <h1>{store.getState()}</h1>
                <select ref={c=>this.selectNumber =c}>
                     <option value="1">1</option>
                     <option value="2">2</option>
                     <option value="3">3</option>
                </select>
                <button onClick={this.increment}>+</button>
                <button onClick={this.decrement}>-</button>
                <button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>
                <button onClick={this.incrementAsync}>异步加</button>
            </div>
        );
    }
}

export default Count;
  • 全局监听组件,一旦改变值,则重新渲染render
    index.js
store.subscribe(()=>{
   ReactDOM.render(<App />,document.getElementById('root'))
 })
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值