Skip to content

React基础知识点

创建项目

bash
npx create-react-app 你的项目名称
  • 创建好之后启动
bash
npm run start
  • 删除多余的文件 安装normalize.css
bash
npm i normalize.css

目录结构

下面是一个推荐使用的 React 项目目录结构,基于 create-react-app 创建后进行调整,适用于中大型业务项目:

my-react-app/
├── public/                  # 静态资源(不会被 webpack 处理)
│   ├── favicon.ico          # 网站图标
│   ├── index.html           # HTML 模板
│   ├── logo192.png          # PWA 小图标
│   ├── logo512.png          # PWA 大图标
│   ├── manifest.json        # PWA 配置
│   └── robots.txt           # 爬虫协议
├── src/                     # 源代码目录
│   ├── api/                 # 接口请求层(与后端交互)
│   │   ├── index.js         # axios 实例封装(请求/响应拦截器)
│   │   ├── user.js          # 用户相关接口
│   │   └── product.js       # 商品相关接口
│   ├── assets/              # 静态资源(会被 webpack 处理)
│   │   ├── images/          # 图片资源
│   │   ├── styles/          # 全局样式
│   │   │   ├── index.css        # 全局样式入口
│   │   │   ├── normalize.css    # 样式重置(已安装)
│   │   │   └── variables.scss   # SCSS 变量
│   │   └── fonts/           # 字体文件
│   ├── components/          # 公共组件(无业务逻辑、可复用)
│   │   ├── Button/          # 单个组件独立目录
│   │   │   ├── index.jsx        # 组件实现
│   │   │   └── index.module.scss # 组件样式
│   │   ├── Modal/
│   │   │   ├── index.jsx
│   │   │   └── index.module.scss
│   │   └── NavBar/
│   │       ├── index.jsx
│   │       └── index.module.scss
│   ├── pages/               # 页面级组件(与路由一一对应)
│   │   ├── Home/            # 首页
│   │   │   ├── index.jsx        # 页面组件
│   │   │   └── index.module.scss # 页面样式
│   │   ├── Login/           # 登录页
│   │   │   ├── index.jsx
│   │   │   └── index.module.scss
│   │   └── Detail/          # 详情页
│   │       ├── index.jsx
│   │       └── index.module.scss
│   ├── router/              # 路由配置
│   │   └── index.jsx        # 路由表(使用 react-router-dom v6)
│   ├── store/               # 全局状态管理(Redux / Zustand / Mobx)
│   │   ├── index.js         # store 入口
│   │   ├── slices/          # Redux Toolkit 的 slice 模块
│   │   │   ├── userSlice.js
│   │   │   └── cartSlice.js
│   │   └── actions.js       # action 定义
│   ├── hooks/               # 自定义 Hook(封装可复用逻辑)
│   │   ├── useRequest.js    # 通用数据请求 hook
│   │   └── useAuth.js       # 登录态相关 hook
│   ├── utils/               # 工具函数(纯函数、无副作用)
│   │   ├── format.js        # 格式化工具(日期、价格等)
│   │   ├── storage.js       # 本地存储封装(localStorage)
│   │   └── request.js       # 请求工具封装
│   ├── constants/           # 常量定义
│   │   └── index.js         # 全局常量
│   ├── layouts/             # 布局组件
│   │   ├── DefaultLayout.jsx    # 默认布局(含头部、底部)
│   │   └── BlankLayout.jsx      # 空白布局(登录页等)
│   ├── config/              # 全局配置
│   │   └── index.js         # 环境变量、接口地址等
│   ├── App.jsx              # 应用根组件
│   ├── App.module.scss      # 根组件样式
│   ├── index.js             # 项目入口文件(ReactDOM.render / createRoot)
│   └── reportWebVitals.js   # 性能监控(create-react-app 自带)
├── .eslintrc.js             # ESLint 配置
├── .prettierrc              # Prettier 配置
├── .gitignore               # Git 忽略文件
├── package.json             # 项目依赖与脚本
├── README.md                # 项目说明
└── yarn.lock / package-lock.json # 包锁文件

目录说明

  • public/:放置不需要编译的静态资源,例如 HTML 模板、图标、manifest.json 等。文件夹内的文件会原样拷贝到构建产物根目录。
  • assets/:放置会被 webpack 处理(哈希、压缩等)的静态资源,例如通过 import 引入的图片、SCSS 等。
  • components/:通用组件目录,建议每个组件单独建文件夹,便于按需引入。
  • pages/:页面级组件,与路由表中的路由一一对应,承载业务页面逻辑。
  • router/:集中管理路由配置,配合 react-router-dom v6 使用。
  • store/:全局状态管理目录,推荐使用 Redux ToolkitZustand
  • hooks/:自定义 React Hook,封装通用逻辑(如请求封装、权限校验等)。
  • utils/:纯工具函数,与业务解耦,便于测试和复用。
  • layouts/:通用布局组件,避免在每个页面中重复编写头部、底部等结构。
  • config/:集中管理环境变量、接口域名、第三方 key 等配置项。

简化版(小型项目)

如果是 demo 或小项目,可以使用更轻量的目录结构:

my-react-app/
├── public/
│   ├── index.html
│   └── favicon.ico
└── src/
    ├── assets/
    │   ├── images/
    │   ├── styles/
    │   │   ├── index.css
    │   │   └── normalize.css
    │   └── fonts/
    ├── components/         # 公共组件
    ├── pages/              # 页面组件
    ├── App.jsx
    ├── App.css
    ├── index.js
    └── index.css

命名规范

  • 文件夹名称使用小写小驼峰,如 componentsmyComponents
  • 组件文件使用大驼峰(PascalCase) 命名,如 HomePage.jsxMyButton.jsx
  • 非组件文件(工具、配置)使用小驼峰命名,如 formatDate.jsrequest.js
  • 样式文件建议使用 CSS Modulesxxx.module.scss)避免样式全局污染。

index.js (React项目入口文件)

js
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import "normalize.css";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App />);

JSX

即 js 和 html 语法写在一起

基本语法

  • 函数组件return后面的小括号(),不换行可以不用写小括号

  • jsx 只能返回一个根元素,如果必须设计多元素,两个方法:

      1. 用Fragment包裹多个元素
      1. 用空标签包裹住(渲染的时候不产生多余的标签)
js
return (
  <>
    <div>1</div>
    <div>2</div>
  </>
);

数据插值

基础使用

  • 变量必须用大括号包裹,不能用引号包裹

  • 变量可以是js表达式,也可以是函数

  • 插值可以使用的位置: 1.标签位置 2.标签内容

ts

function App() {
  const divContent = '标签内容'
  const divTitle = '标签标题'
  return (
    <div title={divTitle}>{divContent}</div>
  )
}

但是你要是用引号包裹了变量,就会变成字符串,而不是变量

html
<div title="{divTitle}">{divContent}</div>

条件渲染

js
function App() {
  const divTitle = "标签标题";
  let divContent = null;
  const flag = false;
  if (flag) {
    divContent = (
      <div>
        <span>flag为true</span>
      </div>
    );
  } else {
    divContent = (
      <div>
        <p>flag为false</p>
      </div>
    );
  }
  return <div title={divTitle}>{divContent}</div>;
}
  • 注意这里不要加引号写成 divContent = '<span>flag为true</span>'

  • return里面除了三元表达式以外.其余都不行.比如if else,for,while,do while之类的循环语句

列表渲染

ts

import { Fragment } from 'react'

function App() {
  const list = [
    {id: 1,name:'小吴'},
    {id: 2,name:'小李'},
    {id: 3,name:'小花'},
  ]

  const listContent = list.map(item=>(
    <Fragment key={item.id}>
      <li>{item.name}</li>
      <li>---------</li>
    </Fragment>
  ))
  return (
    <ul>{listContent}</ul>
  )
}

由于只能有一个根目录,所以map下两个li元素可以写成如下形式

ts
const listContent = list.map(item=>(
    <>
      <li key={item.id}>{item.name}</li>
      <li>---------</li>
    </>
))

如果<li>---------</li>也需要遍历展示呢?

我们知道 空标签<></>是不会被渲染成结构的,因此key定义在空标签上是不会生效的

这时可使用 React提供的Fragment标签,同时要记得在前面import引用该标签

事件处理

  • jsx 属性大多使用驼峰命名
ts
function App() {
  function handleClick(e){
    console.log('点击按钮',e)
  }
  return (
    <button onClick={handleClick}>按钮</button>
  )
}

React组件基础使用

在React中,一个组件就是首字母大写的函数,内部存放了组件的逻辑和视图UI,渲染组件只需要把组件当成标签书写即可

useState 基本使用

useState是一个React Hook,它允许我们向组件添加一个状态变量

本质:和普通JS变量不同的是,状态变量一旦发生变化组件的视图UI也会跟着变化(数据驱动视图)

ts
const [count, setCount] = useState(0);
  • useState 是一个函数,返回值是一个数组

  • 数组中的第一个参数是状态变量,第二个参数是set函数用来修改状态变量

  • useState的参数可以作为count的初始值

  • 字符串,数字的更改

ts

import { useState } from "react";
// 项目的根组件
function App() {
  let [count, setCount] = useState(0)
  const handleMyButton = () => {
    setCount(()=>{
      return count + 1
    })
  }
  return (
    <div className="App">
      <button onClick={handleMyButton}>点击增加{count}</button>
    </div>
  );
}

export default App;
  • 对象状态的更改
ts
import { useState } from 'react'
function App() {
  const [data,setData] = useState({
    title:'默认标题',
    content:'默认内容'
  })

  function handleClick(){
    setData({
      ...data,
      title: '新标题'
    })
  }
  return (
    <>
      <div title={data.title}>{data.content}</div>
      <button onClick={handleClick}>按钮</button>
    </>
  )
}

对于对象的状态操作,其实和字符串差不多,唯一一个要注意的点是,在setData修改内容的时候,不能只写修改内容的操作,即

ts
function handleClick() {
  setData({
    title: "新标题",
  });
}

这样会造成其他属性内容的丢失,在修改属性操作前加上...data拓展操作,即把其他不变属性也添加进来,便不会造成内容丢失

  • 数组的状态更改
ts
function App() {

  const [data,setData] = useState([
    {id: 1,name:'小吴'},
    {id: 2,name:'小李'},
    {id: 3,name:'小花'},
  ])

  const listData = data.map(item=>(
    <li key={item.id}>{item.name}</li>
  ))

  function handleClick(){
    setData(data.filter(item=>item.id!==2))
  }
  return (
    <>
      <ul>{listData}</ul>
      <button onClick={handleClick}>按钮</button>
    </>
  )
}

className 类名

在React中 className 就是类名,而不是class属性

ts
import { useState } from "react";
import "./App.css";
import classNames from 'classnames';
// 项目的根组件
function App() {
  // 使用 const 定义,useState 返回值不需要重新赋值
  const [count, setCount] = useState(0);
  // 行内样式对象(不推荐大量使用,权重高不易覆盖)
  const style = {
    color: 'blue'
  };
  // 函数式更新:基于最新值计算,避免连续调用时丢更新
  const handleMyButton = () => {
    setCount(() => count + 1);
  };
  return (
    <div className="App">
      {/* 行内样式,不推荐 */}
      <span style={style}>测试一下</span>
      {/* 通过类名设置样式,className 写法等同于原生 class */}
      <button
        onClick={handleMyButton}
        // 静态类名直接写字符串
        className="my-btn"
        // 多类名拼接:普通写法 "my-btn is-active"
        // className="my-btn is-active"
      >
        点击增加{count}
      </button>
    </div>
  );
}

export default App;

组件通信和插槽

一些基础语法

  • 看起来像html 属性的功能,在react中称为为DOM组件设置Props

举例

jsx
import image from "XXX";
<img src={image} alt="" />;

对于src第一种方法是 import(如示例),第二种方法是使用在线地址

举例2 vue 中的class属性 对应react中className属性

jsx
<div className="App">
  <h1>Hello React</h1>
</div>

举例3

  • style 属性采用键值对写法
jsx

style={{
   width:100, // width: '100px'
   height:100
}}

如果要加单位,要添加引号

但是更推荐下面的写法

jsx
const imgStyle = {
  xxx样式内容,
};
style = { imgStyle };

举例4

在react使用css中带-的属性,例如background-color,一般要写成驼峰形式,如backgroundColor

jsx展开语法和props

jsx
import image from "./logo.svg";

function App() {
  const imgData = {
    className: "small",
    style: {
      width: 100,
      height: 100,
      backgroundColor: "grey",
    },
    src: image,
  };
  return (
    <div>
      <img alt="" {...imgData} />
    </div>
  );
}

注意

  1. 在一个标签里设置太多属性,书写繁琐

  2. react便提供了一种展开语法,将属性集中写在一个对象中,再应用在标签内(其中{...imgData}中的大括号是jsx单独的功能支持)

注意: jsx的展开操作不是es6中的展开运算符

  1. jsx的展开操作必须写在一个容器中,例如<img/>,但是es6的展开运算符是可以单独暴露出来的,如const a = {...b}

  2. 但是如果是要打印console.log()imgData中的内容,不能写成console.log(...imgData),要写成console.log({...imgData})

react自定义组件

tsx
function Article(props) {
  return (
    <div>
      <h2>{props.title}</h2>
      <p>{props.content}</p>
      <p>状态{active ? "显示中" : "已隐藏"}</p>
    </div>
  );
}

export default function App() {
  return (
    <>
      <Article title="标题1" content="内容1" />
      <Article title="标题2" content="内容2" />
      <Article title="标题3" content="内容3" />
    </>
  );
}

也可以不写成props点xxx的形式,需要使用到es6的解构

tsx
function Article({ title, content, active }) {
  return (
    <div>
      <h2>{title}</h2>
      <p>{content}</p>
      <p>状态{active ? "显示中" : "已隐藏"}</p>
    </div>
  );
}

react 组件使用props

父传子,传递的值不可修改

tsx
function Detail({ content, active }) {
  return (
    <>
      <p>{content}</p>
      <p>状态{active ? "显示中" : "已隐藏"}</p>
    </>
  );
}

function Article({ title, articleData }) {
  return (
    <div>
      <h2>{title}</h2>
      <Detail {...articleData} />
    </div>
  );
}

export default function App() {
  const articleData = {
    title: "标题1",
    detailData: {
      content: "内容1",
      active: true,
    },
  };
  return (
    <>
      <Article {...articleData} />
    </>
  );
}

React 插槽功能

  • 默认卡槽通过children传递
tsx
function List({ children }) {
  return <ul>{children}</ul>;
}

export default function App() {
  return (
    <>
      <List>
        <li>列表项1</li>
        <li>列表项1</li>
        <li>列表项1</li>
      </List>
      <List>
        <li>列表项2</li>
        <li>列表项2</li>
        <li>列表项2</li>
      </List>
    </>
  );
}
  • 具名卡槽
tsx
function List({ children, title, footer = <div>默认底部</div> }) {
  return (
    <>
      <h2>{title}</h2>
      <ul>{children}</ul>
      {footer}
    </>
  );
}

export default function App() {
  return (
    <>
      <List title="标题1" footer={<p>这是底部内容1</p>}>
        <li>列表项1</li>
        <li>列表项1</li>
        <li>列表项1</li>
      </List>
      <List title="标题2" footer={<p>这是底部内容1</p>}>
        <li>列表项A</li>
        <li>列表项B</li>
        <li>列表项C</li>
      </List>
      <List title="标题3">
        <li>列表项X</li>
        <li>列表项Y</li>
        <li>列表项Z</li>
      </List>
    </>
  );
}

子组件通过父组件方法修改状态

tsx
import { useState } from "react";
function Detail({ onActive }) {
  const [status, setStatus] = useState(false);
  function handleClick() {
    setStatus(!status);
    onActive(status);
  }
  return (
    <div>
      <button onClick={handleClick}>按钮</button>
      <p
        style={{
          display: status ? "block" : "none",
        }}
      >
        Detail的内容
      </p>
    </div>
  );
}

export default function App() {
  function handleActive(status) {
    console.log(status);
  }
  return (
    <>
      <Detail onActive={handleActive} />
    </>
  );
}