(尚)react教程(2022-11-20)

第一章:JavaScript复习

1.复习js中的三种变量声明
三种声明方式:第一种:首先使用的const  如果希望变量被改变则使用let关键字  至于var关键字最好报在代码中出现
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script>
/*var和let对比:
* var
* -没有块级作用域
*
* let
* -有块级作用域
*
*const  和let类似 具有块级作用域 但是它只能赋值一次  使用场景:对于一些常量可以使用这个关键字
* 
* 对于一些对象函数也可以使用这个关键字 这样可以避免对象和函数被意外修改
* */
        var a=2;//
{
var b=1;//块作用域

}
function fn(){

    var  a=1;

}

console.log(a)

for (var i = 0; i < 10; i++) {
    console.log(i);//在循环内部访问
}
console.log(i)//在循环外部访问


if (true){

    var tx=3;
    console.log("内部"+tx)
}
    //在外面访问tx
console.log("外部"+tx)


    //因此只能使用闭包来书写
    (function () {

       if (true){

           var  mn=12;
           console.log("mn为")
           console.log(mn);

       }
    })()

const Pi=3.14;
let t=2;
t=Pi*t*t;
console.log(t);



    </script>
</head>
<body>

</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>解构赋值</title>
    <script>
        // let a,b;
        // let arr=['吴邪','解雨臣'];
        // a=arr[0];
        // b=arr[1];
        // console.log(a,b);
    //    以上方法不太友好  因此使用解构赋值
    //     [a,b]=arr;
    //     console.log(a,b);//效果一样

/*
const [a,b]=arr;//声明完直接赋值
        console.log(a,b)

*/

function f() {
    return ['吴邪','王胖子','解雨臣','张起灵'];
}

    //调用函数来赋值
        const [a,b, ,...c]=f();//可以跳过元素  ...变量 会接受后面所有的元素
        console.log(a,b,c)

    //    对于对象的解包
        const obj={
            name:'吴邪',
            age:19,
            gender:'男'
        };
        let x,y,z;

    //    传统的赋值方式
        x=obj.name;
        y=obj.age;
        z=obj.gender;
        console.log(x,y,z);//这种方式太麻烦



    //
    //    对象的解构:

        let o,d,j;
        ({
            name:o,//将name赋值给o
            age:d,//将age赋值给d
            gender: j,//将gender赋值给j

        }=obj) ;//如果变量名和属性名一致  则可以只写一个

        console.log(o,d,j)


    //    利用数组的解构来交换两个变量的位置
        let p=10,q=20;
        // console.log(p,q)

    //    交换变量
        [p,q]=[q,p];
        console.log(p,q);


        arr01=[12,14,15,12]
        [arr01[1],arr01[2]]=[arr01[2],arr01[1]];//交换数组的值
        console.log(arr01)

    </script>
</head>
<body>

</body>
</html>
展开:

//展开:
function fn(a,b,c) {
    return a+b+c;
}

const arr=[1,2,3];
//计算数组中三个数的和
/*let result=fn(arr[0],arr[1],arr[2]);
console.log(result)*/

//可以通过 ...来展开一个数组
let result=fn(...arr)
console.log(result)



//2.相当于将arr复制
const arr2=[...arr,4,5,6];
console.log(arr2);


//3.对象的展开  相当于一个复制

const obj={

    name:'解雨臣',
    age:'23',
    sex:'男'

};

const obj2={...obj,address:'吴山居'};

console.log(obj2);



//箭头函数:箭头函数是传统函数表达式的简写方式 他简化了函数的编写
//特点:箭头函数没有自己的this 不能作为构造函数来使用  无法通过call    apply  bind 指定函数的this


//1.箭头函数的定义:

//类型一:只有一个参数的箭头函数  参数=>返回值
const fn=function (a) {
    return '我是函数';
}

//等价于:
const fn2=a =>'我是函数';

console.log(fn2(1));

//类型二:两个参数:如果没有参数 或多个参数 参数需要用()括起来
//()=>返回值   (a,b,c)=>返回值
const fn3 =(a,b,c)=>a+'我是另一个箭头函数';
console.log(fn3());

//例如:
const sum=(a,b)=>a+b;//箭头后面的值就是返回值  返回值必须是·一个表达式
let t=sum(1,2);
console.log(t);

//表达式:有返回值的语句

const vb=(m,n)=>({name:'吴邪',age:18,sex:'男'})
console.log(vb());

//如果需要在箭头函数中定义逻辑 则可以在后面书写代码块

const fn4=(a,b)=>{
            if (a===b){
                a+=5;
                console.log(a)

            }else if (a===10){

                a+=10;
            }
            return a+b;
            
};
console.log(fn4(2,2))


/*
* 箭头函数:只有一个参数返回值  参数 =>返回值
*  如果没有参数 或多个参数 参数需要使用()括起来
*   ()=>返回值
* (a,b,c)=>返回值
*
*   箭头后面的值就是函数的返回值
*   返回值必须是一个表达式
*   如果返回值是对象 必须加()
*
* 如果需要在箭头函数中定义逻辑 可以直接在箭头后跟一个代码块
*
*
* */


//特点:

function fn(...args){

    console.log(args);
    console.log(arguments.length);//表示当前函数的实参 用来保存函数的实参
}


fn();


const fn2=(...aggs)=>{

    // console.log(arguments)  箭头函数没有这个
    console.log(aggs);

};
fn2()
/*
* 1.箭头函数中没有argue
* 2.箭头函数中没有自己的this
*       -它的this总是外边的this  也就是全局作用域的this
* 3.箭头函数中的this无法通过call() apply()来修改
*
* 4.箭头函数无法作为构造函数来使用
* */

const fn3=()=>{

    console.log(this)

};
fn3()


const obj={

    hello:()=>{

        console.log(this)

    }

}
obj.hello();

new fn();//fn是普通函数

js中的模块化:


/*js中的模块化:
*
*es中的模块化分为为:export (导出)和 import(导入)
*
* */

export const a=10;//导出a
const b=20;
const c=90;
const obj={
    name:'吴邪',
    age:'18'
}

const fn=()=>{

    console.log('我是fn');

}
fn()

//作为一个模块,我们不希望模块中的内容都暴露给外部
//作为导入方:我们也不希望导入无用的变量
// export导出:导出用来决定模块中那些内容可以被外部查看
//导出分为两种:
//1.默认导出
//2.命名导出


//1.默认导出:语法:export default 要导出的值
export default a;//将变量a作为默认导出


/*2.导入:import  导入用来将外部模块中的内容导入当前模块中
*       -import obj from '../react教程/react03'  导入模块时 变量名可以自主指定 无需和模块中的变量名对应
* */

//导入模块中的默认模块
import a from './react03';//这样写不会成功
    

/*
* 默认情况下script标签不能使用import语句:如果想让其支持模块化 必须设置script属性为module
*
* */
import a from '../react教程/react03'

console.log(a)

/*
* 在网页中导入模块时  模块的路径必须写完整  (./或../开头 扩展名也必须写上)
* */



//导入指定的内容:import {b,c} from './react02.js'
//也可以更改变量名称:import {b as x,c} from './react02.js'

export {obj,fn};//命名导出


类(Class):类是对象的模块  类中定义了对象中包含了那些属性和方法 也可以跳过function来定义类 但是两种方式并不是完全通用


/*
* 1.类的定义:
* 类是对象的模板  类决定了一个对象有哪些属性和方法
*       使用class关键字来定义一个类
*
*
* */

class person{
//在类中可以定义属性
//    属性
    name='吴邪';
    //属性
    age=19;


//    在类中定义方法
//    定义了一个实例化方法
    run(){

        console.log("我是一个实例化方法")
    }

//    构造函数:当我们通过new创建对象时 实际上就是在调用类的构造函数

    constructor(name,age) {
        //在构造函数中我们可以跳过this来应用当前的对象

        //将参数赋值给对象中的属性

        this.name=name;
        this.age=age;


        console.log("我是构造函数")
    }

}
const per=new person('吴邪','12');
console.log(per);

//类中的MyClass
class MyClass{

fn(){

    console.log(this)

}
fn3=()=>{
    console.log(this)
    
}
}
const mc=new MyClass();

console.log(mc)
mc.fn();//调用方法  和Java中的一样

const test=mc.fn;
test()//以函数的形式调用  就是未定义

//类中的所有代码斗湖在严格模式下执行  严格模式的特点就是函数的this就不是window
"use strict";

//注意:在类中方法的this不是固定的  以方法形式调用时  this就是当前的实例  以函数形式调用 this是undefined

//在开发的时候 我们希望方法中的this是固定的 不会因为调用方式不同而改变

//如果有这种需求 我们可以使用箭头函数来定义  
/*如果类中的方法是以箭头函数来定义的 则方法中的this恒为当前示例 不会改变
* 
* */
/*
* 类中的继承:
*
* */

class Dog{

    constructor(name,age) {

        this.name=name;
        this.age=age;
    }


    sayHello=()=>{

        console.log("你好")

    }

}

//从以上代码可以看出:上面哟荤多重复的代码 一次我们可以吧许多共有的代码提取出来 然后使用继承
//将多个重复代码提取出来

class Animal{
    constructor(name,age) {
    this.name=name;
    this.age=age;

    }
    sayHello=()=>{

        console.log("吴邪")

    }
}


//通过使用继承 我们可以使得其他类中的属性和方法
/*
class Snake{
    constructor(name,age) {
        this.name=name;
        this.age=age;

    }
    SayHello=()=>{
        console.log("你好!")

    }

}*/

//使用extends来继承一个类 继承后就相当于将该类的代码赋值到了当前类
class Snake extends Animal{
//当我们在子类中重写父类构造函数时  必须在子类构造函数中第一时间调用父类构造函数 否则会报错
    constructor(lenngth,anme,age) {
        
        super(name,age);//这个就是第一时间调用
    //    然后再重新定义一个length
        this.len=lenngth;
    }

//    很明显代码块内没有代码但是仍然可以控制台输出
}
const dog=new Snake('吴邪','12');
dog.sayHello()
console.log(dog);

//当我们使用继承后 被继承的类就称为父类  继承父类的类 称为子类
//子类继承父类后  将获得父类指定所有属性和方法
//也可以创建同名属性或方法来对父类进行重写
/*静态属性和方法:
*
*       直接通过类调用的属性和方法被称为静态属性和静态方法
*
*
*
* */

class MyClass{
    name='吴邪';//属性
    fn=()=>{//方法
        console.log("铁三角")

    }

}

//开始面向对象
const mc=new MyClass();
mc.fn()
console.log(mc.name);

//使用static关键字来定义
class MyClass1{
   static name='吴邪';//属性
   static fn=()=>{//方法
        console.log("铁三角")

    }

}

//开始面向对象  
console.log(MyClass1.name);
console.log(MyClass1.fn());
/*数组的方法:
* 1.map()-根据原有数组返回一个新数组
*
* */

const arr=[1,2,3,45,6];

let map = arr.map(()=>10);//这个直接是把数组中的所有元素都变为10
console.log(map);
//需要一个回调函数作为参数  回调函数的返回值会最为数组中的元素


/*let map1 = arr.map((item)=>item+2);//这个代表把数组中的每个元素都加2
console.log(map1);*/


let map1 = arr.map((item,index)=>item+2);//这个代表把数组中的每个元素都加2
console.log(map1);

//回调函数中有三个参数:
/*
* 第一个参数:当前元素
* 第二个参数:当前元素的索引
* 第三个参数:当前数组
*
* */


const arr2=['吴邪','解雨臣','张起灵','王胖子']
let strings = arr2.map(item=>"<li>"+item+"<li>");
console.log(strings);


/*
*
* filter()过滤:可以从一个数组中获得符合条件的元素*/

const arr3=[1,2,3,4,5];
let numbers = arr3.filter(item=>item%2===0);
console.log(numbers);
//会根据回调函数的结果来决定是否保留元素 true保留 FALSE 不保留


//find()会从数组中找到符合条件的第一个元素

let number = arr3.find(items=>items%2===0);
console.log(number);


//reduce()用来整合数组  也就是对数组中的元素进行运算
console.log(arr.reduce((m, n) => {

    console.log(m, n);
    return m + n;

}),1);//指定初始值

//需要两个参数:回调函数:指定运算规则  四个参数:m上一次运算结构 n当前值  index 当前索引 arr 当前值
//初始值:用来指定第一次运算的m 如果不指定则直接从第二个元素开始计算

第二章:react入门

1.网页中是BS架构 

react是一个用于构建用户界面的JavaScript库 用来为携带的网络构建用户界面 react起源于Facebook 
react特点:虚拟dom  声明式  基于组件  支持服务器端渲染 快速简单 易学 
2.书写第一个程序:hello world
解决了兼容性的问题
基于传统编程的命令式  react采用声明式

helloworld的书写:在入门阶段我们使用js脚本来书写:
react.js
react-dom.js

代码示例:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>hello world</title>
    <!--1.引入react的库-->
    <script src="script/react.development.js"></script>
<!--2.引入react的dom库-->
    <script src="script/react-dom.development.js"></script>

</head>
<body>
<div id="root"></div>


<script >

    /*
    * react就是用来代替dom的
    * 通过dom向页面中添加一个div
    * */
/*原生js操作*/
/*//    1.创建一个div
    const div =document.createElement('div')
//    2.向div中设置问题
    div.innerText='我是一个div'
//    3.获取root
    const root=document.getElementById('root')
//    4.将div添加到页面中
    root.appendChild(div)*/


//    二.使用react库来操作dom

/*React.createElement()--用来创建一个react元素  参数:1.元素名(组件名)  2.元素的属性  3.元素的子元素(内容)
*
* */
//    1.通过react向页面中添加一个div
   const div= React.createElement('div',{},'我是创建的div');//创建一个react元素

    //2.获取根元素对应的react元素
 const root=   ReactDOM.createRoot(document.getElementById('root'))//这个方法用来创建根元素  需要一个DOm元素作为参数

    //3.将div渲染到根元素中
    root.render(div);

    

    // console.log(div);

</script>




</body>
</html>





三个API之一:
<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title>三个API</title>
    <script src="script/react.development.js"></script>
    <script src="script/react-dom.development.js"></script>
</head>
<body>
<button id="btn">我是按钮</button>
<div id="root"></div>

<script>
    /*
    * React.createElement()
    *   - 用来创建一个React元素
    *   - 参数:
    *        1.元素的名称(html标签必须小写)
    *        2.标签中的属性
    *           - class属性需要使用className来设置
    *           - 在设置事件时,属性名需要修改为驼峰命名法
    *       3.元素的内容(子元素)
    *   - 注意点:
    *       React元素最终会通过虚拟DOM转换为真实的DOM元素
    *       React元素一旦创建就无法修改,只能通过新创建的元素进行替换
    *
    *
    * */

    // 创建一个React元素
    const button = React.createElement('button', {
        type: 'button',
        className: 'hello',
        onClick: () => {
            alert('你点我干嘛')
        }
    }, '点我一下');

    // 创建第一个div
    const div = React.createElement('div', {}, '我是一个div', button);

    // 获取根元素
    const root = ReactDOM.createRoot(document.getElementById('root'));

    // 将元素在根元素中显示
    root.render(div);

    // 获取按钮对象
    const btn = document.getElementById('btn');
    btn.addEventListener('click', ()=>{
        // 点击按钮后,修改div中button的文字为click me
        const button = React.createElement('button', {
            type: 'button',
            className: 'hello',
            onClick: () => {
                alert('你点我干嘛')
            }
        }, 'click me');

        // 创建一个div
        const div = React.createElement('div', {}, '我是一个div', button);

        // 修改React元素后,必须重新对根元素进行渲染
        // 当调用render渲染页面时,React会自动比较两次渲染的元素,只在真实DOM中更新发生变化的部分
        //      没发生变化的保持不变
        root.render(div);
    });
</script>

</body>
</html>


 
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>

    <script src="script/react.development.js"></script>

    <script src="script/react-dom.development.js"></script>
    <script >
        <!--    1.创建一个react元素-->
const button=React.createElement('button',{


    type:'button',
    className:'hello',
    onClick:()=>{
        alert("点我干嘛")
    }
},'点我一下')

    //1.创建一个div
        const div=React.createElement('div',{},'我是一个模块',button)

    //2.获取根元素: 就是React元素要插入的位置
        const  root=ReactDOM.createRoot(document.getElementById('root'))//老版本中react中使用的方法
    //3/将元素在根元素中显示
        root.render(div)// root.render()用来将react元素渲染到根元素中  根元素中所有的内容都会被删除 被react所替换  当调用render的
    //    时候  react会将两次的渲染结果进行比较 他会确保只修改那些发生变化的元素 对dom做最少的修改

    </script>

</head>
<body>


<div id="root">




</div>



</body>
</html>

第三章:JSX介绍

1.jsx说明

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title>JSX</title>
    <script src="script/react.development.js"></script>
    <script src="script/react-dom.development.js"></script>
    <!-- 引入babel -->
    <script src="script/babel.min.js"></script>
</head>
<body>
<div id="root"></div>

<!--设置js代码被babel处理-->
<script type="text/babel">

    // 创建一个React元素 <button>我是按钮</button>
    // 命令式编程
    // const button = React.createElement('button', {}, '我是按钮');

    // 声明式编程,结果导向的编程
    // 在React中可以通过JSX(JS扩展)来创建React元素,JSX需要被翻译为JS代码,才能被React执行
    // 要在React中使用JSX,必须引入babel来完成“翻译”工作
    // const button = <button>我是按钮</button>; // React.createElement('button', {}, '我是按钮');

    /*
    *   JSX就是React.createElement()的语法糖
    *       JSX在执行之前都会被babel转换为js代码
    * */
    const div = <div>
        我是一个div
        <button>我是按钮</button>
    </div>;

    const root = ReactDOM.createRoot(document.getElementById('root'));
    root.render(div);

</script>
</body>
</html>
<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title>JSX的注意</title>
    <script src="script/react.development.js"></script>
    <script src="script/react-dom.development.js"></script>
    <!-- 引入babel -->
    <script src="script/babel.min.js"></script>
</head>
<body>
<div id="root"></div>

<script type="text/babel">
    /*
    *   JSX的注意事项
    *       1. JSX不是字符串,不要加引号
    *       2. JSX中html标签应该小写,React组件应该大写开头
    *       3. JSX中有且只有一个根标签
    *       4. JSX的标签必须正确结束(自结束标签必须写/)
    *       5. 在JSX中可以使用{}嵌入表达式
    *              - 有值的语句的就是表达式
    *       6. 如果表达式是空值、布尔值、undefined,将不会显示
    *       7. 在JSX中,属性可以直接在标签中设置
    *           注意:
    *               class需要使用className代替
    *               style中必须使用对象设置
    *                   style={{background:'red'}}
    *
    * */

    function fn() {
        return 'hello';
    }

    const name = '孙悟空';

    const div = <div
        id="box"

        onClick={() => {
            alert('哈哈')
        }} className="box1"

        // style中必须使用对象设置    style={{background:'red'}}
                
        style={{backgroundColor: "yellowgreen", border: '10px red solid'}}
    >
        我是一个div
        <ul>
            <li>列表项</li>
        </ul>

        <input type="text"/>

        <div>
            {name} <br/>
            {1 + 1} <br/>
            {fn()} <br/>
            {NaN} <br/>
        </div>
    </div>;

    // alert(div);


    const root = ReactDOM.createRoot(document.getElementById('root'));
    root.render(div);

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

2.列表渲染

注意:如果在页面中书写的代码没有在对应的网页中显示可以更改一下默认值:
	<html lang="en">   更改为<html lang="zh">
	
	
	<!DOCTYPE html>
<html lang="zh">
<head>
  <meta charset="UTF-8">
  <title>渲染列表</title>
  <script src="script/react.development.js"></script>
  <script src="script/react-dom.development.js"></script>
  <!-- 引入babel -->
  <script src="script/babel.min.js"></script>
</head>
<body>
<div id="root"></div>

<script type="text/babel">

  const name = '吴邪';
  const lang = 'cn';

  /*
  *   {} 只能用来放js表达式,而不能放语句(if for)
  *       在语句中是可以去操作JSX
  * */
  // const div = <div>Hello {name}</div>;

  let div;

  if(lang === 'en'){
    div = <div>hello {name}</div>;
  }else if(lang === 'cn'){
    div = <div>你好 {name}</div>;
  }

  const data = ['吴邪', '解雨臣', '张起灵'];

  /*
      <ul>
           <li>孙悟空</li>
           <li>猪八戒</li>
          ...
      </ul>

      [<li>孙悟空</li>, <li>猪八戒</li>, <li>沙和尚</li>]
  * */

  // const arr = [];

  // 遍历data  使用for循环
  // for(let i=0; i<data.length; i++){
  //     arr.push(<li>{data[i]}</li>);
  // }

  // const arr = data.map(item => <li>{item}</li>);


  // 将arr渲染为一个列表在网页中显示
  // jsx中会自动将数组中的元素在页面中显示
  // const list = <ul>{arr}</ul>;

  const list = <ul>{data.map(item => <li>{item}</li>)}</ul>;

  const root = ReactDOM.createRoot(document.getElementById('root'));
  root.render(list);

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

3.虚拟DOM

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title>虚拟DOM</title>
    <script src="script/react.development.js"></script>
    <script src="script/react-dom.development.js"></script>
    <script src="script/babel.min.js"></script>
</head>
<body>
<button id="btn">点我一下</button>
<hr>
<div id="root"></div>

<script type="text/babel">

    //创建一个数据
    const data = ['孙悟空', '猪八戒', '沙和尚'];

    // 创建列表
    const list = <ul>
        {/*data.map(item => <li key={item}>{item}</li>)*/}
        {data.map((item, index) => <li key={index}>{item}</li>)}
    </ul>;

    // 获取根元素
    const root = ReactDOM.createRoot(document.getElementById('root'));
    // 渲染元素
    root.render(list);

    /*
    *   在React我们操作的元素被称为React元素,并不是真正的原生DOM元素,
    *       React通过虚拟DOM 将React元素 和 原生DOM,进行映射,虽然操作的React元素,但是这些操作最终都会在真实DOM中体现出来
    *       虚拟DOM的好处:
    *           1.降低API复杂度
    *           2.解决兼容问题
    *           3.提升性能(减少DOM的不必要操作)
    *
    *   每当我们调用root.render()时,页面就会发生重新渲染
    *       React会通过diffing算法,将新的元素和旧的元素进行比较
    *       通过比较找到发生变化的元素,并且只对变化的元素进行修改,没有发生的变化不予处理
    * */
    document.getElementById('btn').onclick = function (){
        // 重新渲染页面
        //创建一个数据
        const data = ['唐僧', '孙悟空', '猪八戒', '沙和尚'];
        // 创建列表
        const list = <ul>
            {/*data.map(item => <li key={item}>{item}</li>)*/}
            {data.map((item, index) => <li key={index}>{item}</li>)}
        </ul>;
        // 渲染元素
        root.render(list);

        /*
        *   旧数据
        *       ul
        *           li>孙悟空
        *           li>猪八戒
        *           li>沙和尚
        *  新数据
        *       ul
        *           li>孙悟空
        *           li>猪八戒
        *           li>沙和尚
        *   比较两次数据时,React会先比较父元素,父元素如果不同,直接所有元素全部替换
        *       父元素一致,在去逐个比较子元素,直到找到所有发生变化的元素为止
        *   上例中,新旧两组数据完全一致,所以没有任何DOM对象被修改
        *
        *
        * 旧数据
        *       ul
        *           li>孙悟空
        *           li>猪八戒
        *           li>沙和尚
        *  新数据
        *       ul
        *           li>tom
        *           li>猪八戒
        *           li>沙和尚
        *
        *  上例中,只有第一个li发生变化,所以只有第一个li被修改,其他元素不变
        *
        *  当我们在JSX中显示数组中,数组中每一个元素都需要设置一个唯一key,否则控制台会显示红色警告
        *       重新渲染页面时,React会按照顺序依次比较对应的元素,当渲染一个列表时如果不指定key,同样也会按照顺序进行比较,
        *       如果列表的顺序永远不会发生变化,这么做当然没有问题,但是如果列表的顺序会发生变化,这可能会导致性能问题出现
        *
        *
        *   旧数据
        *       ul
        *           li>孙悟空
        *           li>猪八戒
        *           li>沙和尚
        *   新数据
        *       ul
        *           li>孙悟空
        *           li>猪八戒
        *           li>沙和尚
        *           li>唐僧
        *
        *   上例中,在列表的最后添加了一个新元素,并没有改变其他的元素的顺序,所以这种操作不会带来性能问题
        *
        *
        *
        *   旧数据
        *       ul
        *           li>孙悟空
        *           li>猪八戒
        *           li>沙和尚
        *   新数据
        *       ul
        *           li>唐僧
        *           li>孙悟空
        *           li>猪八戒
        *           li>沙和尚
        *
        *   上例中,在列表的最前边插入了一个新元素,其他元素内容并没有发生变化,
        *       但是由于新元素插入到了开始位置,其余元素的位置全都发生变化,而React默认是根据位置比较元素
        *       所以 此时,所有元素都会被修改
        *
        *   为了解决这个问题,React为列表设计了一个key属性,
        *       key的作用相当于ID,只是无法在页面中查看,当设置key以后,再比较元素时,
        *       就会比较相同key的元素,而不是按照顺序进行比较
        *   在渲染一个列表时,通常会给列表项设置一个唯一的key来避免上述问题
        *       (这个key在当前列表中唯一即可)
        *       注意:
        *           1.开发中一般会采用数据的id作为key
        *           2.尽量不要使用元素的index作为key
        *               索引会跟着元素顺序的改变而改变,所以使用索引做key跟没有key是一样的
        *                   唯一的不同就是,控制台的警告没了
        *               当元素的顺序不会发生变化时,用索引做key也没有什么问题
        *   旧数据
        *       ul
        *           li(key=孙悟空)>孙悟空
        *           li(key=猪八戒)>猪八戒
        *           li(key=沙和尚)>沙和尚
        *   新数据
        *       ul
        *           li(key=唐僧)>唐僧
        *           li(key=孙悟空)>孙悟空
        *           li(key=猪八戒)>猪八戒
        *           li(key=沙和尚)>沙和尚
        * */
    };



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

第四章:React包管理器

1.react项目的创建

react项目的创建使用:

	步骤:
	-创建根目录:
			-public
					-index.html
			-src
				-App.js
				-index.js
				
	创建完成文件后在 文件目录 进入终端输入:npm init -y

然后在终端安装依赖:输入命令:
		React-project>npm install react react-dom react-scripts -S
依赖下载完成后 开始配置:



index.js文件示例:

/*src下的index.js是js的入口文件
*
* */

//引入ReactDOM
import ReactDOM from 'react-dom/client';

//创建一个jsx

const App=<div>
    <h1>
计算机科学与技术学院欢迎你!  你好!
        <p>计算机科学与技术学院</p>

    </h1>
</div>;

    //2.获取根容器
const root=ReactDOM.createRoot(document.getElementById('root'));

//将App渲染到根容器
root.render(App);




index.html代码示例:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>react项目</title>
</head>
<body>

<div id="root"></div>
<!--
public下的index.html是首页的模板 webpack在编译文件时 会以index.html为模板生成index.html


-->

</body>
</html>

package.json代码示例:
	{
  "name": "03_React_Project",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "dependencies": {
    "react": "^18.0.0",
    "react-dom": "^18.0.0",
    "react-scripts": "^5.0.1"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}




				

2.习题代码

创建public 和src文件
		public下的index代码示例:
		<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>练习</title>
    <style>



    </style>
</head>
<body>


<div id="root">

<!--  创建一个根标签-->


</div>
</body>
</html>

src文件夹下:
		index.css样式:
			
				
/*设置body的样式*/
body{
    background-color: darkorange;

}

/*设置基本样式*/
*{
    box-sizing: border-box;
    margin: 0;

}
/*设置最外面容器的样式*/

.logs{
    /*设置宽度*/
    width: 800px;
    /*设置居中*/
    margin: 50px auto;

    /*设置背景颜色*/
    background-color: #0A246A;

border-radius: 10px; /*设置边框圆角*/

    box-shadow: 0 0  10px rgba(0,0,0,.3);
}

/*设置item的样式
*/

.item{

/*    开启弹性盒子*/
    display: flex;

    /*设置了背景颜色*/
    background-color: #00C1B3;

/*    */
    border-radius: 10px; /*设置边框圆角*/

    box-shadow: 0 0  10px rgba(0,0,0,.2);

margin: 16px 0;

    padding: 6px;
}


/*设置日期*/

.date{
    width: 90px;
    background-color: #F4F9FF;
    border-radius: 10px;
    font-weight: bold;
    /*设置为居中*/
    text-align: center;


    overflow: hidden;
}


/*设置月份的效果*/
.month{

    height: 25px;
    background-color: #D62828;
    line-height: 30px;
color: #F4F9FF;
    font-size: 20px;

}


/*设置日期的效果*/
.day{
    height: 60px;
    line-height: 60px;
    font-size: 50px;


}


/*设置日志的样式*/

.content{
/*    设置弹性盒子*/
    flex: auto;

    text-align: center;

}
/*设置描述内容*/

.desc{

    font-size: 16px;
    color: #194B49;


}

/*设置学习时间*/

.time{

    color: #D91600;

}



index.js代码:
	
		
//创建完成后 通过终端开始初始化:终端输入:npm init y 开始初始化(注意一定要联网)
//安转依赖:npm install react react-dom react-scripts -S

import ReactDOM from 'react-dom/client';

//引入样式表
import './index.css'

//创建一个react元素
const App=<div className="logs">
    {/*日志项容器*/}
    {/*hello  计算机科学与技术学院*/}
    {/*容器的容器*/}
    <div className="item">
   <div className="date">
       <div className="month">
           四月
       </div>

       <div className="day">
           18

       </div>
   </div>

    {/*    日志内容的容器*/}
    <div className="content">

        <h2 className="desc">学习React</h2>
        <div className="item">40分钟</div>
    </div>

    </div>


    <div className="item">
        <div className="date">
            <div className="month">
                四月
            </div>

            <div className="day">
                19

            </div>
        </div>

        {/*    日志内容的容器*/}
        <div className="content">

            <h2 className="desc">学习数据结构与算法</h2>
            <div className="item">40分钟</div>
        </div>

    </div>

    <div className="item">
        <div className="date">
            <div className="month">
                四月
            </div>

            <div className="day">
                20

            </div>
        </div>

        {/*    日志内容的容器*/}
        <div className="content">

            <h2 className="desc">学习计算机组成原理</h2>
            <div className="item">40分钟</div>
        </div>

    </div>


    <div className="item">
        <div className="date">
            <div className="month">
                四月
            </div>

            <div className="day">
                21

            </div>
        </div>

        {/*    日志内容的容器*/}
        <div className="content">

            <h2 className="desc">学习JavaSpring</h2>
            <div className="item">40分钟</div>
        </div>

    </div>

</div>

//获取元素
const root=ReactDOM.createRoot(document.getElementById('root'));

//渲染元素
root.render(App)


第五章:react组件

1.函数组件


1.在react中网页被拆分为了一个个组件 组件具有独立页面


创建src和public文件夹:
public文件夹:index.html代码示例:
	<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>练习</title>
    <style>



    </style>
</head>
<body>


<div id="root">

<!--  创建一个根标签-->


</div>
</body>
</html>

src文件下:
App代码示例;

/**/

const App=()=>{

    return <div>我是学生</div>

}



//导出
export default App;

indexdaima1:示例:
/*
* 在react中组件有两种创建方式:
*           函数式组件:函数组件就是一个返回jsx的普通函数
*                                       --注意:组件的首字母必须大写
*           类组件:
*
*
* */

import ReactDOm from "react-dom/client"
/*
function App() {

    return <div>计算机科学与技术学院欢迎您!</div>

}
*/


import App from "./App"
const root=ReactDOM.createRoot(document.getElementById('root'))


//react组件可以直接通过jsx去渲染
root.render(<App/>)





2.类组件

创建public和src文件夹
public文件夹书写index.html
	<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>类组件</title>
</head>
<body>
<!--创建一个根元素-->
<div id="root"></div>

</body>
</html>

src文件夹:
App.js
	import Logs from "./Components/Logs/Logs";

const App = () => {
  return <div>
    <Logs/>
  </div>
};

// 导出App
export default App;


index.js文件:
import ReactDOM from "react-dom/client";
import App from "./App";
import './index.css';

const root = ReactDOM.createRoot(document.getElementById('root'));

// React组件可以直接通过JSX渲染
root.render(<App/>);

src下的Component:

		LogItem.css文件:
			/*设置item的样式*/
.item{
    /*开启弹性盒*/
    display: flex;
    margin: 16px 0;
    padding: 6px;
    background-color: #FCBF49;
    border-radius: 10px;
    box-shadow: 0 0 10px rgba(0,0,0,.2);
}
/*设置日志内容的样式*/
.content{
    flex: auto;
    text-align: center;
    font-weight: bold;
}

/*设置描述内容*/
.desc{
    font-size: 16px;
    color: #194B49;
}

/*设置学习时间*/
.time{
    color: #D62828;
}



LogItem.js文件:
import React from 'react';
import MyDate from "./MyDate/MyDate";
import './LogItem.css'

const LogItem = () => {
    return (
        <div className="item">
            <MyDate/>
            {/* 日志内容的容器 */}
            <div className="content">
                <h2 className="desc">学习React</h2>
                <div className="time">40分钟</div>
            </div>
        </div>
    );
};

export default LogItem;



Component下的Log下的LogItem下的Mydate文件:

MyDate.css文件:
	/*设置日期的样式*/
.date{
    width: 90px;
    background-color: #fff;
    border-radius: 10px;
    font-weight: bold;
    text-align: center;
    overflow: hidden;
}

/*设置月份效果*/
.month{
    height: 30px;
    line-height: 30px;
    font-size: 20px;
    color: #fff;
    background-color: #D62828;
}

/*设置日期的效果*/
.day{
    height: 60px;
    line-height: 60px;
    font-size: 50px;
}

MyDate.js文件:
import React from 'react';
import './MyDate.css';

const MyDate = () => {
    return (
        <div className="date">
            <div className="month">
                四月
            </div>
            <div className="day">
                19
            </div>
        </div>
    );
};

export default MyDate;

3.事件

创建public文件夹和src文件夹
1.在public文件夹下创建index.html

                      <!DOCTYPE html>
            <html lang="en">
            <head>
                <meta charset="UTF-8">
                <title>Title</title>
            </head>
            <body>
            <div id="root">



            </div>
            </body>
            </html>  
2.在src文件夹下创建App.js

       const App = () => {

    const clickHandler = (event) => {
        event.preventDefault(); // 取消默认行为
        event.stopPropagation(); // 取消事件的冒泡

        alert('我是App中的clickHandler!');
        /*
        *     在React中,无法通过return false取消默认行为
        *     return false;
        *
        *     事件对象
        *         - React事件中同样会传递事件对象,可以在响应函数中定义参数来接收事件对象
        *         - React中的事件对象同样不是原生的事件对象,是经过React包装后的事件对象
        *         - 由于对象进行过包装,所以使用过程中我们无需再去考虑兼容性问题
        * */
    };


    return <div
        onClick={() => {
            alert('div');
        }}
        style={{width: 200, height: 200, margin: '100px auto', backgroundColor:'#bfa'}}>

        {/*
      在React中事件需要通过元素的属性来设置,
        和原生JS不同,在React中事件的属性需要使用驼峰命名法:
           onclick -> onClick
           onchange -> onChange
        属性值不能直接执行代码,而是需要一个回调函数:
          onclick="alert(123)"
          onClick={()=>{alert(123)}}
    */}
        <button onClick={() => {
            alert(123);
        }}>点我一下
        </button>
        <button onClick={clickHandler}>哈哈</button>
        <br/>
        <a href="https://www.baidu.com" onClick={clickHandler}>超链接</a>
    </div>
};

/*
*   <button onclick="alert(123)">点我一下</button>
*
*   <button id="btn01">点我一下</button>
*
*   document.getElementById('btn01').onclick = function(){};
*   document.getElementById('btn01').addEventListener('click', function(){}, false);
*
*
* */

// 导出App
export default App;
                                      

 3.在src文件夹下创建index.js文件
 	
 	import ReactDOM from "react-dom/client";
import App from "./App";
import './index.css';

const root = ReactDOM.createRoot(document.getElementById('root'));

// React组件可以直接通过JSX渲染
root.render(<App/>);

4.props

需要动态的实现数据的更新
1.创建public和src文件夹
 	在public文件夹下创建index.html
	在src文件夹下创建AApp.js  index.js
	
	src文件夹下创建Component文件夹
	  Component文件夹下创建 Logs文件夹 Logs文件夹下创建Logitem 
	  Logitem文件夹下创建 Mydate文件夹 在此文件夹下创建 Mydate.css MyDate.js
	  
	 在Logitem文件夹下创建Logitem.css Logitem.js
	 
	 在Logs文件夹下创建 Logs.css Logs.js
	 
	 
	 
	 index.js代码:
	 import ReactDOM from "react-dom/client";
import App from "./App";
import './index.css';

const root = ReactDOM.createRoot(document.getElementById('root'));

// React组件可以直接通过JSX渲染
root.render(<App/>);


index.css代码:
	/*设置基本样式*/
*{
    box-sizing: border-box;
}

/*设置body的样式*/
body{
    background-color: #DFDFDF;
    margin: 0;
}

App.js代码:
	import Logs from "./Components/Logs/Logs";

const App = () => {
    return <div>
        <Logs/>
    </div>
};

// 导出App
export default App;


Logs.js代码:
/* 日志的容器 */
import LogItem from "./LogItem/LogItem";
import './Logs.css';

const Logs = () => {


  const logData=[


    {
id:'001',
      date:new Date(2021,1,20,18,30),
      desc:'学习计算机网络',
      time:30
    },

    {
id:'002',
      date:new Date(2021,2,10,28,30),
      desc:'学习前端',
      time:30
    },




    {
id:'003',
      date:new Date(2021,3,10,8,30),
      desc:'学习javaWeb',
      time:30
    },

    {
id:'004',
      date:new Date(2021,9,1,8,30),
      desc:'学习javaWeb',
      time:80
    },



  ];



  //将数据放入jsx
  const LogItemDate=
      logData.map(item=><LogItem  key={item.id}   date={item.date}  desc={item.desc}  time={item.time}   />)

  return <div className="logs">

    {/*在父组件中可以直接在子组件中设置属性*/}
    {/*<LogItem test="456" hello="abc" fn={()=>{}} />*/}

    {
LogItemDate

    }

  </div>
};

export default Logs;



Log.css代码:
/*设置外层容器logs的样式*/
.logs{
    width: 800px;
    margin: 50px auto;
    padding: 20px;
    background-color: #EAE2B7;
    border-radius: 10px;
    box-shadow: 0 0 10px rgba(0,0,0,.2);
}


Logitem.css代码:
/*设置item的样式*/
.item{
    /*开启弹性盒*/
    display: flex;
    margin: 16px 0;
    padding: 6px;
    background-color: #FCBF49;
    border-radius: 10px;
    box-shadow: 0 0 10px rgba(0,0,0,.2);
}
/*设置日志内容的样式*/
.content{
    flex: auto;
    text-align: center;
    font-weight: bold;
}

/*设置描述内容*/
.desc{
    font-size: 16px;
    color: #194B49;
}

/*设置学习时间*/
.time{
    color: #D62828;
}


Logitem.js代码:
import React from 'react';
import MyDate from "./MyDate/MyDate";
import './LogItem.css'

const LogItem = (props) => {

    // 在函数组件中,属性就相当于是函数的参数,可以通过参数来访问
    // 可以在函数组件的形参中定义一个props,props指向的是一个对象
    // 它包含了父组件中传递的所有参数
    // console.log(props.date);


    /*
    * 注意:props是只读的不能修改
    *
    *
    *
    * */
    return (
        <div className="item">
            <MyDate date={props.date}/>
            {/* 日志内容的容器 */}
            <div className="content">
                {/*
                  如果将组件中的数据全部写死,将会导致组件无法动态设置,不具有使用价值
                    我们希望组件数据可以由外部设置,在组件间,父组件可以通过props(属性)向子组件传递数据
                */}
                <h2 className="desc">{props.desc}</h2>
                <div className="time">{props.time}分钟</div>
            </div>
        </div>
    );
};


export default LogItem;


Mydate.css代码:
/*设置日期的样式*/
.date{
    width: 90px;
    background-color: #fff;
    border-radius: 10px;
    font-weight: bold;
    text-align: center;
    overflow: hidden;
}

/*设置月份效果*/
.month{
    height: 30px;
    line-height: 30px;
    font-size: 20px;
    color: #fff;
    background-color: #D62828;
}

/*设置日期的效果*/
.day{
    height: 60px;
    line-height: 60px;
    font-size: 50px;
}


Mydate.js代码:
import React from 'react';
import './MyDate.css';

const MyDate = (props) => {
    // console.log(props.date.getDate());
    // 获取月份
    const month = props.date.toLocaleString('zh-CN', {month:'long'});
    // 获取日期
    const date = props.date.getDate();

    return (
        <div className="date">
            <div className="month">
                {month}
            </div>
            <div className="day">
                {date}
            </div>
        </div>
    );
};

export default MyDate;

	

第六章:State

import './app.css';
import {useState} from "react";

const App = () => {

    console.log('函数执行了 ---> 组件创建完毕!');

    /*
    * 在React中,当组件渲染完毕后,再修改组件中的变量,不会使组件重新渲染
    *   要使得组件可以收到变量的影响,必须在变量修改后对组件进行重新渲染
    *   这里我们就需要一个特殊变量,当这个变量被修改使,组件会自动重新渲染
    *
    * state相当于一个变量,
    *   只是这个变量在React中进行了注册,
    *   React会监控这个变量的变化,当state发生变化时,会自动触发组件的重新渲染
    *   使得我们的修改可以在页面中呈现出来
    *
    * 在函数组件中,我们需要通过钩子函数,获取state
    *
    * 使用钩子 useState() 来创建state
    *   import {useState} from "react";
    *
    * 它需要一个值作为参数,这个值就是state的初始值
    *   该函数会返回一个数组
    *     数组中第一个元素,是初始值
    *       - 初始值只用来显示数据,直接修改不会触发组件的重新渲染
    *     数组中的第二个元素,是一个函数,通常会命名为setXxx
    *       - 这个函数用来修改state,调用其修改state后会触发组件的重新渲染,
    *           并且使用函数中的值作为新的state值
    *
    *
    *
    *
    * */

    const [counter, setCounter] = useState(1);
    // let counter = result[0];
    // let setCounter = result[1];
    // const [counter, setCounter] = result;
    /*
    *   当点击+时,数字增大
    *   点击-时,数字减少
    * */

    // 创建一个变量存储数字
    // let counter = 2;

    const addHandler = () => {
        // 点击后数字+1
        // alert('+1');
        // counter++;
        setCounter(counter + 1); // 将counter值修改为2
    };

    const lessHandler = () => {
        // 点击后数字-1
        // alert('-1');
        // counter--;

        setCounter(counter-1);

    };

    return <div className={'app'}>
        <h1>{counter}</h1>
        <button onClick={lessHandler}>-</button>
        <button onClick={addHandler}>+</button>
    </div>;
};

// 导出App
export default App;
import './app.css';
import {useState} from "react";

const App = () => {

    console.log('函数执行了 ---> 组件创建完毕!');
    /*
    * State:
    *       --state实际上就是一个react管理的变量
    * 当我们通过setState()修改变量的值时 会触发组件的自动刷新
    *       只有state值方式变化时 才会发生渲染
    *
    *   当state的值是一个对象时 修改时是使用新的镀锡去替换已有的对象
    *   当通过setSate去修改state 并不会修改当前的state  它修改的是组件下一次渲染的state值
    *
    *   setState()会触发组件的重新渲染 它是异步的
    *       因此当我们调用setState()需要用到state的值时 用到要注意有可能出现计算错误的情况
    *       为了避免出现这种情况 我们通过setState()传递回调函数
    * 
    *
    * */
const [user,setUser]=useState({name:'吴邪',age:18})

    const [counter, setCounter] = useState(1);
    const addHandler = () => {
        setCounter(counter + 1); // 将counter值修改为2
        setCounter(2);
        setCounter(3);
        setCounter(4)

        
    //    setState()中回调函数的返回值会成为新的state的值  回调函数执行时  React会将最新的值作为参数传递
    };


    const lessHandler = () => {
        setCounter(counter-1);
    };
    const updateUser=()=>{

        // setUser({name:'张起灵',age:20});

        // user.name='张起灵';
        //     setUser(user)

    //    如果直接修改就的state对象 由于对象还是那个对象 使用不会发生改变
    //    因此要进行浅复制

        const newUser=Object.assign({},user);
        // console.log(newUser);
        // console.log(newUser===user)//判断是不是同一个对象


        newUser.name='张起灵'
        setUser(newUser)





    }

    return <div className={'app'}>
        <h1>{counter}--{user.name}--{user.age}</h1>
        <button onClick={lessHandler}>-</button>
        {/*<button onClick={addHandler}>+</button>*/}
        <button onClick={updateUser}>更新 </button>
    </div>;
};
// 导出App
export default App;

DOM对象与useRef

import './App.css';
import {useRef, useState} from "react";

let temp;

const App = () => {
    /*
    *   获取原生的DOM对象
    *       1.可以使用传统的document来对DOM进行操作
    *       2.直接从React处获取DOM对象
    *           步骤:
    *               1.创建一个存储DOM对象的容器
    *                   - 使用 useRef() 钩子函数
    *                       钩子函数的注意事项:
    *                           ① React中的钩子函数只能用于函数组件或自定义钩子
    *                           ② 钩子函数只能直接在函数组件中调用
    *               2.将容器设置为想要获取DOM对象元素的ref属性
    *                   <h1 ref={xxx}>....</h1>
    *                   - React会自动将当前元素的DOM对象,设置为容器current属性
    *
    *   useRef()
    *       - 返回的就是一个普通的JS对象
    *       - {current:undefined}
    *       - 所以我们直接创建一个js对象,也可以代替useRef()
    *       - 区别:
    *           我们创建的对象,组件每次重新渲染都会创建一个新对象
    *           useRef()创建的对象,可以确保每次渲染获取到的都是同一个对象
    *
    *       - 当你需要一个对象不会因为组件的重新渲染而改变时,就可以使用useRef()
    *
    * */

    const h1Ref = useRef(); // 创建一个容器
    const [count, setCount] = useState(1);
    // const h1Ref = {current:null};


    // console.log(temp === h1Ref);
    // temp = h1Ref;

    const clickHandler = () => {


        // 通过id获取h1
        const header = document.getElementById('header');
        // alert(header);
        // header.innerHTML = '哈哈';

        console.log(h1Ref);
        // alert(h1Ref.current === header);
        h1Ref.current.innerText = '嘻嘻!';
    };

    const countAddHandler = ()=>{
        setCount(prevState => prevState + 1);
    };

    return <div className={'app'}>
        <h1 id="header" ref={h1Ref}>我是标题{count}</h1>
        <button onClick={clickHandler}>1</button>
        <button onClick={countAddHandler}>2</button>
    </div>;
};

// 导出App
export default App;

类组件:

App.js代码:
import React from "react";

/*
*   Webstrom中的快捷方式:
*       rsc --> 函数组件(不带props)
*       rsi --> 函数组件(带props)
*       rcc --> 类组件
* */

import React, {Component} from 'react';
import './App.css';
import User from "./components/User";

class App extends Component {
    render() {
        return (
            <div className="app">
                <User name='猪八戒' age={28} gender={'男'}/>
            </div>
        );
    }
}

export default App;
User.js代码:
import React, {Component} from 'react';

class User extends Component {
    /*
    *   类组件的props是存储到类的实例对象中,
    *       可以直接通过实例对象访问
    *       this.props
    *   类组件中state统一存储到了实例对象的state属性中
    *       可以通过 this.state来访问
    *       通过this.setState()对其进行修改
    *           当我们通过this.setState()修改state时,
    *               React只会修改设置了的属性
    *
    *   函数组件中,响应函数直接以函数的形式定义在组件中,
    *       但是在类组件中,响应函数是以类的方法来定义,之前的属性都会保留
    *       但是这你仅限于直接存储于state中的属性
    *
    *   获取DOM对象
    *       1.创建一个属性,用来存储DOM对象
    *           divRef = React.createRef();
    *       2.将这个属性设置为指定元素的ref值
    * */

    // 创建属性存储DOM对象
    divRef = React.createRef();

    // 向state中添加属性
    state = {
        count: 0,
        test: '哈哈',
        obj: {name: '孙悟空', age: 18}
    };

    // 为了省事,在类组件中响应函数都应该以箭头函数的形式定义
    clickHandler = () => {
        // this.setState({count: 10});
        // this.setState(prevState => {
        //     return {
        //         count: prevState + 1
        //     }
        // });
        /*this.setState({
            obj:{...this.state.obj, name:'沙和尚'}
        });*/

        console.log(this.divRef);
    };


    render() {

        // console.log(this.props);
        // console.log(this.divRef);

        return (
            <div ref={this.divRef}>
                <h1>{this.state.count} --- {this.state.test}</h1>
                <h2>{this.state.obj.name} --- {this.state.obj.age}</h2>
                <button onClick={this.clickHandler}>点</button>
                <ul>
                    <li>姓名:{this.props.name}</li>
                    <li>年龄:{this.props.age}</li>
                    <li>性别:{this.props.gender}</li>
                </ul>
            </div>
        );
    }
}

export default User;

	

添加Card组件

import React, {useState} from 'react';
import Card from "../UI/Card/Card";
import './LogsForm.css';

const LogsForm = () => {

    /*
    *   当表单项发生变化时,获取用户输入的内容
    * */
    // 创建三个变量,用来存储表单中的数据
    // let inputDate = '';
    // let inputDesc = '';
    // let inputTime = 0;

    const [inputDate, setInputDate] = useState('');
    const [inputDesc, setInputDesc] = useState('');
    const [inputTime, setInputTime] = useState('');


    // 创建一个响应函数,监听日期的变化
    const dateChangeHandler = (e) => {
        // 获取到当前触发事件的对象
        // 事件对象中保存了当前事件触发时的所有信息
        // event.target 执行的是触发事件的对象(DOM对象)
        //console.log(e.target.value);
        setInputDate(e.target.value);
    };

    // 监听内容的变化
    const descChangeHandler = (e) => {
        // 获取到当前触发事件的对象
        // 事件对象中保存了当前事件触发时的所有信息
        // event.target 执行的是触发事件的对象(DOM对象)
        //console.log(e.target.value);
        setInputDesc(e.target.value);
    };

    //监听时长的变化
    const timeChangeHandler = (e) => {
        // 获取到当前触发事件的对象
        // 事件对象中保存了当前事件触发时的所有信息
        // event.target 执行的是触发事件的对象(DOM对象)
        //console.log(e.target.value);
        setInputTime(e.target.value);
    };

    // 当表单提交时,汇总表单中的数据
    /*
    *   在React中,通常表单不需要自行提交
    *       而是要通过React提交
    * */
    const formSubmitHandler = (e) => {
        // 取消表单的默认行为
        e.preventDefault();
        // 获取表单项中的数据日期、内容、时长
        // 将数据拼装为一个对象
        const newLog = {
            date: new Date(inputDate),
            desc: inputDesc,
            time: +inputTime
        };

        // 清空表单项
        setInputDate('');
        setInputDesc('');
        setInputTime('');

        console.log(newLog);

        /*
        *   提交表单后如何清空表单中的旧数据
        *       现在这种表单,在React我们称为非受控组件
        *
        *   我们可以将表单中的数据存储到state中,
        *       然后将state设置为表单项value值,
        *       这样当表单项发生变化,state会随之变化,
        *       反之,state发生变化,表单项也会跟着改变,这种操作我们就称为双向绑定
        *       这样一来,表单就成为了一个受控组件
        *
        *
        *
        * */

    };

    return (
        <Card className="logs-form">
            <form onSubmit={formSubmitHandler}>
                <div className="form-item">
                    <label htmlFor="date">日期</label>
                    <input onChange={dateChangeHandler} value={inputDate} id="date" type="date"/>
                </div>
                <div className="form-item">
                    <label htmlFor="desc">内容</label>
                    <input onChange={descChangeHandler} value={inputDesc} id="desc" type="text"/>
                </div>
                <div className="form-item">
                    <label htmlFor="time">时长</label>
                    <input onChange={timeChangeHandler} value={inputTime} id="time" type="number"/>
                </div>
                <div className="form-btn">
                    <button>添加</button>
                </div>
            </form>
        </Card>
    );
};

export default LogsForm;




import React, {useState} from 'react';
import Card from "../UI/Card/Card";
import './LogsForm.css';

const LogsForm = () => {
    
    //将表单数据统一到一个state中

    const [formData,setFormData]=useState({
        inputDate:'',
        inputDesc:'',
        inputTime:''


    })



    // 创建一个响应函数,监听日期的变化
    const dateChangeHandler = (e) => {
       

    setFormData({
      ...formData,
        inputDate:e.target.value


    })






    };

    // 监听内容的变化
    const descChangeHandler = (e) => {
        
        setFormData({
            ...formData,
            inputDesc:e.target.value


        })

    };

    //监听时长的变化
    const timeChangeHandler = (e) => {
       
        setFormData({
            ...formData,
            inputTime:e.target.value


        })

    };

    // 当表单提交时,汇总表单中的数据
    /*
    *   在React中,通常表单不需要自行提交
    *       而是要通过React提交
    * */
    const formSubmitHandler = (e) => {
        // 取消表单的默认行为
        e.preventDefault();
        // 获取表单项中的数据日期、内容、时长
        // 将数据拼装为一个对象
        const newLog = {
            date: new Date(formData.inputDate),
            desc: formData.inputDesc,
            time: +formData.inputTime
        };

        // 清空表单项
        setFormData({


            inputDate:'',
            inputDesc:'',
            inputTime:''

        })
        console.log(newLog);
    };

    return (
        <Card className="logs-form">
            <form onSubmit={formSubmitHandler}>
                <div className="form-item">
                    <label htmlFor="date">日期</label>
                    <input onChange={dateChangeHandler} value={formData.inputDate} id="date" type="date"/>
                </div>
                <div className="form-item">
                    <label htmlFor="desc">内容</label>
                    <input onChange={descChangeHandler} value={formData.inputDesc} id="desc" type="text"/>
                </div>
                <div className="form-item">
                    <label htmlFor="time">时长</label>
                    <input onChange={timeChangeHandler} value={formData.inputTime} id="time" type="number"/>
                </div>
                <div className="form-btn">
                    <button>添加</button>
                </div>
            </form>
        </Card>
    );
};

export default LogsForm;


添加数据

import Logs from "./Components/Logs/Logs";
import LogsForm from "./Components/LogsForm/LogsForm";
import './App.css';
import {useState} from "react";


const App = () => {
  //数据:
 const [logsData,setLogsData]=useState(

    [
        {
            id: '001',
            date: new Date(2021, 1, 20, 18, 30),
            desc: '学习九阳神功',
            time: 30
        },
        {
            id: '002',
            date: new Date(2022, 2, 10, 12, 30),
            desc: '学习降龙十八掌',
            time: 20
        },
        {
            id: '003',
            date: new Date(2022, 2, 11, 11, 30),
            desc: '学习JavaScript',
            time: 40
        },
        {
            id: '004',
            date: new Date(2022, 2, 15, 10, 30),
            desc: '学习React',
            time: 80
        }
    ]



 )


  /*下一步:将logsForm中的数据传递给App组件 然后将App组件 将新的日志添加到数组中
  *
  *
  *
  * */

  //定义一个函数saveLogHandler
  const saveLogHandler=(newLog)=>{


  //  向新的日志里面添加id

    newLog.id=Date.now()+'';
    console.log("App.js",newLog);

  //将新的数据他就爱到数组中
  //   logsData.push(newLog);
    setLogsData([...logsData,newLog])

  };


  return <div className="app">
    {/*引入LogsFrom*/}
    <LogsForm onSaveLogs={saveLogHandler}/>
    <Logs logsData={logsData}/>
  </div>;
};

// 导出App
export default App;



/* 日志的容器 */
import LogItem from "./LogItem/LogItem";
import Card from "../UI/Card/Card";
import './Logs.css';

const Logs = (props) => {



    /*logData是一个用来存储学习的日志
    *       这个数据除了当前组件Logs需要使用外 LogsForm也需要使用
    *           当遇到这种一个数据需要被多个组件使用时  我们可以将数据放入这些组件共同祖先元素中
    *           这样子就可以使得多个组件都能方便的访问这个数据
    *           state提升
    * */


    // 模拟一组从服务器中加载的数据

    // 将数据放入JSX中
    const logItemDate = props.logsData.map(item => <LogItem key={item.id} date={item.date} desc={item.desc}
                                                      time={item.time}/>);

    return <Card className="logs">
        {
            logItemDate
            // logsData.map(item => <LogItem {...item}/> )
        }
    </Card>
};

export default Logs;



import React, {useState} from 'react';
import Card from "../UI/Card/Card";
import './LogsForm.css';

const LogsForm = (props) => {

    //将表单数据统一到一个state中
    console.log(props)
    const [formData,setFormData]=useState({
        inputDate:'',
        inputDesc:'',
        inputTime:''


    })



    // 创建一个响应函数,监听日期的变化
    const dateChangeHandler = (e) => {


    setFormData({
      ...formData,
        inputDate:e.target.value


    })






    };

    // 监听内容的变化
    const descChangeHandler = (e) => {

        setFormData({
            ...formData,
            inputDesc:e.target.value


        })

    };

    //监听时长的变化
    const timeChangeHandler = (e) => {

        setFormData({
            ...formData,
            inputTime:e.target.value


        })

    };

    // 当表单提交时,汇总表单中的数据
    /*
    *   在React中,通常表单不需要自行提交
    *       而是要通过React提交
    * */
    const formSubmitHandler = (e) => {
        // 取消表单的默认行为
        e.preventDefault();
        // 获取表单项中的数据日期、内容、时长
        // 将数据拼装为一个对象
        const newLog = {
            date: new Date(formData.inputDate),
            desc: formData.inputDesc,
            time: +formData.inputTime
        };

        //当要添加新的日志时 调用父组件传递过来的函数
        props.onSaveLogs(newLog)

        // 清空表单项
        setFormData({


            inputDate:'',
            inputDesc:'',
            inputTime:''

        })
        console.log(newLog);
    };

    return (
        <Card className="logs-form">
            <form onSubmit={formSubmitHandler}>
                <div className="form-item">
                    <label htmlFor="date">日期</label>
                    <input onChange={dateChangeHandler} value={formData.inputDate} id="date" type="date"/>
                </div>
                <div className="form-item">
                    <label htmlFor="desc">内容</label>
                    <input onChange={descChangeHandler} value={formData.inputDesc} id="desc" type="text"/>
                </div>
                <div className="form-item">
                    <label htmlFor="time">时长</label>
                    <input onChange={timeChangeHandler} value={formData.inputTime} id="time" type="number"/>
                </div>
                <div className="form-btn">
                    <button>添加</button>
                </div>
            </form>
        </Card>
    );
};

export default LogsForm;

日志删除

import React from 'react';
import MyDate from "./MyDate/MyDate";
import './LogItem.css'
import Card from "../../UI/Card/Card";

const LogItem = (props) => {

    /*
    *   props是只读的不能修改
    * */
    // props.desc = '嘻嘻'; // 不能修改props中的属性
    // console.log(props.desc);

    //书写一个删除事件

    const deleteItemHandler=()=>{

const isDel=window.confirm('确定要删除吗?')

        if (isDel){
            alert('删除!')
            props.onDelLog();

        }else {
            
        }
        
    //    删除当前的item 要删除item 其实就是要从数据的state移除指定的数据


    }



    return (
        <Card className="item">
            <MyDate date={props.date}/>
            {/* 日志内容的容器 */}
            <div className="content">
                {/*
                  如果将组件中的数据全部写死,将会导致组件无法动态设置,不具有使用价值
                    我们希望组件数据可以由外部设置,在组件间,父组件可以通过props(属性)向子组件传递数据
                */}
                <h2 className="desc">{props.desc}</h2>
                <div className="time">{props.time}分钟</div>
            </div>


        {/*    添加一个删除的按钮*/}

            <div >

                <div onClick={deleteItemHandler} className='delete'>删除</div>

            </div>

        </Card>
    );
};


export default LogItem;


/* 日志的容器 */
import LogItem from "./LogItem/LogItem";
import Card from "../UI/Card/Card";
import './Logs.css';

const Logs = (props) => {



    /*logData是一个用来存储学习的日志
    *       这个数据除了当前组件Logs需要使用外 LogsForm也需要使用
    *           当遇到这种一个数据需要被多个组件使用时  我们可以将数据放入这些组件共同祖先元素中
    *           这样子就可以使得多个组件都能方便的访问这个数据
    *           state提升
    * */


    // 模拟一组从服务器中加载的数据
    

    // 将数据放入JSX中
    const logItemDate = props.logsData.map((item ,index)=> <LogItem
                                                                      onDelLog={()=>props.onDelLog(index)}
                                                                      key={item.id} date={item.date}
                                                                      desc={item.desc}
                                                                         time={item.time}/>);

    return <Card className="logs">
        {
            logItemDate
            // logsData.map(item => <LogItem {...item}/> )
        }
    </Card>
};

export default Logs;


import Logs from "./Components/Logs/Logs";
import LogsForm from "./Components/LogsForm/LogsForm";
import './App.css';
import {useState} from "react";


const App = () => {
  //数据:
 const [logsData,setLogsData]=useState(

    [
        {
            id: '001',
            date: new Date(2021, 1, 20, 18, 30),
            desc: '学习九阳神功',
            time: 30
        },
        {
            id: '002',
            date: new Date(2022, 2, 10, 12, 30),
            desc: '学习降龙十八掌',
            time: 20
        },
        {
            id: '003',
            date: new Date(2022, 2, 11, 11, 30),
            desc: '学习JavaScript',
            time: 40
        },
        {
            id: '004',
            date: new Date(2022, 2, 15, 10, 30),
            desc: '学习React',
            time: 80
        }
    ]

 )


  //  定义一个函数 从数据中删除一条日志
    const  dellogByIndex=(index)=>{

     // setLogsData(prevState=>[...prevState.slice(index,1)])

        setLogsData(prevState=>{

           const newLogs=[...prevState]
            newLogs.splice(index,1)
           return newLogs;
        });


    };

 
  /*下一步:将logsForm中的数据传递给App组件 然后将App组件 将新的日志添加到数组中
  *
  *
  *
  * */

  //定义一个函数saveLogHandler
  const saveLogHandler=(newLog)=>{


  //  向新的日志里面添加id

    newLog.id=Date.now()+'';
    console.log("App.js",newLog);

  //将新的数据他就爱到数组中
  //   logsData.push(newLog);
    setLogsData([...logsData,newLog])

  };


  return <div className="app">
    {/*引入LogsFrom*/}
    <LogsForm onSaveLogs={saveLogHandler}/>
    <Logs   onDelLog={dellogByIndex}  logsData={logsData}/>
  </div>;
};

// 导出App
export default App;

空列表提示

/* 日志的容器 */
import LogItem from "./LogItem/LogItem";
import Card from "../UI/Card/Card";
import './Logs.css';

const Logs = (props) => {



    /*logData是一个用来存储学习的日志
    *       这个数据除了当前组件Logs需要使用外 LogsForm也需要使用
    *           当遇到这种一个数据需要被多个组件使用时  我们可以将数据放入这些组件共同祖先元素中
    *           这样子就可以使得多个组件都能方便的访问这个数据
    *           state提升
    * */


    // 模拟一组从服务器中加载的数据


    // 将数据放入JSX中
    const logItemDate = props.logsData.map((item ,index)=> <LogItem
                                                                      onDelLog={()=>props.onDelLog(index)}
                                                                      key={item.id} date={item.date}
                                                                      desc={item.desc}
                                                                         time={item.time}/>);
    console.log(logItemDate);

    return <Card className="logs">
        {
            logItemDate.length!==0 ? logItemDate:<p className="no-logs">没有数据了!</p>

            // logItemDate
            // logsData.map(item => <LogItem {...item}/> )
        }
    </Card>
};

export default Logs;

使用Portal修改项目

import React, {useState} from 'react';
import MyDate from "./MyDate/MyDate";
import './LogItem.css'
import Card from "../../UI/Card/Card";
import ConfirmModal from "../../UI/ConfirmModal/ConfirmModal";

const LogItem = (props) => {

    // 添加一个state,记录是否显示确认窗口
    const [showConfirm, setShowConfirm] = useState(false);

    // 删除item的响应函数
    const deleteItemHandler = () => {
        // 显示确认窗口
        setShowConfirm(true);
    };

    //取消函数
    const cancelHandler = () => {
        setShowConfirm(false);
    };

    // 确认函数
    const okHandler = () => {
        props.onDelLog();
    };

    /*
    *   portal
    *       - 组件默认会作为父组件的后代渲染到页面中
    *           但是有些情况下,这种方式会带来一些问题
    *       - 通过portal可以将组件渲染到页面中的指定位置
    *       - 使用方法:
    *           1.在index.html添加一个新的元素
    *           2.修改组件的渲染方式
    *               - 通过ReactDOM.createPortal()作为返回值创建元素
    *               - 参数:
    *                   1. jsx(修改前return后的代码)
    *                   2. 目标位置(DOM元素)
    *
    * */

    return (
        <Card className="item">

            {showConfirm && <ConfirmModal
                confirmText="该操作不可恢复!请确认"
                onCancel={cancelHandler}
                onOk={okHandler}
            />}

            <MyDate date={props.date}/>
            {/* 日志内容的容器 */}
            <div className="content">
                {/*
                  如果将组件中的数据全部写死,将会导致组件无法动态设置,不具有使用价值
                    我们希望组件数据可以由外部设置,在组件间,父组件可以通过props(属性)向子组件传递数据
                */}
                <h2 className="desc">{props.desc}</h2>
                <div className="time">{props.time}分钟</div>
            </div>

        {/*    添加一个删除按钮*/}
             <div>
                 <div onClick={deleteItemHandler} className='delete'>×</div>
             </div>
        </Card>
    );
};


export default LogItem;



import React from 'react';
import './Backdrop.css';
import ReactDOM from "react-dom";

// 获取backdrop的根元素
const backdropRoot = document.getElementById('backdrop-root');

const Backdrop = (props) => {
    return ReactDOM.createPortal(<div className="backdrop">
        {props.children}
    </div>, backdropRoot);
};

export default Backdrop;


<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title>学习日志</title>
</head>
<body>
<div id="root"></div>

<!--这个容器专门用来渲染遮罩层-->
<div id="backdrop-root"></div>

</body>
</html>

过滤日志功能


/* 日志的容器 */
import LogItem from "./LogItem/LogItem";
import Card from "../UI/Card/Card";
import './Logs.css';
import LogFilter from "./LogFilter/LogFilter";
import {useState} from "react";

const Logs = (props) => {

    // 创建一个存储年份的state
    const [year, setYear] = useState(2022);

    // 过滤数据,只显示某一年的数据
    let filterData = props.logsData.filter(item => item.date.getFullYear() === year);

    // 创建一个修改年份的函数
    const changeYearHandler = (year) => {
        setYear(year);
    };


    // 将数据放入JSX中
    let logItemData = filterData.map(
        (item, index) => <LogItem
            onDelLog={() => props.onDelLog(item.id)}
            key={item.id}
            date={item.date}
            desc={item.desc}
            time={item.time}/>
    );

    if (logItemData.length === 0) {
        logItemData = <p className="no-logs">没要找到日志!</p>;
    }

    return <Card className="logs">
        {/*引入年份的选择组件*/}
        <LogFilter
            year={year}
            onYearChange={changeYearHandler}
        />
        {logItemData}
    </Card>
};

export default Logs;



第七章:React项目自动创建

1.Create React App的使用

终端输入:npx create-react-app react-app(项目名称)
完成之后自动创建 我们就不需要手动创建那些文件

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(

  //  这个标签与之前的不一样 这个是严格模式
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

// 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();


import logo from './logo.svg';
import './App.css';

function App() {
  return (

    <div className="App">

      <h1>计算机科学与技术学院</h1>

      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    </div>
  );
}

export default App;




2.react中的CSS

import logo from './logo.svg';
import './App.css';

function App() {

  //       方式一: 在这里书写一个CSS样式
    const pStyle={
        color:'red',
        backgroundColor:'#bfa',
        border:"blue solid 1px"
    }

  return (

    <div className="App">

      <h1 style={pStyle}>计算机科学与技术学院</h1>

      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    </div>
  );
}

export default App;





/*方式二:采用外部样式  这种方式只需要在使用的时引入就可以
这种全局样式:会导致全局覆盖 样式冲突  它不像函数那种有作用域  因此这种不适应大范围使用

*/

.App {
  text-align: center;
}

.App-logo {
  height: 40vmin;
  pointer-events: none;
}

@media (prefers-reduced-motion: no-preference) {
  .App-logo {
    animation: App-logo-spin infinite 20s linear;
  }
}

.App-header {
  background-color: #282c34;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-size: calc(10px + 2vmin);
  color: white;
}

.App-link {
  color: #61dafb;
}

@keyframes App-logo-spin {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

3.CSS模块

创建一个App.module.css样式模块文件


import React from 'react';
import ReactDOM from 'react-dom/client';
import App from "./App";

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App/>
  </React.StrictMode>
);


import React, {useState} from 'react';
import classes from './App.module.css';
import A from "./A";

const App = () => {

    /*
    *   CSS模块
    *       使用步骤:
    *           1.创建一个xxx.module.css
    *           2.在组件中引入css
    *               import classes from './App.module.css';
    *           3.通过classes来设置类
    *               className={classes.p1}
    *       CSS模块可以动态的设置唯一的class值
    *           App_p1__9v2n6
    * */

    const [showBorder, setShowBorder] = useState(false);

    const clickHandler = () => {
        setShowBorder(true);
    };


    return (
        <div>
            <A/>
            <p className={`${classes.p1} ${showBorder ? classes.Border : ''}`}>我是一个段落</p>
            <button onClick={clickHandler}>点我一下</button>
        </div>
    );
};

export default App;

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值