likes
comments
collection
share

好了,俺要开始学习React了 (基础一)

作者站长头像
站长
· 阅读数 4

写前话痨

为啥俺开始学习React了,讲白了,就是为了适应这个 卷的时代 ,既然俺不能也不可能让这个时代去适应俺,那俺就能 热脸贴着冷时代(冷屁股) 了,还是嘎嘎会贴的那种😁

个人理解

俺之前的主打一直是vue,项目也都是围绕vue写的,其他的框架学了也用不上,纯属浪费时间。但现在很多招聘信息上都加上 Vue + React ,不学不行呀,像我这种菜鸟程序员,只会 Ctrl+C+V 的那种,不多学点技术绑在身上,出去就是饿死🙄

接触了一段时间的React开发后,从刚开始的不太适应(毕竟用Vue有小几年了),到慢慢觉得,你还别说,还真滴挺香的😍,有点爱不释手了,真的。但要想取代俺的主打框架Vue,还有待进一步验证。

好了,俺要开始学习React了 (基础一)

学前准备

  • 开发工具: VSCode ;
  • 开发环境: 俺的node版本是16.20.0的,个人建议版本 >=16.x 就行了 Node.js (nodejs.org) ;
  • 俺叫它代码提交工具: git Git (git-scm.com) ;
  • VSCode插件拓展:

Simple React Snippets,可通过缩写快速生成React相关的代码片段

好了,俺要开始学习React了 (基础一)

Auto Import - ES6, TS, JSX, TSX,可自动引入React相关依赖

好了,俺要开始学习React了 (基础一)

Prettier - Code formatter,可自动格式化代码

好了,俺要开始学习React了 (基础一)

Error Lens,错误行代码高亮提示错误

好了,俺要开始学习React了 (基础一)

CodeGeeX: AI Code AutoComplete, Chat, Auto Comment,代码智能提示工具

好了,俺要开始学习React了 (基础一)

Auto Close Tag,自动关闭标签,在开始标记的结束括号中键入后,将自动插入结束标记

好了,俺要开始学习React了 (基础一)

ESLint,自动检测不符合规范的代码

好了,俺要开始学习React了 (基础一)

JavaScript (ES6) code snippets,ES6 语法中的 JavaScript 代码片段提示

好了,俺要开始学习React了 (基础一)

Night Owl,设置编辑器颜色主题

好了,俺要开始学习React了 (基础一)

  • node版本管理工具: 两种工具都可,看个人意愿吧,俺用的是 nvm

开始入坑

网上有很多对React的介绍,俺这就不多说了。这里俺一个不吱声,直接给俺看官网: React ; 直到看到它想吐,那就牛逼了😎

create-react-app 脚手架

  1. 采用脚手架创建项目:react-basics
$ npx create-react-app react-basics

将代码和文件该删的删,该留的留: 如下结构

好了,俺要开始学习React了 (基础一)

App.js为根组件,并在index.js文件中渲染

React三大API

index.js 中就可以看到两个:

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

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

ReactDOM.createRoot(): 获取根元素,根元素就是React元素要插入的位置

root.render(): 用来将React元素渲染到根元素中,根元素中所有的内容都会被删除,被React元素所替换,当重复调用render()时,React会将两次的渲染结果进行比较,他会确保只修改那些发生变化的元素,对DOM做最少的修改。

除了这两个还有一个:- React.createElement(): 用来出创建一个React元素:

参数:

  1. 元素名(组件名、html标签必须小写)

  2. 元素的属性

  3. 在设置事件时

    • 事件名使用驼峰命名法,后面跟事件函数
    • 在设置class时,class使用className
  4. 元素的子元素(内容)

注意点:React元素最终会通过虚拟DOM转化为真实的DOM元素;React元素一旦创建无法修改,只能通过新创建的元素进行替换,例如:

// 创建一个div
const div = React.createElement("div", {}, "Hello World", button);

JSX注意事项

  1. JSX不是字符串,不要加引号

  2. JSX中html标签应该小写,React组件应该大写开头

  3. JSX中有且只有一个根标签

  4. JSX的标签必须正确结束(自结束标签必须写/)

  5. 在JSX中可以使用{}嵌入表达式

    有值的语句的就是表达式

  6. 如果表达式是空值、布尔值、undefined,将不会显示

  7. 在JSX中,属性可以直接在标签中设置

    注意:

    class需要使用className代替

    style中必须使用对象设置style={{background:'red'}}

  8. 渲染列表:{} 只能用来放js表达式,而不能放语句(if for),但在语句中是可以去操作JSX

虚拟DOM

  • 在React我们操作的元素被称为React元素,并不是真正的原生DOM元素,React通过虚拟DOM 将React元素 和 原生DOM,进行映射,虽然操作的React元素,但是这些操作最终都会在真实DOM中体现出来。

  • 虚拟DOM的好处:

    1. 降低API复杂度
    2. 解决兼容问题
    3. 提升性能(减少DOM的不必要操作)
  • 每当我们调用root.render()时,页面就会发生重新渲染,React会通过diffing算法,将新的元素和旧的元素进行比较,通过比较找到发生变化的元素,并且只对变化的元素进行修改,没有发生的变化不予处理

例子:将 const data = ['孙悟空', '猪八戒', '沙和尚']; 用列表展示:

function App() {
  const data = ["孙悟空", "猪八戒", "沙和尚"];
  return (
    <div className="App">
      hello world!
      <ul>
        {data.map((item, index) => (
          <li key={index}>{item}</li>
        ))}
      </ul>
    </div>
  );
}

export default App;

当重新再用当前一样的数据再渲染一次,比较两次数据时,React会先比较父元素,父元素如果不同,直接所有元素全部替换,父元素一致,在去逐个比较子元素,直到找到所有发生变化的元素为止,上例中,新旧两组数据完全一致,所以没有任何DOM对象被修改。

当我们在JSX中显示数组时,数组中每一个元素都需要设置一个唯一key,否则控制台会显示红色警告。

当在列表的最后添加了一个新元素时:

function App() {
  const data = ["孙悟空", "猪八戒", "沙和尚"];
  const addDataHandler = () => {
    data.push("唐三藏");
    console.log(data);
  };
  return (
    <div className="App">
      hello world!
      <ul>
        {data.map((item, index) => (
          <li key={index}>{item}</li>
        ))}
      </ul>
      <button onClick={addDataHandler}>添加</button>
    </div>
  );
}

export default App;

运行项目,打开控制台:

好了,俺要开始学习React了 (基础一)

state 数据发生变化时自动重新渲染

你看,俺也向末尾添加数据了呀,为啥没有显示到页面去呢,你重写刷新页面也不行,data 是个死数据。这时我们就需要 state 数据发生变化时自动重新渲染:

  • 在React中,当组件渲染完毕后,再修改组件中的变量,不会使组件重新渲染

  • 要使得组件可以收到变量的影响,必须在变量修改后对组件进行重新渲染

  • 这里我们就需要一个特殊变量,当这个变量被修改时,组件会自动重新渲染

  • state相当于一个变量:

    • state只属于当前组件,其他组件无法访问。并且state是可变的,当其发生变化后组件会自动> 重新渲染,以使变化在页面中呈现
    • 只是这个变量在React中进行了注册,React会监控这个变量的变化,当state发生变化时,会自动触发组件的重新渲染,使得我们的修改可以在页面中呈现出来
  • 在函数组件中,我们需要通过钩子函数,获取state,使用钩子 useState() 来创建state,import {useState} from "react";

  • 它需要一个值作为参数,这个值就是state的初始值,该函数会返回一个数组,数组中第一个元素,是初始值,初始值只用来显示数据,直接修改不会触发组件的重新渲染,数组中的第二个元素,是一个函数,通常会命名为setXxx

  • 这个函数用来修改state,调用其修改state后会触发组件的重新渲染,并且使用函数中的值作为新的state值

state 的问题

  • state实际就是一个被React管理的变量,当我们通过setState()修改变量的值时,会触发组件的自动重新渲染
  • 只有state值发生变化时,组件才会重新渲染
  • 当state的值是一个对象时,修改时是使用新的对象去替换已有对象
  • 当通过setState去修改一个state时,并不表示修改当前的state,它修改的是组件下一次渲染时state值
  • setState()会触发组件的重新渲染,它是异步的,所以当调用setState()需要用旧state的值时,一定要注意,有可能出现计算错误的情况,为了避免这种情况,可以通过为setState()传递回调函数的形式来修改

来尝尝鲜:

import { useState } from "react";

function App() {
  const [data, setData] = useState(["孙悟空", "猪八戒", "沙和尚"]);
  const addDataHandler = () => {
    // prevData为旧的data
    setData((prevData) => {
      return [...prevData, "唐僧"];
    });
  };
  console.log(data);
  return (
    <div className="App">
      hello world!
      <ul>
        {data.map((item, index) => (
          <li key={index}>{item}</li>
        ))}
      </ul>
      <button onClick={addDataHandler}>添加</button>
    </div>
  );
}

export default App;

好了,俺要开始学习React了 (基础一)

你会看到,在列表的最后添加了一个新元素,并没有改变其他的元素的顺序,在控制台中你会看到只是最后追加了新的 li ,所以这种操作不会带来性能问题。

但当在列表的最前边插入了一个新元素,你在看看:就是将 [...prevData, "唐僧"] 调换下位置

setData((prevData) => {
      return ["唐僧", ...prevData];
    });

好了,俺要开始学习React了 (基础一)

你看看,在列表的最前边插入了一个新元素,其他元素内容并没有发生变化,但是由于新元素插入到了开始位置,其余元素的位置全都发生变化,而React默认是根据位置比较元素,所以 此时,所有元素都会被修改,为了解决这个问题,React为列表设计了一个key属性,key的作用相当于ID,只是无法在页面中查看,当设置key以后,再比较元素时,就会比较相同key的元素,而不是按照顺序进行比较,在渲染一个列表时,通常会给列表项设置一个唯一的key来避免上述问题(这个key在当前列表中唯一即可)。

注意:

  1. 开发中一般会采用数据的id作为key
  2. 尽量不要使用元素的index作为key
  3. 索引会跟着元素顺序的改变而改变,所以使用索引做key跟没有key是一样的
  4. 唯一的不同就是,控制台的警告没了
  5. 当元素的顺序不会发生变化时,用索引做key也没有什么问题

下集精彩

屁股坐疼了,需要出门透透气,理解一下好不啦😁。下篇俺会讲到哪些知识点呢: 可能这篇已经涉及到一些下一篇的知识点了,没事,不用知道那些是为啥,只要懂得这篇的知识点就很棒了

  • React如何组件化:在代码示例中你可能也会看到组件化的大致结构长啥样了;
  • React如何定义事件:在代码示例中的button就添加了一个点击事件;
  • React如何组件通信;
  • React如何如何获取真实的DOM;

好了,俺要开始学习React了 (基础一)