React是由Facebook开发的用于构建用户界面的JavaScript库。它采用了组件化的开发思想,允许开发者将复杂的UI拆分成独立、可复用的代码片段,每个组件维护自己的状态和逻辑。React的核心特点包括虚拟DOM、单向数据流、声明式编程等,这些特性使其成为现代Web开发中最受欢迎的前端框架之一。
React最初由Jordan Walke在2013年开源,最初用于Facebook的News Feed产品。经过多年的发展,React已经成为前端开发领域的主流技术,被广泛应用于各种规模的Web应用中。许多知名公司如Netflix、Airbnb、Uber、Twitter等都采用React构建其产品。
React的主要优势在于其高效的渲染机制和灵活的组件化架构。虚拟DOM技术使得React能够在不直接操作真实DOM的情况下,通过对比前后状态的差异来最小化DOM操作,从而大幅提升应用性能。组件化的开发模式则提高了代码的可维护性和可测试性。
| 概念 | 说明 |
|---|---|
| 组件(Component) | UI的独立可复用构建块 |
| JSX | JavaScript的语法扩展,用于描述UI |
| Props | 父组件向子组件传递数据 |
| State | 组件内部的响应式数据 |
| 虚拟DOM | 内存中的DOM表示 |
| 单向数据流 | 数据从父到子的流向 |
| 框架 | 特点 | 学习曲线 | 适用场景 |
|---|---|---|---|
| React | 灵活、组件化、生态丰富 | 中等 | 中大型应用 |
| Vue | 简单易学、文档友好 | 较平缓 | 中小型应用 |
| Angular | 完整解决方案、TypeScript优先 | 较陡 | 大型企业级应用 |
Create React App是官方推荐的React项目创建工具,提供了开箱即用的开发环境。
# 安装Node.js(如果未安装)
# 访问 https://nodejs.org 下载安装
# 创建React应用
npx create-react-app my-app
# 进入项目目录
cd my-app
# 启动开发服务器
npm start
npx是npm提供的工具,会临时下载并执行create-react-app包,无需全局安装。项目创建完成后,会自动配置好Webpack、Babel、ESLint等开发工具。
Vite是新一代构建工具,相比Create React App具有更快的启动和热更新速度。
# 使用Vite创建React项目
npm create vite@latest my-react-app -- --template react
# 进入目录
cd my-react-app
# 安装依赖
npm install
# 启动开发服务器
npm run dev
Vite利用浏览器原生ES模块支持,实现了即时的服务器启动和热模块替换(HMR),在大型项目中性能优势明显。
my-app/
├── public/ # 静态资源
│ ├── index.html
│ └── favicon.ico
├── src/ # 源代码
│ ├── App.js # 根组件
│ ├── App.css # 组件样式
│ ├── index.js # 入口文件
│ └── index.css # 全局样式
├── package.json # 项目配置
└── README.md # 项目说明
JSX是JavaScript的语法扩展,允许在JavaScript中编写类似HTML的标记。它看起来像HTML,但实际上是一种在JavaScript中描述UI结构的语法。JSX不是模板语言,它会被编译成普通的JavaScript函数调用。
// JSX示例
const element = (
<div className="container">
<h1>Hello, World!</h1>
<p>Welcome to React</p>
</div>
);
JSX中的元素必须闭合,自闭合标签需要以/>结尾。与HTML不同的是,JSX中使用className而不是class,因为class是JavaScript的保留字。
JSX中可以嵌入任何JavaScript表达式,使用花括号包裹。
const name = "张三";
const age = 25;
// 嵌入变量
const element = (
<div>
<h1>姓名:{name}</h1>
<h2>年龄:{age}</h2>
<h3>两年后:{age + 2}</h3>
</div>
);
// 嵌入函数调用
function formatName(user) {
return user.firstName + " " + user.lastName;
}
const user = { firstName: "李", lastName: "四" };
const element = <h1>Hello, {formatName(user)}!</h1>;
// 字符串属性
const element = <div className="container" id="main"></div>;
// JavaScript表达式属性
const isActive = true;
const element = <button className={isActive ? "active" : "inactive"}>点击</button>;
// 展开属性
const props = { className: "btn", onClick: handleClick };
const element = <button {...props}>提交</button>;
// if语句
function Greeting({ isLoggedIn }) {
if (isLoggedIn) {
return <h1>欢迎回来</h1>;
}
return <h1>请登录</h1>;
}
// 三元运算符
function LoginButton({ isLoggedIn }) {
return (
<button>
{isLoggedIn ? "退出" : "登录"}
</button>
);
}
// 逻辑与运算符
function Warning({ hasWarning }) {
return (
<div>
{hasWarning && <p className="warning">注意!</p>}
</div>
);
}
函数组件是现代React开发中推荐的方式,使用JavaScript函数定义组件。
// 简单的函数组件
function Welcome({ name }) {
return <h1>Hello, {name}!</h1>;
}
// 箭头函数组件
const Greeting = ({ name }) => {
return <h1>Hello, {name}!</h1>;
};
// 使用组件
function App() {
return (
<div>
<Welcome name="张三" />
<Welcome name="李四" />
</div>
);
}
函数组件本质是一个接收props对象并返回JSX的JavaScript函数。它们比类组件更简洁,现代React推荐优先使用函数组件配合Hooks。
类组件使用ES6的class语法,需要继承React.Component。
import React from "react";
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}!</h1>;
}
}
// 使用
function App() {
return <Welcome name="张三" />;
}
类组件具有以下特点:需要继承React.Component、具有自己的state、具有生命周期方法、需要定义render方法返回JSX。
// 内联样式
const style = {
color: "blue",
fontSize: "16px"
};
function StyledComponent() {
return <p style={style}>内联样式</p>;
}
// CSS模块(文件名格式:Component.module.css)
// import styles from "./Button.module.css";
// <button className={styles.button}>点击</button>
// CSS-in-JS(styled-components库)
import styled from "styled-components";
const Button = styled.button`
background: #4CAF50;
color: white;
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
&:hover {
background: #45a049;
}
`;
function App() {
return <Button>点击</Button>;
}
Props(属性)是父组件向子组件传递数据的机制。Props是只读的,子组件不应修改props的值。
// 父组件
function Parent() {
return <Child name="张三" age={25} isActive={true} />;
}
// 子组件
function Child({ name, age, isActive }) {
return (
<div className={isActive ? "active" : "inactive"}>
<h2>{name}</h2>
<p>年龄:{age}</p>
</div>
);
}
// Props类型检查
import PropTypes from "prop-types";
function Child({ name, age }) {
return <div>{name} - {age}</div>;
}
Child.propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.number
};
Child.defaultProps = {
age: 18
};
State是组件内部的响应式数据,用于存储组件运行时可能变化的数据。与props不同,state是组件私有的,由组件自己管理。
import React, { useState } from "react";
function Counter() {
// 声明state
const [count, setCount] = useState(0);
// 更新state
const increment = () => {
setCount(count + 1);
};
return (
<div>
<p>计数:{count}</p>
<button onClick={increment}>增加</button>
</div>
);
}
useState是React Hook,返回一个状态值和更新该状态的函数。调用setCount会触发组件重新渲染,显示最新的计数值。
| 特性 | State | Props |
|---|---|---|
| 所有者 | 组件自身 | 父组件 |
| 可变性 | 可变 | 只读 |
| 触发渲染 | 变化时重新渲染 | 变化时重新渲染 |
| 初始值 | 构造函数或useState | 父组件传递 |
React Hook是React 16.8引入的新特性,允许在函数组件中使用state和其他React特性。
| Hook | 用途 |
|---|---|
| useState | 管理组件状态 |
| useEffect | 处理副作用(数据获取、订阅等) |
| useContext | 访问Context |
| useReducer | 复杂状态管理 |
| useRef | 引用DOM或保存值 |
| useMemo | 缓存计算结果 |
| useCallback | 缓存函数 |
useEffect用于处理副作用操作,类似于类组件的生命周期方法。
import React, { useState, useEffect } from "react";
function DataFetcher({ url }) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
// 组件挂载时执行
const fetchData = async () => {
try {
setLoading(true);
const response = await fetch(url);
const json = await response.json();
setData(json);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchData();
// 返回清理函数(组件卸载时执行)
return () => {
console.log("清理副作用");
};
}, [url]); // 依赖数组,url变化时重新执行
if (loading) return <p>加载中...</p>;
if (error) return <p>错误:{error}</p>;
return <div>{JSON.stringify(data)}</div>;
}
useEffect接收两个参数:副作用函数和依赖数组。依赖数组控制effect何时重新执行,空数组表示只在挂载时执行一次,类似componentDidMount。
useRef返回一个可变的ref对象,其.current属性可以存储任意值。
import React, { useRef } from "react";
function TextInput() {
const inputRef = useRef(null);
const handleFocus = () => {
// 聚焦输入框
inputRef.current.focus();
};
const handleGetValue = () => {
console.log(inputRef.current.value);
};
return (
<div>
<input ref={inputRef} type="text" />
<button onClick={handleFocus}>聚焦</button>
<button onClick={handleGetValue}>获取值</button>
</div>
);
}
useRef还常用于保存不需要触发重新渲染的值,比如计时器ID。
function EventExample() {
const handleClick = (event) => {
console.log("点击事件", event);
};
const handleClickWithParam = (message) => {
console.log(message);
};
return (
<div>
{/* 直接绑定 */}
<button onClick={handleClick}>点击</button>
{/* 传递额外参数 */}
<button onClick={() => handleClickWithParam("Hello")}>带参数</button>
{/* 传递事件对象和参数 */}
<button onClick={(e) => handleClickWithParam("World")}>参数2</button>
</div>
);
}
function FormEvents() {
const handleSubmit = (e) => {
e.preventDefault(); // 阻止表单默认提交
console.log("表单提交");
};
const handleChange = (e) => {
console.log("输入值:", e.target.value);
console.log("输入名称:", e.target.name);
};
const handleFocus = (e) => {
console.log("获得焦点");
};
const handleBlur = (e) => {
console.log("失去焦点");
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
name="username"
onChange={handleChange}
onFocus={handleFocus}
onBlur={handleBlur}
/>
<button type="submit">提交</button>
</form>
);
}
function UserProfile({ user, isLoading }) {
// 加载状态
if (isLoading) {
return <div>加载中...</div>;
}
// 无用户
if (!user) {
return <div>请登录</div>;
}
// 正常显示
return (
<div>
<h2>{user.name}</h2>
<p>{user.email}</p>
</div>
);
}
// 使用&&进行条件渲染
function Warning({ hasWarning }) {
return (
<div>
<h1>标题</h1>
{hasWarning && <div className="warning">警告信息</div>}
</div>
);
}
function TodoList() {
const [todos, setTodos] = useState([
{ id: 1, text: "学习React", completed: false },
{ id: 2, text: "做项目", completed: true },
{ id: 3, text: "写文档", completed: false }
]);
const toggleTodo = (id) => {
setTodos(todos.map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
));
};
return (
<ul>
{todos.map(todo => (
<li
key={todo.id}
style={{
textDecoration: todo.completed ? "line-through" : "none"
}}
onClick={() => toggleTodo(todo.id)}
>
{todo.text}
</li>
))}
</ul>
);
}
列表渲染时,每个列表项都需要一个唯一的key属性,帮助React识别哪些元素发生了变化。key应该是稳定且唯一的,通常使用数据的ID。
受控组件的值由React state控制,每个输入都有对应的状态更新逻辑。
import React, { useState } from "react";
function LoginForm() {
const [formData, setFormData] = useState({
username: "",
password: "",
remember: false
});
const handleChange = (e) => {
const { name, value, type, checked } = e.target;
setFormData(prev => ({
...prev,
[name]: type === "checkbox" ? checked : value
}));
};
const handleSubmit = (e) => {
e.preventDefault();
console.log("表单数据:", formData);
};
return (
<form onSubmit={handleSubmit}>
<div>
<label>用户名:</label>
<input
type="text"
name="username"
value={formData.username}
onChange={handleChange}
/>
</div>
<div>
<label>密码:</label>
<input
type="password"
name="password"
value={formData.password}
onChange={handleChange}
/>
</div>
<div>
<label>
<input
type="checkbox"
name="remember"
checked={formData.remember}
onChange={handleChange}
/>
记住我
</label>
</div>
<button type="submit">登录</button>
</form>
);
}
function ValidatedForm() {
const [formData, setFormData] = useState({ email: "" });
const [errors, setErrors] = useState({});
const validate = () => {
const newErrors = {};
if (!formData.email) {
newErrors.email = "邮箱不能为空";
} else if (!/\S+@\S+\.\S+/.test(formData.email)) {
newErrors.email = "邮箱格式不正确";
}
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};
const handleSubmit = (e) => {
e.preventDefault();
if (validate()) {
console.log("提交成功", formData);
}
};
const handleChange = (e) => {
setFormData({ [e.target.name]: e.target.value });
// 清除错误
if (errors[e.target.name]) {
setErrors({ ...errors, [e.target.name]: "" });
}
};
return (
<form onSubmit={handleSubmit}>
<div>
<input
name="email"
value={formData.email}
onChange={handleChange}
/>
{errors.email && <span className="error">{errors.email}</span>}
</div>
<button type="submit">提交</button>
</form>
);
}
// 父组件
function Parent() {
const [message, setMessage] = useState("Hello from parent");
return (
<div>
<h1>父组件</h1>
<Child
message={message}
onMessageChange={setMessage}
/>
</div>
);
}
// 子组件
function Child({ message, onMessageChange }) {
return (
<div>
<p>收到消息:{message}</p>
<button onClick={() => onMessageChange("Hello from child")}>
发消息给父组件
</button>
</div>
);
}
Context提供了一种在组件树中传递数据的方式,避免层层传递props。
import React, { createContext, useContext, useState } from "react";
// 创建Context
const ThemeContext = createContext();
function App() {
const [theme, setTheme] = useState("light");
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
<div className={`app ${theme}`}>
<Header />
<Content />
</div>
</ThemeContext.Provider>
);
}
function Header() {
const { theme, setTheme } = useContext(ThemeContext);
return (
<header>
<p>当前主题:{theme}</p>
<button onClick={() => setTheme(theme === "light" ? "dark" : "light")}>
切换主题
</button>
</header>
);
}
function Content() {
const { theme } = useContext(ThemeContext);
return (
<main>
<p>主题内容:{theme}</p>
</main>
);
}
| 方式 | 适用场景 |
|---|---|
| Props | 父子组件直接通信 |
| Context | 跨多层组件共享数据 |
| 状态管理库 | 复杂应用全局状态管理 |
| 事件总线 | 非父子关系的组件通信 |
# 安装React Router
npm install react-router-dom
import React from "react";
import { BrowserRouter, Routes, Route, Link } from "react-router-dom";
function Home() {
return <h1>首页</h1>;
}
function About() {
return <h1>关于</h1>;
}
function User({ match }) {
return <h1>用户ID: {match.params.id}</h1>;
}
function App() {
return (
<BrowserRouter>
<nav>
<Link to="/">首页</Link>
<Link to="/about">关于</Link>
<Link to="/user/123">用户</Link>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/user/:id" element={<User />} />
</Routes>
</BrowserRouter>
);
}
import { Navigate } from "react-router-dom";
function PrivateRoute({ isAuthenticated, children }) {
return isAuthenticated ? children : <Navigate to="/login" />;
}
// 使用
<Route
path="/dashboard"
element={
<PrivateRoute isAuthenticated={isLoggedIn}>
<Dashboard />
</PrivateRoute>
}
/>
Redux是JavaScript应用的可预测状态容器,常用于React应用的状态管理。
# 安装Redux和React Redux
npm install @reduxjs/toolkit react-redux
import { configureStore, createSlice } from "@reduxjs/toolkit";
// 创建slice
const counterSlice = createSlice({
name: "counter",
initialState: { value: 0 },
reducers: {
increment: (state) => {
state.value += 1;
},
decrement: (state) => {
state.value -= 1;
},
incrementByAmount: (state, action) => {
state.value += action.payload;
}
}
});
export const { increment, decrement, incrementByAmount } = counterSlice.actions;
// 创建store
const store = configureStore({
reducer: {
counter: counterSlice.reducer
}
});
// Provider
import { Provider } from "react-redux";
function App() {
return (
<Provider store={store}>
<Counter />
</Provider>
);
}
// 组件中使用
import { useSelector, useDispatch } from "react-redux";
function Counter() {
const count = useSelector((state) => state.counter.value);
const dispatch = useDispatch();
return (
<div>
<p>计数:{count}</p>
<button onClick={() => dispatch(increment())}>+</button>
<button onClick={() => dispatch(decrement())}>-</button>
</div>
);
}
| 方案 | 特点 | 适用场景 |
|---|---|---|
| useState | 简单直接 | 组件局部状态 |
| useReducer | 复杂逻辑 | 组件内复杂状态 |
| Context | 数据共享 | 主题、国际化等 |
| Redux | 强大生态 | 大型复杂应用 |
| Zustand | 轻量简单 | 中型应用 |
| Jotai | 原子化状态 | 灵活状态管理 |
useReducer适用于复杂的状态逻辑,类似于Redux的单组件版本。
import React, { useReducer } from "react";
const initialState = { count: 0, status: "idle" };
function reducer(state, action) {
switch (action.type) {
case "increment":
return { ...state, count: state.count + 1 };
case "decrement":
return { ...state, count: state.count - 1 };
case "reset":
return initialState;
case "loading":
return { ...state, status: "loading" };
case "success":
return { ...state, status: "success" };
default:
return state;
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<p>计数:{state.count}</p>
<p>状态:{state.status}</p>
<button onClick={() => dispatch({ type: "increment" })}>+</button>
<button onClick={() => dispatch({ type: "decrement" })}>-</button>
<button onClick={() => dispatch({ type: "reset" })}>重置</button>
</div>
);
}
import React, { useMemo, useCallback } from "react";
function ExpensiveComponent({ items, filter }) {
// 缓存计算结果
const filteredItems = useMemo(() => {
console.log("计算过滤");
return items.filter(item => item.name.includes(filter));
}, [items, filter]);
// 缓存函数
const handleClick = useCallback((id) => {
console.log("点击:", id);
}, []);
return (
<ul>
{filteredItems.map(item => (
<li key={item.id} onClick={() => handleClick(item.id)}>
{item.name}
</li>
))}
</ul>
);
}
useMemo缓存计算结果,避免不必要的重新计算。useCallback缓存函数引用,防止子组件不必要的重新渲染。
| 原则 | 说明 |
|---|---|
| 单一职责 | 每个组件只负责一个功能 |
| 高内聚低耦合 | 组件内部紧密相关,组件间相互独立 |
| 可复用性 | 通过props配置实现不同表现 |
| 可测试性 | 组件逻辑清晰,易于单元测试 |
// 1. 使用React.memo避免不必要的渲染
const MemoizedComponent = React.memo(function MyComponent({ data }) {
return <div>{data.name}</div>;
});
// 2. 使用useMemo和useCallback
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
const memoizedCallback = useCallback(() => doSomething(a), [a]);
// 3. 代码分割
import { lazy, Suspense } from "react";
const LazyComponent = lazy(() => import("./HeavyComponent"));
function App() {
return (
<Suspense fallback={<Loading />}>
<LazyComponent />
</Suspense>
);
}
// 4. 虚拟列表(处理大量数据)
import { FixedSizeList } from "react-window";
function BigList() {
return (
<FixedSizeList
height={400}
itemCount={10000}
itemSize={50}
>
{({ index, style }) => (
<div style={style}>Item {index}</div>
)}
</FixedSizeList>
);
}
| 错误 | 正确做法 |
|---|---|
| 直接修改state | 使用setState方法 |
| 缺少依赖数组 | 添加正确的依赖 |
| 内联函数作为props | 使用useCallback缓存 |
| 不使用key | 使用稳定的唯一key |
本教程系统介绍了React开发的各个方面,从基础概念到高级应用。主要内容包括:React核心概念和特点、JSX语法和组件开发、Props和State管理、React Hooks的深入使用、事件处理和表单操作、组件通信方式、React Router路由配置、Redux状态管理,以及性能优化技巧。React采用组件化的开发模式,配合Hooks和虚拟DOM,能够构建高性能的现代Web应用。掌握这些知识后,您可以独立开发功能完整的React应用。