React入门笔记(二)

React入门笔记(二)

      • 1.前情回顾
      • 2.组件
      • 3.视频教程地址

1.前情回顾

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

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;

组件的使用如下:

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

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

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

具体组件的编写以及效果展示如下:
React入门笔记(二)_第2张图片
说明如下几点:
(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;

原因这里的props为只读函数:
React入门笔记(二)_第3张图片
    那肯定不能缺少组件的基本功能,于是就有了state,这个和Vue里面有些相似,能够实现对于组件修改和重新渲染,使用方法如下:
React入门笔记(二)_第4张图片
可以发现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);//被调用后能够触发页面渲染
  }

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

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

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

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

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

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

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

 function testFun() {
    setTimeout(() => {
      console.log('函数被触发');
      setCount((preCounter) => preCounter + 100);
    }, 1000);
  }

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

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

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

3.视频教程地址

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

你可能感兴趣的:(#,React,react.js,javascript,ecmascript)