React知识点梳理

注:纯手打,如有错误欢迎评论区交流!

转载请注明出处:https://blog.csdn.net/testleaf/article/details/148403372

编写此文是为了更好地学习前端知识,如果损害了有关人的利益,请联系删除!

本文章将不定时更新,敬请期待!!!

欢迎点赞、收藏、转发、关注,多谢!!!

目录

一、React开发环境搭建

1、必备工具安装

Node.js:

作用:提供npm/yarn包管理工具和运行JavaScript的环境

下载:Node.js官网

验证安装:

bash 复制代码
node -v  # 检查Node版本
npm -v   # 检查npm版本

代码编辑器:

推荐:VS Code(安装插件:ES7+ React/Redux、Prettier、ESLint)

2、创建React项目​

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

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

特点​​:

零配置,内置Babel、Webpack、ESLint等工具

适合新手和快速原型开发
方式2:使用Vite(更快的现代构建工具)​

bash 复制代码
npm create vite@latest my-react-app --template react
cd my-react-app
npm install
npm run dev

优势​​:启动和热更新速度极快,适合大型项目。
方式3:手动配置Webpack(进阶)​

适合需要深度定制化的项目,需自行配置Babel、Loader等(不推荐新手)。

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

bash 复制代码
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、常见问题解决

端口冲突​​:

修改启动端口

bash 复制代码
PORT=3001 npm start

依赖安装慢​​:

查看当前 npm 镜像源​

bash 复制代码
npm config get registry

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

bash 复制代码
npm config list

切换npm镜像源

bash 复制代码
npm config set registry https://registry.npmmirror.com

恢复为官方源​​

bash 复制代码
npm config set registry https://registry.npmjs.org

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

bash 复制代码
npm install 包名 --registry=https://registry.npmmirror.com

常见镜像源地址​

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

React版本升级​​:

bash 复制代码
npm install react@latest react-dom@latest

7、创建React项目【TypeScript版】

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

创建项目:

bash 复制代码
npx create-react-app my-app --template typescript

或(旧版 CRA):

bash 复制代码
npx create-react-app my-app --typescript

项目结构:

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

启动项目:

bash 复制代码
cd my-app
npm start

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

创建项目​:

bash 复制代码
npm create vite@latest my-react-ts-app --template react-ts

或使用 yarn:

bash 复制代码
yarn create vite my-react-ts-app --template react-ts

项目结构:

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

安装依赖并启动:

bash 复制代码
cd my-react-ts-app
npm install
npm run dev

访问 http://localhost:5173(Vite 默认端口)。
方法 3:手动配置(Webpack + TypeScript)​​

适用于需要深度定制的项目(不推荐新手)。

初始化项目​:

bash 复制代码
mkdir my-react-ts-project
cd my-react-ts-project
npm init -y

安装依赖​:

bash 复制代码
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:

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

配置 webpack.config.js:

javascript 复制代码
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:

typescript 复制代码
import React from "react";
import ReactDOM from "react-dom/client";

const App = () => <h1>Hello, React + TypeScript!</h1>;

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

启动开发服务器​:

bash 复制代码
npx webpack serve --mode development

二、JSX基础

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

1、JSX 是什么?​

JSX = JavaScript + XML​​,是一种语法扩展。

它会被 Babel 转译为 React.createElement() 调用,最终生成 JavaScript 对象(虚拟 DOM)。

​​示例​​:

javascript 复制代码
const element = <h1>Hello, JSX!</h1>;

编译后:

javascript 复制代码
const element = React.createElement("h1", null, "Hello, JSX!");

2、JSX 基本规则​

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

javascript 复制代码
// ✅ 正确
const element = (
  <div>
    <h1>标题</h1>
    <p>内容</p>
  </div>
);

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

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

javascript 复制代码
const element = (
  <>
    <h1>标题</h1>
    <p>内容</p>
  </>
);

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

javascript 复制代码
// ✅ 正确
<img src="logo.png" alt="Logo" />
<input type="text" />

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

​​(3) 使用 className 代替 class

javascript 复制代码
// ✅ 正确
<div className="container">内容</div>

// ❌ 错误
<div class="container">内容</div>

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

javascript 复制代码
// ✅ 正确
<label htmlFor="username">用户名</label>
<input id="username" type="text" />

// ❌ 错误
<label for="username">用户名</label>

3、JSX 嵌入 JavaScript 表达式​​

用 {} 包裹 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 属性)​​

使用 ​​对象​​ 传递样式,属性名用 ​​驼峰命名法​​:

javascript 复制代码
const style = {
  color: "red",
  fontSize: "20px", // 注意是 fontSize,不是 font-size
};

const element = <p style={style}>红色文字</p>;

或直接写:

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

(2) CSS 类名(className)​

javascript 复制代码
// App.css
.container {
  background: #f0f0f0;
}

// App.jsx
import "./App.css";

const element = <div className="container">内容</div>;

5、JSX 中的条件渲染​​

​​(1) 三元运算符​

javascript 复制代码
const isLoggedIn = true;

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

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

javascript 复制代码
const hasMessages = true;

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

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

javascript 复制代码
function renderContent(isAdmin) {
  if (isAdmin) {
    return <AdminPanel />;
  } else {
    return <UserPanel />;
  }
}

const element = <div>{renderContent(true)}</div>;

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

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

javascript 复制代码
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)。

传递函数,而不是字符串。

基础实现:

javascript 复制代码
const handleClick = () => {
  alert("按钮被点击了!");
};

const element = <button onClick={handleClick}>点击我</button>;

使用事件参数:

javascript 复制代码
const handleClick = (e) => {
  alert("按钮被点击了!", e);
};

const element = <button onClick={handleClick}>点击我</button>;

传递自定义参数​:

javascript 复制代码
const deleteUser = (userId) => {
  console.log("删除用户", userId);
};

const userList = users.map((user) => (
  <button onClick={() => deleteUser(user.id)}>删除 {user.name}</button>
));

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

javascript 复制代码
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 中的注释

javascript 复制代码
{/* 注释 */}

三、React组件基础使用

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

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

​​(1) 函数组件(推荐)​​

使用 JavaScript 函数定义,返回 JSX。

​​适用于简单、无状态逻辑的组件​​。

javascript 复制代码
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

// 使用组件
<Welcome name="Alice" />

(2) 类组件(旧版写法)​​

使用 ES6 class 继承 React.Component。

​​适用于需要生命周期或状态管理的组件​​(现代 React 推荐用 Hooks 替代)。

javascript 复制代码
class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

// 使用组件
<Welcome name="Bob" />

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

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

(1) 传递 props

javascript 复制代码
function UserCard(props) {
  return (
    <div>
      <h2>{props.name}</h2>
      <p>年龄:{props.age}</p>
    </div>
  );
}

// 使用组件
<UserCard name="Alice" age={25} />

(2) 解构 props(推荐)​

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

(3) 默认 props

javascript 复制代码
function UserCard({ name, age = 18 }) {
  // 如果未传递 age,默认为 18
  return <p>{name} 年龄:{age}</p>;
}

// 或者使用 defaultProps(类组件)
UserCard.defaultProps = {
  age: 18,
};

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

javascript 复制代码
function Card({ children }) {
  return <div className="card">{children}</div>;
}

// 使用
<Card>
  <h3>标题</h3>
  <p>内容</p>
</Card>

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

state 是组件内部的可变数据,​​修改状态会触发重新渲染​​。

(1) 函数组件:useState Hook​

javascript 复制代码
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>
  );
}

修改对象状态:

javascript 复制代码
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

javascript 复制代码
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

javascript 复制代码
function Parent() {
  const message = "Hello from Parent";
  return <Child msg={message} />;
}

function Child({ msg }) {
  return <p>{msg}</p>;
}

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

javascript 复制代码
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 传递数据和回调函数】:

javascript 复制代码
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 访问】:

javascript 复制代码
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 实现灵活组合:

javascript 复制代码
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)​

javascript 复制代码
function withLogger(WrappedComponent) {
  return function (props) {
    console.log("组件渲染:", props);
    return <WrappedComponent {...props} />;
  };
}

const EnhancedComponent = withLogger(MyComponent);

(3) 自定义 Hook​

javascript 复制代码
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 事件更新。

javascript 复制代码
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 获取值。

javascript 复制代码
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 基本语法​

javascript 复制代码
import { useEffect } from 'react';

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

参数说明​:

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

2、useEffect 的 3 种使用方式​

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

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

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

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

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

适用场景​​:

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

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

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

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

javascript 复制代码
const [count, setCount] = useState(0);

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

适用场景​​:

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

示例:监听输入框变化​

javascript 复制代码
const [keyword, setKeyword] = useState('');

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

3、useEffect 的清理机制​​

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

示例 1:清除定时器​

javascript 复制代码
useEffect(() => {
  const timer = setInterval(() => {
    console.log('每秒执行');
  }, 1000);

  return () => {
    clearInterval(timer); // 组件卸载时清除定时器
  };
}, []);

示例 2:取消事件监听​

javascript 复制代码
useEffect(() => {
  const handleClick = () => console.log('点击了窗口');
  window.addEventListener('click', handleClick);

  return () => {
    window.removeEventListener('click', handleClick); // 清理事件
  };
}, []);

4、useEffect 常见使用场景​

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

javascript 复制代码
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) 订阅外部数据源​

javascript 复制代码
useEffect(() => {
  const subscription = dataSource.subscribe((data) => {
    setData(data);
  });

  return () => {
    subscription.unsubscribe(); // 清理订阅
  };
}, []);

(3) 手动操作 DOM​

javascript 复制代码
useEffect(() => {
  const button = document.getElementById('my-button');
  button.addEventListener('click', handleClick);

  return () => {
    button.removeEventListener('click', handleClick);
  };
}, []);

5、useEffect 的注意事项​

(1) 避免无限循环​​

​​错误示例​​:

javascript 复制代码
const [count, setCount] = useState(0);

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

修复方式​​:

确保依赖项正确

使用函数式更新(避免直接依赖 count)

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

(2) 依赖项遗漏导致的问题​

如果 effect 使用了某个变量但没有声明为依赖项,可能导致逻辑错误:

javascript 复制代码
const [count, setCount] = useState(0);

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

修复方式​​:

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

(3) useEffect 与 useLayoutEffect 的区别

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

五、React路由

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

1、安装与基础配置​​

​​(1) 安装​

bash 复制代码
npm install react-router-dom

(2) 基本路由结构​

javascript 复制代码
// 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) 路由定义​
javascript 复制代码
<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) 导航组件​

javascript 复制代码
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) 路径参数​

javascript 复制代码
// 路由定义
<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) 查询参数​

javascript 复制代码
// 跳转时传递
navigate(`/search?query=${keyword}`);

// 组件中获取
import { useSearchParams } from 'react-router-dom';

function SearchPage() {
  const [searchParams] = useSearchParams();
  const query = searchParams.get('query'); // 获取 ?query=xxx
}

(3) 状态参数​

javascript 复制代码
// 跳转时传递
navigate('/profile', { state: { fromHome: true } });

// 组件中获取
import { useLocation } from 'react-router-dom';

function ProfilePage() {
  const { state } = useLocation(); // 获取 { fromHome: true }
}

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

​​(1) 共享布局​

javascript 复制代码
// 定义带布局的路由
<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) 多布局入口​

javascript 复制代码
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>
  );
}

对象配置方式:

javascript 复制代码
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) 高阶组件模式​

javascript 复制代码
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) 包装器组件模式​

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

6、代码分割与懒加载​

javascript 复制代码
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配置方式:

javascript 复制代码
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>
  );
}

使用对象配置方式:

javascript 复制代码
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 字段:

javascript 复制代码
// action 类型常量(避免拼写错误)
const ADD_TODO = 'ADD_TODO';

// action 创建函数(Action Creator)
const addTodo = (text) => ({
  type: ADD_TODO,
  payload: { text }
});

​​(2) Reducer​​

纯函数,接收当前 state 和 action,返回新状态:

javascript 复制代码
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 创建,提供核心方法:

javascript 复制代码
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) 安装依赖​

bash 复制代码
npm install redux react-redux

(2) 创建 Redux Store​

javascript 复制代码
// store.js
import { createStore } from 'redux';
import rootReducer from './reducers';

const store = createStore(rootReducer);
export default store;

(3) 使用 Provider 注入 Store​

javascript 复制代码
// 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)​

javascript 复制代码
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)​

javascript 复制代码
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) 安装中间件​

bash 复制代码
npm install redux-thunk

​​(2) 配置 Store​

javascript 复制代码
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';

const store = createStore(rootReducer, applyMiddleware(thunk));

(3) 编写异步 Action​

javascript 复制代码
// 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) 组件中调用​

javascript 复制代码
function TodoApp() {
  const dispatch = useDispatch();

  useEffect(() => {
    dispatch(fetchTodos());
  }, []);

  // ...
}

5、Redux 最佳实践​​

​​(1) 项目结构​

javascript 复制代码
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)​

javascript 复制代码
export const ADD_TODO = 'ADD_TODO';
export const FETCH_TODOS = 'FETCH_TODOS';

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

javascript 复制代码
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)​

javascript 复制代码
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)​

javascript 复制代码
import { combineReducers } from 'redux';
import todoReducer from './todoReducer';

export default combineReducers({
  todos: todoReducer
});

创建 Store (store/index.js)​

javascript 复制代码
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)​

javascript 复制代码
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​

javascript 复制代码
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 代码的官方工具库

bash 复制代码
npm install @reduxjs/toolkit

重构 Store (store/index.js)​

javascript 复制代码
import { configureStore } from '@reduxjs/toolkit';
import todoReducer from './reducers/todoReducer';

export default configureStore({
  reducer: {
    todos: todoReducer
  }
});

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

javascript 复制代码
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) 安装​

bash 复制代码
npm install zustand

​​(2) 创建 Store​

javascript 复制代码
// 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) 在组件中使用​

javascript 复制代码
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) 状态更新​​

​​直接更新​​:

javascript 复制代码
set({ count: 10 }); // 直接替换

基于旧状态更新​​:

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

(2) 异步操作​

javascript 复制代码
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) 计算属性

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

3、性能优化​​

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

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

(2) 浅比较

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

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

bash 复制代码
npm install immer
javascript 复制代码
import { produce } from 'immer';

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

4、高级用法​​

​​(1) 持久化

bash 复制代码
npm install zustand/middleware
javascript 复制代码
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) 中间件

javascript 复制代码
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​

javascript 复制代码
const useCombinedStore = create((...a) => ({
  ...useCounterStore(...a),
  ...useUserStore(...a),
}));

(4) 完整示例​

javascript 复制代码
// 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;
相关推荐
deli00714 分钟前
1 个汉字算几个 token?实测中英文 prompt 与代码的编码差异
前端
无限聚核21 分钟前
抖音 certificate.prepare 之后怎么验券?和美团 msconsume 差在哪,无限聚核
前端·微信小程序
2603_9658966230 分钟前
Grid 网格布局|CSS 二维页面排版体系
前端·css
有味道的男人1 小时前
搜好货平台关键词商品搜索接口:分页容错与商品数据轻量化提取方案
服务器·前端·javascript
lovingsoft1 小时前
Chrome Lighthouse 详解
前端·chrome
Qlly2 小时前
全栈工程师怎么接需求?从一句「加个功能」聊到能开发、能验收
前端·后端
马可家的菠萝2 小时前
你的微信“文件传输助手”,是不是已经成了第二个网盘?
前端·后端·架构
略略略啦2 小时前
Vibe Coding 基于Spec DD和TDD的实践指南
前端
shmily麻瓜小菜鸡2 小时前
前端实现图片与PDF文字识别敏感信息打码方案
前端·pdf·状态模式
打工仔折腾 AI2 小时前
LangChain 工具调用实战:从 Tool 定义到 Agent 执行链路拆解
java·前端·人工智能·后端·python·langchain·ai agent 实战