• React入门笔记(二)


    React入门笔记(二)

    1.前情回顾

        书接上回,React入门笔记(一)——主要介绍React的基本特点,虚拟dom的实现原理,利用包管理工具搭建基本的React单页面引用等。如果你跳过了前面的项目配置也没关系,这里再来贴一张前面的代码结构(下面的介绍中会使用到):
    在这里插入图片描述

    2.组件

        相信在前面的学习中,你可以深入的体会到React与Vue在使用上的相同之处吧。对我们来说确实是一件好事。前面提到React同样支持组件化开发,组件化开发能够很好的提高代码的复用性,管理起来也更加方便。在React中组件分为函数型组件和类组件(其实就是写法有些差异,本质相同)。
    (1)函数型组件,举例如下:

    //组件编写(本质上使用的是JSX语法糖)
    function clickFunction() {
      alert('这简直一模一样!');
    }
    const App = () => (
      <div
        id='myApp'
        className='zhangsan'
        style={{
          color: 'red',
          backgroundColor: 'pink',
          height: '300px',
          width: '400px',
          display: 'flex',
          justifyItems: 'center',
          alignItems: 'center',
        }}
        onClick={clickFunction}
      >
        <div style={{ width: '100%', textAlign: 'center' }}>这是一个React项目!</div>
      </div>
    );
    export default App;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    组件的使用如下:

    //src/index.js入口文件
    import reactDom from 'react-dom/client';
    import App from './App';
    const root = reactDom.createRoot(document.getElementById('root'));
    root.render(<App />);
    
    • 1
    • 2
    • 3
    • 4
    • 5

    (2)类组件,使用如下:

    import React from 'react';
    //注意需要继承React.Component并实现里面的render方法
    class ClassApp extends React.Component {
      clickFun() {
        alert('这个是类组件');
      }
      render() {
        return (
          <div
            id='myApp'
            className='lisi'
            style={{
              color: 'blue',
              backgroundColor: 'pink',
              height: '300px',
              width: '400px',
              display: 'flex',
              justifyItems: 'center',
              alignItems: 'center',
            }}
            onClick={this.clickFun}
          >
            <div style={{ width: '100%', textAlign: 'center' }}>我是一个类组件</div>
          </div>
        );
      }
    }
    export default ClassApp;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

        Java看到了露出了诡异的笑容,由于我这里使用了click函数,这里报了个警告:[Violation] 'click' handler took 1081ms,本质是因为点击时间触发了js事件阻塞,被开发环境的依赖截获到了。
        当然组件光有这些显然不是很够,和Vue一样组件肯定得有传参和父子组件传值,不然这些基本功能都无法实现,组件使用就显得很尴尬。下面还是以函数组件为主来介绍propsstate,毕竟在JavaScript里面函数写法还是更常见一些。给出使用举例:

    //src/index.js入口文件
    import reactDom from 'react-dom/client';
    import Card from './Components/Card/Card.js';
    const root = reactDom.createRoot(document.getElementById('root'));
    const cardList = [
      {
        img: 'https://create.nightowl.top/static/tabbar/index-active.png',
        desc: '这里是首页描述内容',
      },
      {
        img: 'https://nightowl.top/static/images/icons/room.png',
        desc: '这里是虚拟试衣间描述',
      },
      {
        img: 'https://nightowl.top/static/images/icons/communicate.png',
        desc: '这里是消息功能描述内容',
      },
    ];
    root.render(
      cardList.map((item) => <Card desc={item.desc} imgSrc={item.img} />)
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    具体组件的编写以及效果展示如下:
    在这里插入图片描述
    说明如下几点:
    (1)对于函数组件,props将作为参数传入,获取的就是使用组件实例的时候在组件上面挂载的属性,注意props属性都是可读属性,都不够中途修改;
    (2)组件如果是一个序列的话,需要添加key值,用作组件变化的标识(性能优化的一种手段,方便检测组件是否需要重新渲染),建议使用后端传过来的唯一值,如果单纯使用索引的话,可能会由于之后组件顺序改变,可能需要重新渲染大量的组件,顺序改变相当于大量的属性的key都发生了变化;
    (3)在组件的属性中可以和Vue一样在{}直接写函数代码,例如: desc={item.desc.toUpperCase()}
    (4)往组件上挂载函数,错误写法如下:

    import './Card.css';
    const Card = (props) => {
      function testFun() {
        props.desc = String(props.desc).toLowerCase();
        console.log('这里是测试函数,测试将描述转回小写!', props.desc);
      }
      return (
        <div className='card-box' onClick={testFun}>
          <div className='card-img'>
            <img src={props.imgSrc} alt='来源——易搭衣橱' />
          </div>
          <div className='card-content'>{props.desc}</div>
        </div>
      );
    };
    export default Card;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    原因这里的props为只读函数:
    在这里插入图片描述
        那肯定不能缺少组件的基本功能,于是就有了state,这个和Vue里面有些相似,能够实现对于组件修改和重新渲染,使用方法如下:
    在这里插入图片描述
    可以发现state的使用首先需要从react里面解构出UseSate方法,然后利用UseState来创建一个State类型变量(本质是一个方法和数组),初始传入的参数为state初始值,后一个参数是重新修改state的方法:

      let state = useState(1);
      let count = state[0];//获取数组里面的数据
      let setCount = state[1];//获取state数组里面的操作函数
      function testFun() {
        console.log('点击事件被触发!');
        setCount(count + 100);//被调用后能够触发页面渲染
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    这个写法显然不是很优雅,可以使用如下写法:

      let [count, setCount] = useState(1);
      function testFun() {
        console.log('点击事件被触发!');
        setCount(count + 100);
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    接下来探讨一下State的使用注意点。如果是被操作的是一个对象,你可能想到的方法如下:
    在这里插入图片描述
    分析原因:发生了对象的直接替换,新的对象里面没有age属性,解决方法浅拷贝:
    在这里插入图片描述
    其他的浅复制就不再举例了,如果两次的对象完全一致(无论是深拷贝还是浅拷贝)那么将不会触发修改,也就是不会重新渲染UI。使用拷贝,代码看起来还是不是很舒服,于是有了如下的语法糖写法:

     setCount({ ...userInfo, username: '李四' });
    
    • 1

    下面需要明确一点,setState是异步的:

     let [count, setCount] = useState(1);
      function testFun() {
        setTimeout(() => {
          console.log('函数被触发');
          setCount(count + 100);
        }, 1000);
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述
        这里右边的实际函数进入了8次,但是只增加了2次,是因为State函数调用是异步的,导致一些函数的修改被react自动合并了,目的是提高性能。是不是有点像自动给你加了个防抖,但是有些时候可能你需要每次点击都完整执行,也就是不想“防抖”了,那么可以使用回调函数来解决这个问题:

     function testFun() {
        setTimeout(() => {
          console.log('函数被触发');
          setCount((preCounter) => preCounter + 100);
        }, 1000);
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

        说明:setCount()方法实际有两种参数,一种是直接传入数值,数值将会替代state的前一个值,但是存在更改合并机制提高性能;另一种是函数参数,在这个函数内部,函数自己的形参能够精准的获取到上一次渲染之后的值,如果想要更详细的理解State,可以查看这篇文章:react setState核心实现原理。里面提到,setState的一段时间合并是根据JS的事件队列实现的,一次事件循环调用进行一次setState的合并和组件渲染。事件循环其实不难理解,可以想想那个经典的打印输出面试题
        接下来聊聊ref(Vue中其实也有这个),ref能够获取实际的dom对象。有些时候可能不仅仅需要获取虚拟的dom,还需要直接获取实际(原生)的dom对象。获取实际dom对象需要使用useRef的钩子函数,useRef钩子函数只能够在函数组件或者自定义钩子函数里面写,创建之后会返回一个普通的js对象,使用举例如下:
    在这里插入图片描述
        ref对象同样能够修改dom对象,使用方法如下:

     function testFun() {
        console.log('输出ref对象:', testRef);
        testRef.current.innerHTML =
          '这是点击事件触发之后通过Ref对象修改之后的效果。';
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这里插入图片描述
        但是并不建议这么写,因为这样其实和react底层冲突了,相当于React想要去重新渲染,你却依然把被渲染的对象给找了回来。也可以看到在Vue里面还是很少直接操作真实dom的。
        如果你想说,你可能更喜欢Java那种面向对象的写法,当然也是可以的,虽然不能使用ref但是state是完全可以满足的,类组件的state使用如下:

    import './ClassCard.css';
    import React from 'react';
    class Card extends React.Component {
      state = {
        username: '张三',
        age: '18',
      };
      testFun = () => {
        console.log('点击事件被触发!');
        this.setState({ username: '凌空暗羽' });
      };
      render() {
        return (
          <div className='card-box' onClick={this.testFun}>
            <div className='card-img'>
              <img src={this.props.imgSrc} alt='来源——易搭衣橱' />
            </div>
            <div className='card-content'>
              {this.state.username}-{this.state.age}
            </div>
          </div>
        );
      }
    }
    export default Card;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    说明如下几点:
    (1)propsstate的使用和前面的函数组件非常类似,其实就是把这些propsstate全部挂载到了实例中了。state里面不需要使用useState方法构建,state已经相当于函数组件里面的继承成员了,直接用就完事,在修改的时候需要使用this.setState来修改,有微信小程序开发那味儿了;
    (2)setState函数在类组件中只会修改state中已经存在的属性,其他的属性依然自动保留,也就是不需要像函数组件那样还需要浅复制一下。当然还需要注意这个只支持state的一级保留,并不支持state里面的二级属性,所以二级及以下还是得使用语法糖浅拷贝;
    (3)注意setState前面的this需要注意,需要写成箭头函数的形式,这个其实就是this的作用域问题,属于基础基础知识,不太清楚的友友们可以去搜一搜。
        最后,函数组件和类组件各有优点,本质上实现的功能差不多,除了函数组件多支持了一个对性能不是很友好的ref对象外,函数组件代码更加简洁,但是结构显得没有类模式那么清晰,觉得那个适合自己用哪个就行。

    3.视频教程地址

        参考视频教程:尚硅谷李立超——React

  • 相关阅读:
    【InternLM实战营---第七节课笔记】
    GBASE 8A v953报错集锦28--使用企业管理器执行 select count(1) into @c from t1;报错
    会议剪影 | 思腾合力受邀出席第四届长三角文博会并作主题演讲
    【PAT甲级 - C++题解】1120 Friend Numbers
    【华为OD机试真题 python】 连续字母长度【2022 Q4 | 100分】
    【行为识别】差影法三维人体姿态行为识别【含Matlab源码 277期】
    40Java Runtime类
    简明 SQL 子查询指南:掌握 EXISTS 实现数据筛选
    早期Java Swing的eclipse项目导入idea使用
    并查集(路径压缩)
  • 原文地址:https://blog.csdn.net/weixin_46560512/article/details/125509277