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;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

testleaf

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值