Java程序员快速上手 React (第一期)

打开终端,运行命令。使用脚手架创建项目,项目名 my-react-app。

npx create-react-app my-react-app --template typescript

进入项目目录,启动项目,在浏览器查看效果

cd my-react-app
npm start

image-20240410144904942

项目核心结构

my-react-app/
├── node_modules/            # 项目依赖的npm包
├── public/                  # 静态资源文件夹
├── src/                     # 源代码文件夹
│   ├── App.tsx              # 组件
│   └── index.tsx            # 项目入口文件
├── package.json             # 项目依赖和配置文件

浏览器插件:https://legacy.reactjs.org/blog/2015/09/02/new-react-developer-tools.html#installation

可以安装通义灵码,或者 Fitten Code 等AI插件,辅助编程

index.tsx:项目的入口文件。以下代码解释由 Fitten Code 插件生成

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

// ReactDOM.createRoot()方法创建一个新的 ReactDOM 根节点,并将其渲染到指定的 DOM 元素上。
// 该方法返回一个 ReactDOM.Root 实例,该实例可以用来渲染 React 组件。
const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);

// ReactDOM.render()方法渲染一个 React 组件到指定的 DOM 元素上。
// <React.StrictMode>组件可以帮助你在开发过程中发现潜在的错误,并提供有用的警告信息。(严格模式)
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();  // 性能监控

打开 App.js,删除原有的代码,写一个 Hello World,运行。( index.tsx 中已经使用了该组件)

function App() {
  return (
    <h1>Hello World!</h1>
  );
}
export default App;

注意点 1

关于return 后面的小括号,虽然renturn 返回单行代码可以不加括号,但建议无论返回单行还是多行,都加上括号

return (

)

注意点 2

JSX 只能返回一个根元素。所以需要返回多个根元素的时候,可以在外面加一层空标签 <></>,空标签起到占位作用,不会生成最终的html标签

function App() {
  return (
      <>
        <div>
      		{/*代码*/}
        </div>
        <div>
      		{/*代码*/}
        </div>
      </>
    );
}

基本概念

什么是组件?

Commponent,用于构建 HTML 页面,有 state (状态) 和 props (属性)。state 是组件自身管理的数据,props 是父组件传递给子组件的数据,通常不可改变。

React 组件有两种创建方式

  • 函数式组件(这里就是函数式组件,官方主推)

  • 类组件(复杂冗余)

JSX语法(JavaScript XML)

jsx 语法就是 js 和 html 代码混合着写。这种写法更加直观和易于理解,但它实际上是JavaScript表达式,最终会被转换成相应的JavaScript对象。

什么是虚拟DOM?

虚拟DOM是React为了提高性能而采用的一种技术。它是实际DOM的轻量级副本,React首先在虚拟DOM上进行变更,然后通过diff算法计算出最小的变更操作,最后将这些变更应用到实际的DOM上。这种方法避免了直接操作DOM的高成本,从而提高了应用的性能。

ReactDOM是React与真实DOM交互的库。虚拟DOM负责提高性能,而ReactDOM负责将React元素映射到真实DOM上。

插值

可以在标签的属性或者内容上使用插值。{值}

function App() {

  const divContent = "标签内容";
  const divTitle = "标签标题";

  return (
      <div title={divTitle}>{divContent}</div>
    );
}

export default App;

事件操作

需求:点击按钮,控制台打印"点击了按钮"

function App() {

  //点击按钮,触发 handleClick 函数,e是事件对象,包含事件信息
  function handleClick (e){
    console.log("点击了按钮",e);
  }
  return (
      <button onClick={handleClick}>按钮</button>
  );
}
export default App;
  • 事件以小驼峰命名(例如 onClick 而不是 onclick)

列表渲染

需求:使用map函数遍历数组并创建元素列表,展示数据

import React, {Fragment} from 'react';

function App() {
    // 假设这是从服务器获取的用户数据
    const users = [
        { name: 'Alice', email: 'alice@example.com' },
        { name: 'Bob', email: 'bob@example.com' },
        { name: 'Charlie', email: 'charlie@example.com' },
    ];

    // 映射出用户列表
    const userList = users.map((user, index) => (
        <Fragment key={index}>
            <li>{user.name} - {user.email}</li>
            <li>-------------</li>
        </Fragment>
    ));

    return (
        <div>
            <h1>User List</h1>
            <ul>
                {userList}
            </ul>
        </div>
    );
}

export default App;
  • 在循环中,需要给每个元素都设置唯一的 key 属性,当列表数据更新时,React会使用 key 来确定哪些元素发生了变化,从而高效地更新DOM,而不是重新渲染整个列表。
  • 在循环中,如果需要保持多个根元素的时候,在外层使用标签。这里不使用空标签是因为它无法添加key,在循环中不好用

条件渲染

可以使用 js 表达式,根据条件渲染不同的元素。

import React, {Fragment} from 'react';

function App() {
    // 假设这是从服务器获取的用户数据
    const users = [
        { name: 'Alice', email: 'alice@example.com' },
        { name: 'Bob', email: 'bob@example.com' },
        { name: 'Charlie', email: 'charlie@example.com' },
    ];

    // 映射出用户列表
    const userList = users.map((user, index) => (
        <Fragment key={index}>
            <li>{user.name} - {user.email}</li>
            <li>-------------</li>
        </Fragment>
    ));

    // 如果没有用户,渲染一条消息
    if (userList.length === 0){
        return (
            <p>No users available.</p>
        );
    }

    // 否则,渲染用户列表
    return (
        <div>
            <h1>User List</h1>
            <ul>
                {userList}
            </ul>
        </div>
    );
}

export default App;

Axios

官方文档:https://www.axios-http.cn/docs/intro

  • 安装 Axios

    npm install axios
    
  • 启动一个准备好的后端,用来返回数据

  • 使用 Axios 向后端发送请求。可以复制 Axios 官方文档的基本用例:https://www.axios-http.cn/docs/example

import axios from "axios";
import {BaseResponse} from "./model/BaseResponse";
import {UserVO} from "./model/UserVO";

export default function App({ id }: { id: bigint }) {
	
    // 声明一个变量 user,用于存储请求成功后返回的UserVO对象
    let user: UserVO | null = null;
    
    // 定义函数,作用是发送请求。支持async/await用法
    async function getUserVOById() {
        try {
            //BaseResponse就是response.data的类型
            const response = await axios.get<BaseResponse<UserVO>>(`http://localhost:8101/api/user/get/vo?id=${id}`);
            console.log(response.data.data);
            user = response.data.data;
        } catch (error) {
            console.error(error);
        }
    }

    // 调用函数
    getUserVOById();

    return (
        <h1>{user.userName}</h1>
    );
}
export interface BaseResponse<T>{
    code: string,
    data: T,
    message?: string
}
export interface UserVO {
  id: number,
  userName: string,
  userAvatar: string,
  userProfile: string,
  userRole: string,
  createTime: string
}

在 index.tsx 中使用组件,传入id。注意:可以删除 index.tsx 中的 React.StrictMode 标签。因为严格模式会发两次请求,控制台会打印两次

root.render(
    //处理大整数值的精度丢失问题,使用 js 的内置 BigInt 对象,数字末尾加n
    <App id={1772186353223131138n}/>
);

可以在控制台查看响应体结构。文档:https://www.axios-http.cn/docs/res_schema

控制台成功打印,但页面上没有内容

分析执行流程

首次调用函数组件,函数从上到下依次执行,最后返回的 jsx 代码会被渲染成【虚拟 dom 节点】,根据【虚拟 dom 节点】会生成【真实 dom 节点】,由浏览器显示出来。

当函数组件的 props 或 state 发生变化时,才会重新调用函数组件,返回的 jsx与上次的【虚拟 dom 节点】对比,如果没变化,复用上次的节点;有变化,创建新的【虚拟 dom 节点】替换掉上次的节点(这是 react 自身的性能优化,react 是靠不断调用函数来更新页面的)

尝试用浏览器插件控制 props 发生改变,函数确实被重新调用了,但调用又让函数重置到了初始状态。响应的数据永远无法保存(函数无状态)

useState

useState 是React中的一个钩子(Hook),它允许你在函数组件中添加状态。就是把数据保存在了函数之外,随着函数的调用不断更新

  • 参数:数据初始状态

  • 返回值:[当前状态,更新函数]

调用这个更新函数,它能修改数据,并能触发组件函数重新调用,让React重新渲染组件

import axios from "axios";
import {BaseResponse} from "./model/BaseResponse";
import {UserVO} from "./model/UserVO";
import {useState} from "react";

export default function App({ id }: { id: bigint }) {

    // 定义useState,用于存储请求到的用户信息
    const [user, setUser] = useState({userName: ""});
    // 声明一个变量 fetch,用于判断是否已经发送请求
    const [fetch, setFetch] = useState(false);

    // 定义函数,作用是发送请求。支持async/await用法
    async function getUserVOById() {
        try {
            //BaseResponse就是response.data的类型
            const response = await axios.get<BaseResponse<UserVO>>(`http://localhost:8101/api/user/get/vo?id=${id}`);
            console.log(response.data.data);
            setUser(response.data.data);
        } catch (error) {
            console.error(error);
        }
    }

    // 判断是否已经发送请求,如果没有发送请求,则发送请求
    if (!fetch) {
        setFetch(true);
        getUserVOById();
    }

    return (
        <h1>{user.userName}</h1>
    );
}
  • 因为更新函数 setUser() 的执行会触发组件函数重新调用,导致 getUserVOById() 再次执行,这样会循环不断发请求。所以定义 fecth 来控制只发一次请求。下面使用 useEffect 会解决这个问题

  • 对象结构不能在视图中直接呈现,会报错。所以这里不是{user},是{user.userName}

  • 更新函数是直接使用新值完全替换掉旧值,而不是在旧值上做局部修改。

useEffect

Effect 称之为副作用(没有贬义),函数组件的主要目的,是为了渲染生成 html 元素,除了这个主要功能以外的功能,都可以称之为副作用。 useXXX 打头的一系列方法,都是为副作用而生的,在 react 中把它们称为 Hooks

  • 参数1:箭头函数, 在真正渲染 html 之前会执行它

  • 参数2:

    • 情况1:没有,代表每次调用组件函数时,都会执行箭头函数
    • 情况2:[],代表无论组件函数被调用几次,箭头函数只会执行一次
    • 情况3:[依赖性],依赖项变化时,箭头函数会执行
import axios from "axios";
import {BaseResponse} from "./model/BaseResponse";
import {UserVO} from "./model/UserVO";
import {useEffect, useState} from "react";

export default function App({ id }: { id: bigint }) {

    // 定义useState,用于存储请求到的用户信息
    const [user, setUser] = useState({userName: ""});

    // useEffect,用于发起请求
    useEffect(() => {
        async function getUserVOById() {
            try {
                //BaseResponse就是response.data的类型
                const response = await axios.get<BaseResponse<UserVO>>(`http://localhost:8101/api/user/get/vo?id=${id}`);
                console.log(response.data.data);
                setUser(response.data.data);
            } catch (error) {
                console.error(error);
            }
        }
        getUserVOById();
    },[id]);    // 只在 id 发生变化时发起请求

    return (
        <h1>{user.userName}</h1>
    );
}
  • 15
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值