深入解析:React知识点梳理

注:纯手打,如有错误欢迎评论区交流!
转载请注明出处:https://blog.csdn.net/testleaf/article/details/148403372
编写此文是为了更好地学习前端知识,如果损害了有关人的利益,请联系删除!
本文章将不定时更新,敬请期待!!!
欢迎点赞、收藏、转发、关注,多谢!!!

一、React开发环境搭建

1、必备工具安装

Node.js:
作用:提供npm/yarn包管理工具和运行JavaScript的环境
下载:Node.js官网
验证安装:

node -v # 检查Node版本
npm -v # 检查npm版本

代码编辑器:
推荐:VS Code(安装插件:ES7+ React/Redux、Prettier、ESLint)

2、创建React项目​

方式1:使用Create React App(CRA,官方推荐)​

npx create-react-app my-app # 创建项目
cd my-app # 进入项目目录
npm start # 启动开发服务器

特点​​:
零配置,内置Babel、Webpack、ESLint等工具
适合新手和快速原型开发
方式2:使用Vite(更快的现代构建工具)​

npm create vite@latest my-react-app --template react
cd my-react-app
npm install
npm run dev

优势​​:启动和热更新速度极快,适合大型项目。
方式3:手动配置Webpack(进阶)​
适合需要深度定制化的项目,需自行配置Babel、Loader等(不推荐新手)。

3、项目目录结构(CRA生成)

my-app/
├── node_modules/ # 依赖库
├── public/ # 静态资源(HTML模板、图片等)
│ └── index.html # 主HTML文件
├── src/ # 源码目录
│ ├── App.js # 根组件
│ ├── index.js # 入口文件
│ └── styles/ # CSS文件
├── package.json # 项目配置和依赖
└── README.md

4、关键依赖说明​

  • ​​react​​ & ​​react-dom​​:React核心库和DOM渲染库
  • ​​react-scripts​​(CRA):封装了Webpack/Babel配置
  • ​​其他常用库​​:
    • 路由:react-router-dom
    • 状态管理:redux / zustand
    • UI组件库:Material-UI / Ant Design

5、开发与构建命令​

命令作用
npm start启动开发服务器(默认3000端口)
npm run build生成生产环境优化代码(在build/目录)
npm test运行Jest测试
npm run eject暴露CRA配置(不可逆操作)

6、常见问题解决

端口冲突​​:
修改启动端口

PORT=3001 npm start

依赖安装慢​​:
查看当前 npm 镜像源​

npm config get registry

检查所有 npm 配置(包括镜像源、全局安装路径等)

npm config list

切换npm镜像

npm config set registry https://registry.npmmirror.com

恢复为官方源​​

npm config set registry https://registry.npmjs.org

临时使用镜像源(单次安装)

npm install 包名 --registry=https://registry.npmmirror.com

常见镜像源地址​

镜像源名称地址
npm官方源https://registry.npmjs.org/
淘宝镜像https://registry.npmmirror.com/
腾讯云镜像https://mirrors.cloud.tencent.com/npm/

React版本升级​​:

npm install react@latest react-dom@latest

7、创建React项目【TypeScript版】

方法 1:使用 create-react-app(CRA,官方推荐)​
创建项目:

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

或(旧版 CRA):

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

项目结构:

my-app/
├── src/
│ ├── App.tsx # TypeScript 组件
│ ├── index.tsx # TypeScript 入口文件
│ └── react-app-env.d.ts # React 类型声明
├── tsconfig.json # TypeScript 配置
└── package.json

启动项目:

cd my-app
npm start

访问 http://localhost:3000 查看运行效果。
方法 2:使用 Vite(推荐,速度更快)
创建项目​:

npm create vite@latest my-react-ts-app --template react-ts

或使用 yarn:

yarn create vite my-react-ts-app --template react-ts

项目结构:

my-react-ts-app/
├── src/
│ ├── App.tsx
│ ├── main.tsx # 入口文件
│ └── vite-env.d.ts # Vite 类型声明
├── tsconfig.json
├── vite.config.ts # Vite 配置
└── package.json

安装依赖并启动:

cd my-react-ts-app
npm install
npm run dev

访问 http://localhost:5173(Vite 默认端口)。
方法 3:手动配置(Webpack + TypeScript)​​
适用于需要深度定制的项目(不推荐新手)。
初始化项目​:

mkdir my-react-ts-project
cd my-react-ts-project
npm init -y

安装依赖​:

npm install react react-dom
npm install --save-dev typescript @types/react @types/react-dom
npm install --save-dev webpack webpack-cli webpack-dev-server babel-loader @babel/core @babel/preset-env @babel/preset-react @babel/preset-typescript

配置 tsconfig.json:

{
"compilerOptions": {
"target": "ES6",
"module": "ESNext",
"jsx": "react-jsx",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true
},
"include": ["src/**/*"]
}

配置 webpack.config.js:

const path = require("path");
module.exports = {
entry: "./src/index.tsx",
output: {
filename: "bundle.js",
path: path.resolve(__dirname, "dist"),
},
resolve: {
extensions: [".tsx", ".ts", ".js"],
},
module: {
rules: [
{
test: /\.(ts|tsx)$/,
exclude: /node_modules/,
use: "babel-loader",
},
],
},
devServer: {
static: path.join(__dirname, "dist"),
port: 3000,
},
};

创建 src/index.tsx:

import React from "react";
import ReactDOM from "react-dom/client";
const App = () =>
<h1>Hello, React + TypeScript!<
  /h1>
  ;
  ReactDOM.createRoot(document.getElementById("root")!).render(<App />
    );

启动开发服务器​:

npx webpack serve --mode development

二、JSX基础

JSX(JavaScript XML)是 React 的核心语法,它允许在 JavaScript 中编写类似 HTML 的代码,用于定义 React 组件的结构。

1、JSX 是什么?​

JSX = JavaScript + XML​​,是一种语法扩展。
它会被 Babel 转译为 React.createElement() 调用,最终生成 JavaScript 对象(虚拟 DOM)。
​​示例​​:

const element = <h1>Hello, JSX!<
  /h1>
  ;

编译后:

const element = React.createElement("h1", null, "Hello, JSX!");

2、JSX 基本规则​

(1) 必须有一个根元素​

// ✅ 正确
const element = (
<div>
  <h1>标题<
    /h1>
    <p>内容<
      /p>
      <
      /div>
      );
      // ❌ 错误(多个根元素)
      const element = (
      <h1>标题<
        /h1>
        <p>内容<
          /p>
          );

解决方案​​:使用 (<></> 语法糖)包裹:

const element = (
<
>
<h1>标题<
  /h1>
  <p>内容<
    /p>
    <
    />
    );

(2) 所有标签必须闭合​

// ✅ 正确
<img src="logo.png" alt="Logo" />
  <input type="text" />
    // ❌ 错误
    <img src="logo.png" alt="Logo">
      <input type="text">

​​(3) 使用 className 代替 class

// ✅ 正确
<div className="container">内容<
  /div>
  // ❌ 错误
  <div class=
  "container">内容<
  /div>

(4) 使用 htmlFor 代替 for(表单标签)​

// ✅ 正确
<label htmlFor="username">用户名<
  /label>
  <input id="username" type="text" />
    // ❌ 错误
    <label for="username">用户名<
      /label>

3、JSX 嵌入 JavaScript 表达式​​

用 {} 包裹 JavaScript 表达式:

const name = "Alice";
const age = 25;
const element = (
<div>
  <p>姓名:{name
    }<
    /p>
    <p>年龄:{age
      }<
      /p>
      <p>明年年龄:{age + 1
        }<
        /p>
        <p>是否成年:{age >= 18 ? "是" : "否"
          }<
          /p>
          <
          /div>
          );

可以嵌入的内容​​:

  • 变量、数字、字符串
  • 三元运算符 condition ? true : false
  • 函数调用(如 user.getName()
  • 数组(如 [1, 2, 3].map(...)

​​不能嵌入的内容​​:

  • 普通对象(如 {a: 1},除非用于样式)
  • if/for 语句(改用三元运算符或 map

4、JSX 中的样式​​

​​(1) 行内样式(style 属性)​​
使用 ​​对象​​ 传递样式,属性名用 ​​驼峰命名法​​:

const style = {
color: "red",
fontSize: "20px", // 注意是 fontSize,不是 font-size
};
const element = <p style={style
}>红色文字<
/p>
;

或直接写:

<p style={
{
color: "red", fontSize: "20px"
}
}>红色文字<
/p>

(2) CSS 类名(className)​

// App.css
.container {
background: #f0f0f0;
}
// App.jsx
import "./App.css";
const element = <div className="container">内容<
  /div>
  ;

5、JSX 中的条件渲染​​

​​(1) 三元运算符​

const isLoggedIn = true;
const element = (
<div>
  {isLoggedIn ? <button>退出<
    /button>
    : <button>登录<
      /button>
      }
      <
      /div>
      );

​​(2) && 短路运算​

const hasMessages = true;
const element = (
<div>
  {hasMessages &&
  <p>您有未读消息<
    /p>
    }
    <
    /div>
    );

(3) if-else 语句(在函数/组件外使用)​

function renderContent(isAdmin) {
if (isAdmin) {
return <AdminPanel />
  ;
  } else {
  return <UserPanel />
    ;
    }
    }
    const element = <div>
      {
      renderContent(true)
      }<
      /div>
      ;

6、JSX 中的列表渲染(map)​​

使用 map 遍历数组生成元素列表,​​必须加 key 属性​​:

const users = [
{
id: 1, name: "Alice"
},
{
id: 2, name: "Bob"
},
{
id: 3, name: "Charlie"
},
];
const userList = (
<ul>
  {users.map((user) =>
  (
  <li key={user.id
  }>
  {user.name
  }<
  /li>
  ))
  }
  <
  /ul>
  );

key 的作用​​:帮助 React 识别哪些元素发生了变化(通常用唯一 ID 或索引)。

7、JSX 中的事件处理​​

事件名用 ​​驼峰命名法​​(如 onClick 而不是 onclick)。
传递函数,而不是字符串。
基础实现:

const handleClick = () =>
{
alert("按钮被点击了!");
};
const element = <button onClick={handleClick
}>点击我<
/button>
;

使用事件参数:

const handleClick = (e) =>
{
alert("按钮被点击了!", e);
};
const element = <button onClick={handleClick
}>点击我<
/button>
;

传递自定义参数​:

const deleteUser = (userId) =>
{
console.log("删除用户", userId);
};
const userList = users.map((user) =>
(
<button onClick={
() =>
deleteUser(user.id)
}>删除 {user.name
}<
/button>
));

同时传递事件对象和自定义参数:

const deleteUser = (userId, e) =>
{
console.log("删除用户", userId, e);
};
const userList = users.map((user) =>
(
<button onClick={
(e) =>
deleteUser(user.id, e)
}>删除 {user.name
}<
/button>
));

8、JSX 中的注释

{
/* 注释 */
}

三、React组件基础使用

React 的核心思想是 ​​组件化开发​​,组件是构建 UI 的独立、可复用的代码单元。

1、组件的两种定义方式​​

​​(1) 函数组件(推荐)​​
使用 JavaScript 函数定义,返回 JSX。
​​适用于简单、无状态逻辑的组件​​。

function Welcome(props) {
return <h1>Hello, {props.name
  }<
  /h1>
  ;
  }
  // 使用组件
  <Welcome name="Alice" />

(2) 类组件(旧版写法)​​
使用 ES6 class 继承 React.Component。
​​适用于需要生命周期或状态管理的组件​​(现代 React 推荐用 Hooks 替代)。

class Welcome
extends React.Component {
render() {
return <h1>Hello, {
  this.props.name
  }<
  /h1>
  ;
  }
  }
  // 使用组件
  <Welcome name="Bob" />

2、组件的 props(属性)​​

props 是父组件传递给子组件的数据,​​只读不可修改​​。
(1) 传递 props

function UserCard(props) {
return (
<div>
  <h2>
    {props.name
    }<
    /h2>
    <p>年龄:{props.age
      }<
      /p>
      <
      /div>
      );
      }
      // 使用组件
      <UserCard name="Alice" age={
      25
      } />

(2) 解构 props(推荐)​

function UserCard({ name, age
}) {
return (
<div>
  <h2>
    {name
    }<
    /h2>
    <p>年龄:{age
      }<
      /p>
      <
      /div>
      );
      }

(3) 默认 props

function UserCard({ name, age = 18
}) {
// 如果未传递 age,默认为 18
return <p>
  {name
  } 年龄:{age
  }<
  /p>
  ;
  }
  // 或者使用 defaultProps(类组件)
  UserCard.defaultProps = {
  age: 18,
  };

(4) 传递子元素(children)​

function Card({ children
}) {
return <div className="card">
  {children
  }<
  /div>
  ;
  }
  // 使用
  <Card>
    <h3>标题<
      /h3>
      <p>内容<
        /p>
        <
        /Card>

3、组件的 state(状态)​​

state 是组件内部的可变数据,​​修改状态会触发重新渲染​​。
(1) 函数组件:useState Hook​

import { useState
} from "react";
function Counter() {
const [count, setCount] = useState(0);
// 初始值为 0
return (
<div>
  <p>当前计数:{count
    }<
    /p>
    <button onClick={
    () =>
    setCount(count + 1)
    }>
    +1<
    /button>
    <
    /div>
    );
    }

修改对象状态:

import { useState
} from "react";
function Park() {
const [person, setPerson] = useState({
name:'testleaf'
});
const changeName = () =>
{
setPerson({
...person,
name: 'testleaf.cn'
})
}
return (
<div>
  <button onClick={changeName
  }>
  {person.name
  }<
  /button>
  <
  /div>
  );
  }

(2) 类组件:this.state

class Counter
extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
render() {
return (
<div>
  <p>当前计数:{
    this.state.count
    }<
    /p>
    <button onClick={
    () =>
    this.setState({
    count: this.state.count + 1
    })
    }>
    +1
    <
    /button>
    <
    /div>
    );
    }
    }

4、组件通信​​

​​(1) 父传子:通过 props

function Parent() {
const message = "Hello from Parent";
return <Child msg={message
} />
;
}
function Child({ msg
}) {
return <p>
  {msg
  }<
  /p>
  ;
  }

(2) 子传父:通过回调函数​

function Parent() {
const handleChildClick = (data) =>
{
console.log("子组件传递的数据:", data);
};
return <Child onClick={handleChildClick
} />
;
}
function Child({ onClick
}) {
return <button onClick={
() =>
onClick("Child Data")
}>传递数据<
/button>
;
}

(3) 兄弟组件通信​​
通过 ​​共同的父组件​​ 传递状态(状态提升)。
使用 ​​Context API​​ 或 ​​状态管理库​​(如 Redux、Zustand)。

状态提升【两个兄弟组件需要共享同一个数据源,将共享状态提升到父组件,通过 props 传递数据和回调函数】:

import { useState
} from 'react';
// 父组件
function Parent() {
const [count, setCount] = useState(0);
return (
<div>
  <ChildA count={count
  } />
  <ChildB onIncrement={
  () =>
  setCount(count + 1)
  } />
  <
  /div>
  );
  }
  // 子组件 A(显示计数)
  function ChildA({ count
  }) {
  return <p>当前计数:{count
    }<
    /p>
    ;
    }
    // 子组件 B(控制计数)
    function ChildB({ onIncrement
    }) {
    return <button onClick={onIncrement
    }>
    +1<
    /button>
    ;
    }

使用 Context API​【多个组件需要共享状态,避免 props 层层传递,使用 React.createContext 创建全局状态,并通过 Provider 和 useContext 访问】:

import { createContext, useContext, useState
} from 'react';
// 1. 创建 Context
const ThemeContext = createContext();
// 父组件(Provider)
function App() {
const [theme, setTheme] = useState('light');
return (
<ThemeContext.Provider value={
{ theme, setTheme
}
}>
<Header />
  <Content />
    <
    /ThemeContext.Provider>
    );
    }
    // 子组件 A(显示当前主题)
    function Header() {
    const { theme
    } = useContext(ThemeContext);
    return <header>当前主题:{theme
      }<
      /header>
      ;
      }
      // 子组件 B(切换主题)
      function Content() {
      const { setTheme
      } = useContext(ThemeContext);
      return (
      <button onClick={
      () =>
      setTheme(prev => prev === 'light' ? 'dark' : 'light')
      }>
      切换主题
      <
      /button>
      );
      }

5、组件的生命周期(类组件)​

生命周期方法触发时机
componentDidMount组件挂载后(首次渲染完成)
componentDidUpdate组件更新后(state/props 变化)
componentWillUnmount组件卸载前(清理副作用)

函数组件替代方案​​:使用 useEffect Hook。

6、组件复用​​

​​(1) 组合模式​​
通过 props.children 或自定义 props 实现灵活组合:

function Layout({ header, content
}) {
return (
<div>
  <div className="header">
    {header
    }<
    /div>
    <div className="content">
      {content
      }<
      /div>
      <
      /div>
      );
      }
      // 使用
      <Layout
      header={
      <h1>标题<
        /h1>
        }
        content={
        <p>正文内容<
          /p>
          }
          />

(2) 高阶组件(HOC)​

function withLogger(WrappedComponent) {
return function (props) {
console.log("组件渲染:", props);
return <WrappedComponent {
...props
} />
;
};
}
const EnhancedComponent = withLogger(MyComponent);

(3) 自定义 Hook​

function useCounter(initialValue) {
const [count, setCount] = useState(initialValue);
const increment = () =>
setCount(count + 1);
return { count, increment
};
}
// 使用
function CounterA() {
const { count, increment
} = useCounter(0);
return <button onClick={increment
}>计数:{count
}<
/button>
;
}

7、表单控制

受控组件:
受控组件是指表单元素的值由 React 的 state 控制,并通过 onChange 事件更新。

import { useState
} from 'react';
function LoginForm() {
const [formData, setFormData] = useState({
username: '',
password: ''
});
const handleChange = (e) =>
{
const { name, value
} = e.target;
setFormData({
...formData,
[name]: value
});
};
const handleSubmit = (e) =>
{
e.preventDefault();
console.log('提交的数据:', formData);
};
return (
<form onSubmit={handleSubmit
}>
<input
type="text"
name="username"
value={formData.username
}
onChange={handleChange
}
placeholder="用户名"
/>
<input
type="password"
name="password"
value={formData.password
}
onChange={handleChange
}
placeholder="密码"
/>
<button type="submit">登录<
  /button>
  <
  /form>
  );
  }

非受控组件:
非受控组件是指表单数据由 DOM 自身管理,通过 ref 获取值。

import { useRef
} from 'react';
function LoginForm() {
const usernameRef = useRef(null);
const passwordRef = useRef(null);
const handleSubmit = (e) =>
{
e.preventDefault();
console.log('提交的数据:', {
username: usernameRef.current.value,
password: passwordRef.current.value
});
};
return (
<form onSubmit={handleSubmit
}>
<input
type="text"
ref={usernameRef
}
placeholder="用户名"
/>
<input
type="password"
ref={passwordRef
}
placeholder="密码"
/>
<button type="submit">登录<
  /button>
  <
  /form>
  );
  }

四、useEffect

useEffect 是 React Hooks 中最重要的 API 之一,用于处理 ​​副作用(Side Effects)​​,如数据获取、订阅、手动 DOM 操作等。

1、useEffect 基本语法​

import { useEffect
} from 'react';
useEffect(() =>
{
// 副作用逻辑(组件渲染后执行)
return () =>
{
// 清理逻辑(组件卸载或依赖项变化前执行)
};
}, [dependencies]);
// 依赖项数组

参数说明​:

参数说明
effect包含副作用的函数,在组件渲染后执行
dependencies依赖项数组,决定 effect 何时重新执行

2、useEffect 的 3 种使用方式​

(1) 无依赖项(每次渲染后执行)

useEffect(() =>
{
console.log('每次组件更新后都会执行');
});

适用场景​​: 极少使用,可能导致性能问题。

(2) 空依赖项(仅首次渲染后执行)​

useEffect(() =>
{
console.log('仅在组件挂载时执行一次');
}, []);
// 空数组

适用场景​​:

  • 数据初始化请求(如 fetch)
  • 事件监听(如 window.addEventListener)
  • 定时器(如 setInterval)

示例:组件挂载时获取数据​

useEffect(() =>
{
const fetchData = async () =>
{
const res = await fetch('https://api.example.com/data');
const data = await res.json();
setData(data);
};
fetchData();
}, []);

(3) 有依赖项(依赖变化时执行)​

const [count, setCount] = useState(0);
useEffect(() =>
{
console.log('count 变化时执行:', count);
}, [count]);
// 依赖 count

适用场景​​:

  • 监听特定状态变化(如搜索关键词变化时重新查询)
  • 计算衍生数据

示例:监听输入框变化​

const [keyword, setKeyword] = useState('');
useEffect(() =>
{
if (keyword) {
search(keyword);
// 关键词变化时触发搜索
}
}, [keyword]);

3、useEffect 的清理机制​​

如果 effect 返回一个函数,React 会在 ​​组件卸载​​ 或 ​​依赖项变化导致 effect 重新执行前​​ 调用它进行清理。
示例 1:清除定时器​

useEffect(() =>
{
const timer = setInterval(() =>
{
console.log('每秒执行');
}, 1000);
return () =>
{
clearInterval(timer);
// 组件卸载时清除定时器
};
}, []);

示例 2:取消事件监听​

useEffect(() =>
{
const handleClick = () => console.log('点击了窗口');
window.addEventListener('click', handleClick);
return () =>
{
window.removeEventListener('click', handleClick);
// 清理事件
};
}, []);

4、useEffect 常见使用场景​

(1) 数据获取(Fetch Data)​

useEffect(() =>
{
let ignore = false;
// 防止竞态条件
const fetchData = async () =>
{
const res = await fetch(`https://api.example.com/data/${id
}`);
const data = await res.json();
if (!ignore) setData(data);
// 确保组件未卸载
};
fetchData();
return () =>
{
ignore = true;
// 组件卸载时标记忽略结果
};
}, [id]);
// id 变化时重新获取

​​(2) 订阅外部数据源​

useEffect(() =>
{
const subscription = dataSource.subscribe((data) =>
{
setData(data);
});
return () =>
{
subscription.unsubscribe();
// 清理订阅
};
}, []);

(3) 手动操作 DOM​

useEffect(() =>
{
const button = document.getElementById('my-button');
button.addEventListener('click', handleClick);
return () =>
{
button.removeEventListener('click', handleClick);
};
}, []);

5、useEffect 的注意事项​

(1) 避免无限循环​​
​​错误示例​​:

const [count, setCount] = useState(0);
useEffect(() =>
{
setCount(count + 1);
// 每次渲染后修改 count,导致无限循环
});

修复方式​​:
确保依赖项正确
使用函数式更新(避免直接依赖 count)

useEffect(() =>
{
setCount(prev => prev + 1);
// 不依赖 count
}, []);
// 仅在挂载时执行

(2) 依赖项遗漏导致的问题​
如果 effect 使用了某个变量但没有声明为依赖项,可能导致逻辑错误:

const [count, setCount] = useState(0);
useEffect(() =>
{
console.log(count);
// 依赖 count 但未声明
}, []);
// 控制台警告:缺少依赖项

修复方式​​:

  • 添加所有依赖项(ESLint 会提示)
  • 如果某些依赖项变化时不想重新执行 effect,可以拆分逻辑或使用 useRef 存储可变值。

(3) useEffect 与 useLayoutEffect 的区别

Hook执行时机适用场景
useEffect浏览器绘制后异步执行大多数副作用(数据获取、订阅)
useLayoutEffectDOM 更新后同步执行需要阻塞浏览器渲染的场景(如测量 DOM 尺寸)

五、React路由

React Router 是 React 生态中最流行的路由解决方案,用于构建单页应用(SPA)的导航系统。

1、安装与基础配置​​

​​(1) 安装​

npm install react-router-dom

(2) 基本路由结构​

// main.jsx / App.jsx
import { BrowserRouter, Routes, Route
} from 'react-router-dom';
function App() {
return (
<BrowserRouter>
  <Routes>
    <Route path="/" element={
    <HomePage />
      } />
      <Route path="/about" element={
      <AboutPage />
        } />
        <
        /Routes>
        <
        /BrowserRouter>
        );
        }

2、核心组件与 API​​

​​(1) 路由类型​

组件作用使用场景
<BrowserRouter>使用 HTML5 History API生产环境(需要服务器支持)
<HashRouter>使用 URL hash (#)静态文件部署(无服务器配置)
(2) 路由定义​
<Routes>
  {
  /* 精确匹配 */
  }
  <Route path="/" element={
  <Home />
    } />
    {
    /* 嵌套路由 */
    }
    <Route path="/users" element={
    <UsersLayout />
      }>
      <Route index element={
      <UserList />
        } />
        // 默认子路由
        <Route path=":id" element={
        <UserDetail />
          } />
          // 动态参数
          <Route path="create" element={
          <CreateUser />
            } />
            // 静态子路由
            <
            /Route>
            {
            /* 404 页面 */
            }
            <Route path="*" element={
            <NotFound />
              } />
              <
              /Routes>

(3) 导航组件​

import { Link, NavLink, useNavigate
} from 'react-router-dom';
// 1. Link (普通导航)
<Link to="/about">关于我们<
  /Link>
  // 2. NavLink (激活状态样式)
  <NavLink
  to="/about"
  className={
  ({ isActive
  }) => isActive ? 'active' : ''
  }
  >关于我们<
  /NavLink>
  // 3. 编程式导航
  const navigate = useNavigate();
  <button onClick={
  () =>
  navigate('/profile')
  }>跳转<
  /button>

3、动态路由与参数获取​​

​​(1) 路径参数​

// 路由定义
<Route path="/users/:id" element={
<UserDetail />
  } />
  // 组件中获取参数
  import { useParams
  } from 'react-router-dom';
  function UserDetail() {
  const { id
  } = useParams();
  // 获取 :id
  return <div>User ID: {id
    }<
    /div>
    ;
    }

(2) 查询参数​

// 跳转时传递
navigate(`/search?query=${keyword
}`);
// 组件中获取
import { useSearchParams
} from 'react-router-dom';
function SearchPage() {
const [searchParams] = useSearchParams();
const query = searchParams.get('query');
// 获取 ?query=xxx
}

(3) 状态参数​

// 跳转时传递
navigate('/profile', {
state: {
fromHome: true
}
});
// 组件中获取
import { useLocation
} from 'react-router-dom';
function ProfilePage() {
const { state
} = useLocation();
// 获取 { fromHome: true }
}

4、嵌套路由与布局模式​​

​​(1) 共享布局​

// 定义带布局的路由
<Route path="/admin" element={
<AdminLayout />
  }>
  <Route index element={
  <Dashboard />
    } />
    <Route path="products" element={
    <ProductList />
      } />
      <
      /Route>
      // AdminLayout.jsx
      import { Outlet
      } from 'react-router-dom';
      function AdminLayout() {
      return (
      <div>
        <h1>Admin Header<
          /h1>
          <Outlet />
            {
            /* 子路由将渲染在这里 */
            }
            <
            /div>
            );
            }

(2) 多布局入口​

function App() {
return (
<Routes>
  <Route element={
  <PublicLayout />
    }>
    <Route path="/" element={
    <Home />
      } />
      <Route path="/login" element={
      <Login />
        } />
        <
        /Route>
        <Route element={
        <PrivateLayout />
          }>
          <Route path="/dashboard" element={
          <Dashboard />
            } />
            <
            /Route>
            <
            /Routes>
            );
            }

对象配置方式:

const router = createBrowserRouter([
{
element: <PublicLayout />
  ,
  children: [
  {
  path: '/',
  element: <Home />
    },
    {
    path: '/login',
    element: <Login />
      }
      ]
      },
      {
      element: <PrivateLayout />
        ,
        children: [
        {
        path: '/dashboard',
        element: <Dashboard />
          }
          ]
          }
          ]);
          export default router;

5、路由守卫(权限控制)​​

​​(1) 高阶组件模式​

function PrivateRoute({ children
}) {
const { user
} = useAuth();
// 自定义权限钩子
const location = useLocation();
if (!user) {
return <Navigate to="/login" state={
{
from: location
}
} replace />
;
}
return children;
}
// 使用
<Route
path="/dashboard"
element={
<PrivateRoute>
  <Dashboard />
    <
    /PrivateRoute>
    }
    />

(2) 包装器组件模式​

<Route
path="/admin"
element={
<RequireAuth>
  <AdminLayout />
    <
    /RequireAuth>
    }
    >
    {
    /* 子路由自动继承权限 */
    }
    <Route path="settings" element={
    <Settings />
      } />
      <
      /Route>

6、代码分割与懒加载​

import { lazy, Suspense
} from 'react';
const About = lazy(() =>
import('./pages/About'));
function App() {
return (
<Suspense fallback={
<div>Loading...<
  /div>
  }>
  <Routes>
    <Route path="/about" element={
    <About />
      } />
      <
      /Routes>
      <
      /Suspense>
      );
      }

7、404 路由配置

使用JSX配置方式:

import { Routes, Route
} from 'react-router-dom';
function App() {
return (
<Routes>
  <Route path="/" element={
  <Home />
    } />
    <Route path="/about" element={
    <About />
      } />
      {
      /* 404 路由 - 放在最后 */
      }
      <Route path="*" element={
      <NotFound />
        } />
        <
        /Routes>
        );
        }

使用对象配置方式:

const router = createBrowserRouter([
{
path: '/',
element: <Home />
  },
  {
  path: '/about',
  element: <About />
    },
    {
    path: '*',
    element: <NotFound />
      }
      ]);

六、Redux

Redux 是 JavaScript 应用的可预测状态管理库,常用于 React 等框架中管理全局状态。

1、Redux 三大原则​​

  • ​​单一数据源​​:整个应用状态存储在唯一的 store 中。
  • ​​状态只读​​:只能通过 dispatch(action) 修改状态。
  • ​​纯函数修改​​:使用 reducer 纯函数处理状态变更。

2、Redux 核心概念​​

​​(1) Action​​
描述发生了什么的对象,必须包含 type 字段:

// action 类型常量(避免拼写错误)
const ADD_TODO = 'ADD_TODO';
// action 创建函数(Action Creator)
const addTodo = (text) =>
({
type: ADD_TODO,
payload: { text
}
});

​​(2) Reducer​​
纯函数,接收当前 state 和 action,返回新状态:

const initialState = {
todos: []
};
function todoReducer(state = initialState, action) {
switch (action.type) {
case ADD_TODO:
return {
...state,
todos: [...state.todos, action.payload.text]
};
default:
return state;
// 必须返回默认状态
}
}

​​(3) Store​​
通过 createStore 创建,提供核心方法:

import { createStore
} from 'redux';
const store = createStore(todoReducer);
// 获取当前状态
store.getState();
// 派发 action
store.dispatch(addTodo('Learn Redux'));
// 订阅状态变化
const unsubscribe = store.subscribe(() =>
{
console.log('State updated:', store.getState());
});
// 取消订阅
unsubscribe();

3、React-Redux 集成​

(1) 安装依赖​

npm install redux react-redux

(2) 创建 Redux Store​

// store.js
import { createStore
} from 'redux';
import rootReducer from './reducers';
const store = createStore(rootReducer);
export default store;

(3) 使用 Provider 注入 Store​

// index.js
import React from 'react';
import { Provider
} from 'react-redux';
import store from './store';
ReactDOM.render(
<Provider store={store
}>
<App />
  <
  /Provider>
  ,
  document.getElementById('root')
  );

(4) 组件中访问状态(useSelector)​

import { useSelector
} from 'react-redux';
function TodoList() {
const todos = useSelector(state => state.todos);
return (
<ul>
  {todos.map((todo, index) =>
  (
  <li key={index
  }>
  {todo
  }<
  /li>
  ))
  }
  <
  /ul>
  );
  }

(5) 组件中派发 Action(useDispatch)​

import { useDispatch
} from 'react-redux';
import { addTodo
} from './actions';
function AddTodo() {
const [text, setText] = useState('');
const dispatch = useDispatch();
const handleSubmit = () =>
{
dispatch(addTodo(text));
setText('');
};
return (
<div>
  <input
  value={text
  }
  onChange={
  (e) =>
  setText(e.target.value)
  }
  />
  <button onClick={handleSubmit
  }>Add<
  /button>
  <
  /div>
  );
  }

4、异步 Action 处理(Redux-Thunk)​

(1) 安装中间件​

npm install redux-thunk

​​(2) 配置 Store​

import { createStore, applyMiddleware
} from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
const store = createStore(rootReducer, applyMiddleware(thunk));

(3) 编写异步 Action​

// actions.js
const fetchTodos = () =>
{
return async (dispatch) =>
{
dispatch({
type: 'FETCH_TODOS_REQUEST'
});
try {
const res = await fetch('/api/todos');
const todos = await res.json();
dispatch({
type: 'FETCH_TODOS_SUCCESS', payload: todos
});
} catch (error) {
dispatch({
type: 'FETCH_TODOS_FAILURE', error
});
}
};
};

(4) 组件中调用​

function TodoApp() {
const dispatch = useDispatch();
useEffect(() =>
{
dispatch(fetchTodos());
}, []);
// ...
}

5、Redux 最佳实践​​

​​(1) 项目结构​

src/
├── store/ # Redux 核心目录
│ ├── index.js # Store 创建和配置
│ ├── actions/ # Action 定义
│ │ └── todoActions.js
│ ├── reducers/ # Reducer 定义
│ │ ├── todoReducer.js
│ │ └── index.js # Root Reducer
│ └── types.js # Action 类型常量
├── components/ # 展示组件
└── containers/ # 容器组件(连接 Redux)

​​(2) 代码实现
定义 Action 类型 (store/types.js)​

export const ADD_TODO = 'ADD_TODO';
export const FETCH_TODOS = 'FETCH_TODOS';

创建 Action (store/actions/todoActions.js)​

import {
ADD_TODO, FETCH_TODOS
} from '../types';
// 同步 Action
export const addTodo = (text) =>
({
type: ADD_TODO,
payload: text
});
// 异步 Action (需配置 Redux-Thunk)
export const fetchTodos = () =>
{
return async (dispatch) =>
{
const res = await fetch('/api/todos');
const todos = await res.json();
dispatch({
type: FETCH_TODOS, payload: todos
});
};
};

编写 Reducer (store/reducers/todoReducer.js)​

import {
ADD_TODO, FETCH_TODOS
} from '../types';
const initialState = {
todos: []
};
export default function todoReducer(state = initialState, action) {
switch (action.type) {
case ADD_TODO:
return {
...state,
todos: [...state.todos, action.payload]
};
case FETCH_TODOS:
return {
...state,
todos: action.payload
};
default:
return state;
}
}

合并 Reducers (store/reducers/index.js)​

import { combineReducers
} from 'redux';
import todoReducer from './todoReducer';
export default combineReducers({
todos: todoReducer
});

创建 Store (store/index.js)​

import { createStore, applyMiddleware
} from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
const store = createStore(rootReducer, applyMiddleware(thunk));
export default store;

​​(3) React 组件集成​
注入 Store (src/index.js)​

import React from 'react';
import { Provider
} from 'react-redux';
import store from './store';
import App from './App';
ReactDOM.render(
<Provider store={store
}>
<App />
  <
  /Provider>
  ,
  document.getElementById('root')
  );

组件中访问状态和派发 Action​

import React from 'react';
import { useSelector, useDispatch
} from 'react-redux';
import { addTodo, fetchTodos
} from './store/actions/todoActions';
function TodoApp() {
const todos = useSelector(state => state.todos.todos);
const dispatch = useDispatch();
const handleAddTodo = () =>
{
dispatch(addTodo('New Task'));
};
useEffect(() =>
{
dispatch(fetchTodos());
}, []);
return (
<div>
  <button onClick={handleAddTodo
  }>Add Todo<
  /button>
  <ul>
    {todos.map((todo, index) =>
    (
    <li key={index
    }>
    {todo
    }<
    /li>
    ))
    }
    <
    /ul>
    <
    /div>
    );
    }

​​(4) 使用 Redux Toolkit(官方推荐)​
简化 Redux 代码的官方工具库

npm install @reduxjs/toolkit

重构 Store (store/index.js)​

import { configureStore
} from '@reduxjs/toolkit';
import todoReducer from './reducers/todoReducer';
export default configureStore({
reducer: {
todos: todoReducer
}
});

使用 createSlice 简化 Reducer (store/reducers/todoReducer.js)​

import { createSlice
} from '@reduxjs/toolkit';
const todoSlice = createSlice({
name: 'todos',
initialState: {
todos: []
},
reducers: {
addTodo: (state, action) =>
{
state.todos.push(action.payload);
// 直接修改(Immer 支持)
}
}
});
export const { addTodo
} = todoSlice.actions;
export default todoSlice.reducer;

七、Zustand

Zustand 是一个轻量级的 React 状态管理库,相比 Redux 更简洁,比 Context API 更高效。

1、安装与基础使用​​

​​(1) 安装​

npm install zustand

​​(2) 创建 Store​

// store/counterStore.js
import { create
} from 'zustand';
const useCounterStore = create((set) =>
({
count: 0,
increment: () =>
set((state) =>
({
count: state.count + 1
})),
decrement: () =>
set((state) =>
({
count: state.count - 1
})),
reset: () =>
set({
count: 0
}),
}));
export default useCounterStore;

(3) 在组件中使用​

import useCounterStore from './store/counterStore';
function Counter() {
const { count, increment, decrement
} = useCounterStore();
return (
<div>
  <p>Count: {count
    }<
    /p>
    <button onClick={increment
    }>
    +<
    /button>
    <button onClick={decrement
    }>
    -<
    /button>
    <
    /div>
    );
    }

2、核心特性​​

​​(1) 状态更新​​
​​直接更新​​:

set({
count: 10
});
// 直接替换

基于旧状态更新​​:

set((state) =>
({
count: state.count + 1
}));
// 函数式更新

(2) 异步操作​

const useUserStore = create((set) =>
({
user: null,
loading: false,
fetchUser: async (id) =>
{
set({
loading: true
});
const res = await fetch(`/api/users/${id
}`);
const user = await res.json();
set({ user, loading: false
});
},
}));

(3) 计算属性

const useCartStore = create((set, get) =>
({
items: [],
total: () =>
get().items.reduce((sum, item) => sum + item.price, 0),
}));

3、性能优化​​

​​(1) 选择性订阅(避免不必要的渲染)​

function CartTotal() {
// 只订阅 total 计算属性,不依赖整个 store
const total = useCartStore((state) => state.total());
return <div>Total: ${total
  }<
  /div>
  ;
  }

(2) 浅比较

const { name, age
} = useUserStore(
(state) =>
({
name: state.name, age: state.age
}),
shallow // 避免在 name/age 未变化时触发渲染
);

(3) 使用 Immer 简化嵌套更新​

npm install immer
import { produce
} from 'immer';
const useNestedStore = create((set) =>
({
user: {
name: 'Alice', age: 25
},
updateName: (name) =>
set(produce((state) =>
{ state.user.name = name
})),
}));

4、高级用法​​

​​(1) 持久化

npm install zustand/middleware
import { persist
} from 'zustand/middleware';
const useAuthStore = create(
persist(
(set) =>
({
token: null,
login: (token) =>
set({ token
}),
logout: () =>
set({
token: null
}),
}),
{
name: 'auth-storage'
} // 存储到 localStorage
)
);

(2) 中间件

const logMiddleware = (config) =>
(set, get, api) =>
config((args) =>
{
console.log('State changed:', args);
set(args);
}, get, api);
const useStore = create(logMiddleware((set) =>
({
// ...store logic
})));

(3) 组合多个 Store​

const useCombinedStore = create((...a) =>
({
...useCounterStore(...a),
...useUserStore(...a),
}));

(4) 完整示例​

// store/userStore.js
import { create
} from 'zustand';
import { persist
} from 'zustand/middleware';
const useUserStore = create(
persist(
(set, get) =>
({
user: null,
isLoggedIn: () =>
!!get().user,
login: async (email, password) =>
{
const res = await fetch('/api/login', {
/* ... */
});
set({
user: await res.json()
});
},
logout: () =>
set({
user: null
}),
}),
{
name: 'user-storage'
}
)
);
export default useUserStore;
posted @ 2025-07-16 14:33  yjbjingcha  阅读(21)  评论(0)    收藏  举报