最后更新于:2026年7月

React 19 来了! 从 Server Components 到 Actions,从 useOptimistic 到新的 use Hook,React 19 带来了自 Hooks 以来最大的一次范式转变。配合 TypeScript 的类型安全,现代前端开发的效率和质量都上了一个新台阶。
但很多开发者还在用 React 17/18 的老思维写 React 19 的代码,不仅没有享受到新特性的便利,反而可能踩坑。本文从基础到进阶,带你系统掌握 React 19 + TypeScript 的最佳实践。
一、React 19 新特性详解
1.1 React Server Components (RSC)
什么是 Server Components?
Server Components 是在服务器端渲染的 React 组件,它们的代码不会发送到客户端,可以:
- 直接访问后端资源(数据库、文件系统)
- 不增加客户端 JS 包大小
- 自动进行代码分割
Client vs Server 组件对比:
| 特性 | Server Components | Client Components |
|---|---|---|
| 运行环境 | 服务器 | 浏览器 |
| 交互能力 | ❌ 不能用 useState、useEffect | ✅ 可以 |
| 访问后端 | ✅ 直接访问数据库/API | ❌ 需要通过 API |
| 包体积 | 不增加客户端包大小 | 增加 |
| 使用场景 | 数据获取、静态内容 | 交互、状态管理 |
使用方式:
// 默认是 Server Component(不需要 'use client')
export default function BlogPosts() {
// 直接在组件中获取数据
const posts = await db.posts.findMany();
return (
<div>
<h1>博客文章</h1>
{posts.map((post) => (
<PostCard key={post.id} post={post} />
))}
</div>
);
}
// Client Component 需要 'use client' 指令
'use client';
import { useState } from 'react';
export function LikeButton({ postId }: { postId: string }) {
const [likes, setLikes] = useState(0);
return (
<button onClick={() => setLikes(likes + 1)}>
👍 {likes}
</button>
);
}1.2 Actions
Actions 是 React 19 最重要的新特性之一,它让表单处理和数据变更变得更简单。
基础用法:
// Server Action(在 Server Component 中定义)
async function updateName(formData: FormData) {
'use server';
const name = formData.get('name') as string;
await db.user.update({ name });
}
export default function Profile() {
return (
<form action={updateName}>
<input type="text" name="name" />
<button type="submit">更新</button>
</form>
);
}配合 useActionState 使用:
'use client';
import { useActionState } from 'react';
async function updateName(prevState: { error?: string }, formData: FormData) {
'use server';
const name = formData.get('name') as string;
if (name.length < 2) {
return { error: '名字太短了' };
}
await db.user.update({ name });
return { error: undefined };
}
export default function ProfileForm() {
const [state, formAction, isPending] = useActionState(updateName, {});
return (
<form action={formAction}>
<input type="text" name="name" />
{state.error && <p style={{ color: 'red' }}>{state.error}</p>}
<button type="submit" disabled={isPending}>
{isPending ? '更新中...' : '更新'}
</button>
</form>
);
}1.3 useOptimistic Hook
乐观更新是提升用户体验的利器:
'use client';
import { useOptimistic, useState } from 'react';
type Message = {
id: string;
text: string;
sending?: boolean;
};
async function sendMessage(text: string): Promise<Message> {
// 模拟 API 调用
await new Promise((resolve) => setTimeout(resolve, 1000));
return { id: Date.now().toString(), text };
}
export function Chat() {
const [messages, setMessages] = useState<Message[]>([]);
const [optimisticMessages, addOptimistic] = useOptimistic(
messages,
(state, newMessage: string) => [
...state,
{ id: 'temp', text: newMessage, sending: true },
]
);
async function handleSubmit(formData: FormData) {
const text = formData.get('message') as string;
addOptimistic(text);
const message = await sendMessage(text);
setMessages((prev) => [...prev, message]);
}
return (
<div>
<div>
{optimisticMessages.map((msg) => (
<div key={msg.id} style={{ opacity: msg.sending ? 0.5 : 1 }}>
{msg.text} {msg.sending && '发送中...'}
</div>
))}
</div>
<form action={handleSubmit}>
<input type="text" name="message" />
<button type="submit">发送</button>
</form>
</div>
);
}1.4 use Hook
新的 use Hook 让读取异步资源更简单:
import { use } from 'react';
// 读取 Promise
function UserProfile({ userId }: { userId: string }) {
const user = use(fetchUser(userId)); // 自动处理 Suspense
return <div>{user.name}</div>;
}
// 读取 Context
function ThemeButton() {
const theme = use(ThemeContext);
return <button style={{ background: theme.primary }}>按钮</button>;
}
// 配合 Suspense 使用
function App() {
return (
<Suspense fallback={<Loading />}>
<UserProfile userId="1" />
</Suspense>
);
}1.5 其他重要新特性
useRef 初始值函数:
// 之前:初始值只在第一次渲染时计算,但语法不直观
const ref = useRef(createExpensiveObject());
// React 19:支持函数初始化,只执行一次
const ref = useRef(() => createExpensiveObject());forwardRef 已废弃,用 ref 作为 prop:
// 之前:forwardRef
const MyInput = forwardRef((props, ref) => {
return <input ref={ref} {...props} />;
});
// React 19:直接接收 ref prop
function MyInput({ ref, ...props }) {
return <input ref={ref} {...props} />;
}useEffect 清理函数返回值:
// 之前:清理函数不接收参数
useEffect(() => {
const controller = new AbortController();
fetchData(controller.signal);
return () => controller.abort();
}, []);
// React 19:清理函数可以接收一个参数(表示为什么清理)
useEffect(() => {
// ...
return (reason) => {
console.log('Cleanup reason:', reason); // 'unmount' | 'replay' | 'dep-change'
};
}, []);二、TypeScript 类型最佳实践
2.1 组件 Props 类型定义
基础用法:
interface ButtonProps {
text: string;
onClick?: () => void;
variant?: 'primary' | 'secondary' | 'danger';
disabled?: boolean;
}
function Button({
text,
onClick,
variant = 'primary',
disabled = false
}: ButtonProps) {
return (
<button
onClick={onClick}
disabled={disabled}
className={`btn btn-${variant}`}
>
{text}
</button>
);
}扩展原生 HTML 元素:
import type { ButtonHTMLAttributes } from 'react';
interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
variant?: 'primary' | 'secondary';
loading?: boolean;
}
function Button({
variant = 'primary',
loading = false,
children,
disabled,
...props
}: ButtonProps) {
return (
<button
className={`btn btn-${variant}`}
disabled={disabled || loading}
{...props}
>
{loading && <Spinner />}
{children}
</button>
);
}带 children 的组件:
import type { ReactNode } from 'react';
interface CardProps {
title: string;
children: ReactNode;
footer?: ReactNode;
}
function Card({ title, children, footer }: CardProps) {
return (
<div className="card">
<h3>{title}</h3>
<div className="card-body">{children}</div>
{footer && <div className="card-footer">{footer}</div>}
</div>
);
}2.2 useState 类型推断
自动推断:
// TypeScript 会自动推断类型
const [count, setCount] = useState(0); // number
const [name, setName] = useState(''); // string
const [user, setUser] = useState({ name: 'Tom', age: 20 }); // { name: string; age: number }
显式指定类型(当初始值为 null 或 undefined 时):
interface User {
id: string;
name: string;
email: string;
}
// 可能为 null
const [user, setUser] = useState<User | null>(null);
// 可能为 undefined
const [selectedId, setSelectedId] = useState<string | undefined>();函数式更新:
interface Todo {
id: string;
text: string;
completed: boolean;
}
const [todos, setTodos] = useState<Todo[]>([]);
function toggleTodo(id: string) {
setTodos((prev) =>
prev.map((todo) =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
)
);
}2.3 useReducer 类型
interface TodoState {
todos: Todo[];
filter: 'all' | 'active' | 'completed';
}
type TodoAction =
| { type: 'ADD_TODO'; payload: string }
| { type: 'TOGGLE_TODO'; payload: string }
| { type: 'DELETE_TODO'; payload: string }
| { type: 'SET_FILTER'; payload: TodoState['filter'] };
function todoReducer(state: TodoState, action: TodoAction): TodoState {
switch (action.type) {
case 'ADD_TODO':
return {
...state,
todos: [...state.todos, { id: Date.now().toString(), text: action.payload, completed: false }],
};
case 'TOGGLE_TODO':
return {
...state,
todos: state.todos.map((todo) =>
todo.id === action.payload ? { ...todo, completed: !todo.completed } : todo
),
};
case 'DELETE_TODO':
return {
...state,
todos: state.todos.filter((todo) => todo.id !== action.payload),
};
case 'SET_FILTER':
return { ...state, filter: action.payload };
default:
return state;
}
}
function TodoApp() {
const [state, dispatch] = useReducer(todoReducer, {
todos: [],
filter: 'all',
});
// ...
}2.4 Context 类型定义
import { createContext, useContext, useState, type ReactNode } from 'react';
interface Theme {
primary: string;
secondary: string;
mode: 'light' | 'dark';
}
interface ThemeContextType {
theme: Theme;
toggleTheme: () => void;
}
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
function ThemeProvider({ children }: { children: ReactNode }) {
const [theme, setTheme] = useState<Theme>({
primary: '#007bff',
secondary: '#6c757d',
mode: 'light',
});
const toggleTheme = () => {
setTheme((prev) => ({
...prev,
mode: prev.mode === 'light' ? 'dark' : 'light',
}));
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}
function useTheme() {
const context = useContext(ThemeContext);
if (context === undefined) {
throw new Error('useTheme must be used within a ThemeProvider');
}
return context;
}
// 使用
function MyComponent() {
const { theme, toggleTheme } = useTheme();
return <button onClick={toggleTheme}>切换主题</button>;
}2.5 自定义 Hook 类型
import { useState, useEffect } from 'react';
interface UseFetchResult<T> {
data: T | null;
loading: boolean;
error: Error | null;
refetch: () => void;
}
function useFetch<T>(url: string): UseFetchResult<T> {
const [data, setData] = useState<T | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
const fetchData = async () => {
setLoading(true);
setError(null);
try {
const res = await fetch(url);
const json = await res.json();
setData(json);
} catch (e) {
setError(e as Error);
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchData();
}, [url]);
return { data, loading, error, refetch: fetchData };
}
// 使用
function UserProfile({ userId }: { userId: string }) {
const { data: user, loading, error } = useFetch<User>(`/api/users/${userId}`);
if (loading) return <div>加载中...</div>;
if (error) return <div>出错了:{error.message}</div>;
if (!user) return <div>用户不存在</div>;
return <div>{user.name}</div>;
}三、状态管理方案选择
3.1 各方案对比
| 方案 | 适用场景 | 学习曲线 | 包体积 | 推荐度 |
|---|---|---|---|---|
| useState + Props | 小型应用、简单状态 | 低 | 0 | ⭐⭐⭐ |
| useReducer + Context | 中型应用、全局状态 | 中 | 0 | ⭐⭐⭐⭐ |
| Zustand | 中小应用、轻量状态 | 低 | 小 | ⭐⭐⭐⭐⭐ |
| Jotai | 原子化状态、细粒度更新 | 中 | 小 | ⭐⭐⭐⭐ |
| Redux Toolkit | 大型应用、复杂状态 | 高 | 大 | ⭐⭐⭐ |
| TanStack Query | 服务端状态、数据缓存 | 中 | 中 | ⭐⭐⭐⭐⭐ |
3.2 Zustand 最佳实践
import { create } from 'zustand';
import { devtools, persist } from 'zustand/middleware';
interface Bear {
id: string;
name: string;
}
interface BearState {
bears: Bear[];
addBear: (name: string) => void;
removeBear: (id: string) => void;
clearBears: () => void;
}
const useBearStore = create<BearState>()(
devtools(
persist(
(set) => ({
bears: [],
addBear: (name) =>
set((state) => ({
bears: [...state.bears, { id: Date.now().toString(), name }],
})),
removeBear: (id) =>
set((state) => ({
bears: state.bears.filter((bear) => bear.id !== id),
})),
clearBears: () => set({ bears: [] }),
}),
{ name: 'bear-storage' }
)
)
);
// 使用
function BearCounter() {
const bears = useBearStore((state) => state.bears);
return <h1>{bears.length} 只熊</h1>;
}
function BearControls() {
const addBear = useBearStore((state) => state.addBear);
const removeBear = useBearStore((state) => state.removeBear);
return (
<div>
<button onClick={() => addBear('新熊')}>添加熊</button>
</div>
);
}3.3 TanStack Query(React Query)最佳实践
import {
useQuery,
useMutation,
useQueryClient,
type UseQueryResult
} from '@tanstack/react-query';
interface User {
id: string;
name: string;
email: string;
}
// 查询 Hook
function useUser(userId: string): UseQueryResult<User, Error> {
return useQuery({
queryKey: ['user', userId],
queryFn: async () => {
const res = await fetch(`/api/users/${userId}`);
if (!res.ok) throw new Error('获取用户失败');
return res.json();
},
enabled: !!userId,
staleTime: 5 * 60 * 1000, // 5 分钟内视为新鲜
});
}
// 变更 Hook
function useUpdateUser() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({ userId, data }: { userId: string; data: Partial<User> }) => {
const res = await fetch(`/api/users/${userId}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
if (!res.ok) throw new Error('更新失败');
return res.json();
},
onSuccess: (data, variables) => {
// 自动更新缓存
queryClient.setQueryData(['user', variables.userId], data);
},
});
}
// 使用
function UserProfile({ userId }: { userId: string }) {
const { data: user, isLoading, error } = useUser(userId);
const updateUser = useUpdateUser();
if (isLoading) return <div>加载中...</div>;
if (error) return <div>出错了</div>;
if (!user) return <div>用户不存在</div>;
return (
<div>
<h1>{user.name}</h1>
<button
onClick={() => updateUser.mutate({ userId, data: { name: '新名字' } })}
>
更新
</button>
</div>
);
}四、性能优化
4.1 渲染性能优化
memo 组件记忆化:
import { memo } from 'react';
interface TodoItemProps {
todo: Todo;
onToggle: (id: string) => void;
}
// 只有当 props 变化时才重新渲染
const TodoItem = memo(function TodoItem({ todo, onToggle }: TodoItemProps) {
console.log('TodoItem 渲染');
return (
<div>
<input
type="checkbox"
checked={todo.completed}
onChange={() => onToggle(todo.id)}
/>
{todo.text}
</div>
);
});useCallback 函数记忆化:
import { useCallback, useState } from 'react';
function TodoList() {
const [todos, setTodos] = useState<Todo[]>([]);
const [filter, setFilter] = useState<'all' | 'active' | 'completed'>('all');
// 只有 todos 变化时才重新创建函数
const handleToggle = useCallback((id: string) => {
setTodos((prev) =>
prev.map((todo) =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
)
);
}, []); // 空依赖,函数永远不变
const filteredTodos = todos.filter((todo) => {
if (filter === 'all') return true;
if (filter === 'active') return !todo.completed;
return todo.completed;
});
return (
<div>
{filteredTodos.map((todo) => (
<TodoItem key={todo.id} todo={todo} onToggle={handleToggle} />
))}
</div>
);
}useMemo 计算结果记忆化:
import { useMemo } from 'react';
function TodoStats({ todos }: { todos: Todo[] }) {
// 只有 todos 变化时才重新计算
const stats = useMemo(() => {
console.log('计算统计数据');
const total = todos.length;
const completed = todos.filter((t) => t.completed).length;
const active = total - completed;
const completionRate = total > 0 ? (completed / total) * 100 : 0;
return { total, completed, active, completionRate };
}, [todos]);
return (
<div>
<p>总计:{stats.total}</p>
<p>已完成:{stats.completed}</p>
<p>进行中:{stats.active}</p>
<p>完成率:{stats.completionRate.toFixed(1)}%</p>
</div>
);
}4.2 列表性能优化
key 的正确使用:
// ✅ 好:使用唯一 ID
{todos.map((todo) => (
<TodoItem key={todo.id} todo={todo} />
))}
// ❌ 不好:使用索引(当列表顺序会变化时)
{todos.map((todo, index) => (
<TodoItem key={index} todo={todo} />
))}虚拟滚动(长列表):
import { useVirtualizer } from '@tanstack/react-virtual';
function LongList({ items }: { items: Item[] }) {
const parentRef = useRef<HTMLDivElement>(null);
const virtualizer = useVirtualizer({
count: items.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 50,
overscan: 5,
});
return (
<div
ref={parentRef}
style={{
height: '600px',
overflow: 'auto',
}}
>
<div style={{ height: `${virtualizer.getTotalSize()}px`, position: 'relative' }}>
{virtualizer.getVirtualItems().map((virtualItem) => (
<div
key={virtualItem.key}
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: `${virtualItem.size}px`,
transform: `translateY(${virtualItem.start}px)`,
}}
>
{items[virtualItem.index].name}
</div>
))}
</div>
</div>
);
}4.3 代码分割与懒加载
import { lazy, Suspense } from 'react';
// 动态导入组件
const HeavyComponent = lazy(() => import('./HeavyComponent'));
const Dashboard = lazy(() => import('./Dashboard'));
function App() {
return (
<div>
<h1>我的应用</h1>
<Suspense fallback={<Loading />}>
<HeavyComponent />
</Suspense>
<Suspense fallback={<Loading />}>
<Dashboard />
</Suspense>
</div>
);
}
// 路由级代码分割
import { Routes, Route } from 'react-router-dom';
const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));
const Dashboard = lazy(() => import('./pages/Dashboard'));
function AppRouter() {
return (
<Suspense fallback={<Loading />}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/dashboard" element={<Dashboard />} />
</Routes>
</Suspense>
);
}五、项目架构与代码规范
5.1 推荐目录结构
src/
├── components/ # 通用组件
│ ├── ui/ # 基础 UI 组件(Button, Input...)
│ ├── layout/ # 布局组件(Header, Sidebar...)
│ └── features/ # 业务组件
├── pages/ # 页面组件
├── hooks/ # 自定义 Hook
├── stores/ # 状态管理(Zustand store)
├── services/ # API 服务
├── types/ # TypeScript 类型定义
├── utils/ # 工具函数
├── styles/ # 全局样式
├── assets/ # 静态资源
├── App.tsx
└── main.tsx5.2 组件开发规范
组件文件结构:
components/
UserCard/
UserCard.tsx # 主组件
UserCard.test.tsx # 测试
UserCard.stories.tsx # Storybook
index.ts # 导出组件编写顺序:
// 1. 导入
import { useState, useCallback } from 'react';
// 2. 类型定义
interface UserCardProps {
user: User;
onEdit?: (user: User) => void;
}
// 3. 组件函数
export function UserCard({ user, onEdit }: UserCardProps) {
// 4. State
const [isEditing, setIsEditing] = useState(false);
// 5. 计算属性
const fullName = `${user.firstName} ${user.lastName}`;
// 6. 事件处理
const handleEdit = useCallback(() => {
setIsEditing(true);
onEdit?.(user);
}, [user, onEdit]);
// 7. 渲染
return (
<div className="user-card">
<h3>{fullName}</h3>
<p>{user.email}</p>
<button onClick={handleEdit}>编辑</button>
</div>
);
}
// 8. 默认导出
export default UserCard;5.3 类型定义规范
类型文件组织:
types/
api.ts # API 相关类型
models.ts # 数据模型
components.ts # 组件 Props 类型
utils.ts # 工具类型
index.ts # 统一导出类型命名规范:
// ✅ 好的命名
interface User {
id: string;
name: string;
}
type UserRole = 'admin' | 'editor' | 'viewer';
type ApiResponse<T> = {
data: T;
success: boolean;
message: string;
};
// ❌ 不好的命名
interface IUser {} // 不要加 I 前缀
type user = {}; // 用 PascalCase
type UserType = {}; // 避免 Type 后缀(除非必要)
5.4 ESLint + Prettier 配置
推荐的规则集:
{
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:react/recommended",
"plugin:react-hooks/recommended",
"plugin:jsx-a11y/recommended",
"prettier"
],
"rules": {
"react/react-in-jsx-scope": "off",
"@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/no-explicit-any": "warn",
"react/prop-types": "off"
},
"settings": {
"react": {
"version": "detect"
}
}
}六、常见问题与解决方案
Q1:什么时候用 Server Component,什么时候用 Client Component?
快速判断:
需要交互(点击、输入、状态)? → Client Component
需要访问浏览器 API(window, document)? → Client Component
需要获取数据、访问后端? → Server Component
纯展示内容? → Server Component最佳实践:
- 尽量用 Server Component,只有需要交互时才用 Client Component
- Client Component 尽量放在组件树的叶子节点
- 数据获取尽量在 Server Component 中完成
Q2:any 类型怎么处理?
避免 any 的方法:
// 1. 使用 unknown 代替 any
function processValue(value: unknown) {
if (typeof value === 'string') {
// 这里 value 被收窄为 string
console.log(value.toUpperCase());
}
}
// 2. 使用类型守卫
function isUser(value: unknown): value is User {
return (
typeof value === 'object' &&
value !== null &&
'id' in value &&
'name' in value
);
}
// 3. 使用泛型
function identity<T>(value: T): T {
return value;
}Q3:状态更新不及时怎么办?
常见原因:
- 闭包陷阱
// ❌ 问题:count 总是初始值
useEffect(() => {
const timer = setInterval(() => {
console.log(count); // 闭包捕获了初始值
}, 1000);
return () => clearInterval(timer);
}, []); // 空依赖
// ✅ 解决方案:使用函数式更新
useEffect(() => {
const timer = setInterval(() => {
setCount((prev) => prev + 1);
}, 1000);
return () => clearInterval(timer);
}, []);- 状态更新是异步的
// ❌ 问题:刚设置完就读取不到新值
setCount(count + 1);
console.log(count); // 还是旧值
// ✅ 解决方案:使用 useEffect 监听变化
useEffect(() => {
console.log('count 变化了:', count);
}, [count]);Q4:useEffect 依赖警告怎么办?
常见场景:
// ❌ ESLint 警告:缺少依赖
useEffect(() => {
fetchData(userId);
}, []); // 警告:缺少 userId
// ✅ 解决方案 1:添加依赖(推荐)
useEffect(() => {
fetchData(userId);
}, [userId]);
// ✅ 解决方案 2:用 useCallback/useMemo 稳定函数
const fetchUserData = useCallback(() => {
fetchData(userId);
}, [userId]);
useEffect(() => {
fetchUserData();
}, [fetchUserData]);Q5:表单处理有什么好方案?
推荐方案:
- 简单表单 → 原生表单 + FormData + React Actions
- 中等表单 → React Hook Form
- 复杂表单 → React Hook Form + Zod
// React Hook Form + Zod 示例
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
const schema = z.object({
name: z.string().min(2, '名字至少 2 个字符'),
email: z.string().email('请输入有效的邮箱'),
age: z.number().min(18, '必须年满 18 岁').optional(),
});
type FormData = z.infer<typeof schema>;
function MyForm() {
const { register, handleSubmit, formState: { errors } } = useForm<FormData>({
resolver: zodResolver(schema),
});
const onSubmit = (data: FormData) => {
console.log(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<div>
<input {...register('name')} placeholder="姓名" />
{errors.name && <p>{errors.name.message}</p>}
</div>
<div>
<input {...register('email')} placeholder="邮箱" />
{errors.email && <p>{errors.email.message}</p>}
</div>
<button type="submit">提交</button>
</form>
);
}七、工具链与生态推荐
7.1 开发工具
| 工具 | 用途 | 推荐度 |
|---|---|---|
| Vite | 构建工具 | ⭐⭐⭐⭐⭐ |
| TypeScript | 类型安全 | ⭐⭐⭐⭐⭐ |
| ESLint | 代码质量 | ⭐⭐⭐⭐⭐ |
| Prettier | 代码格式化 | ⭐⭐⭐⭐⭐ |
| Storybook | 组件开发 | ⭐⭐⭐⭐ |
| React DevTools | 调试工具 | ⭐⭐⭐⭐⭐ |
7.2 UI 组件库
| 库 | 特点 | 推荐度 |
|---|---|---|
| Shadcn/ui | 可定制、无样式、复制粘贴 | ⭐⭐⭐⭐⭐ |
| MUI (Material UI) | 功能完整、Material Design | ⭐⭐⭐⭐ |
| Ant Design | 企业级、中文文档好 | ⭐⭐⭐⭐ |
| Chakra UI | 简洁、可访问性好 | ⭐⭐⭐⭐ |
| Tailwind CSS | 原子化 CSS、灵活 | ⭐⭐⭐⭐⭐ |
| Radix UI | 无样式、可访问性好 | ⭐⭐⭐⭐ |
7.3 测试工具
| 工具 | 用途 | 推荐度 |
|---|---|---|
| Vitest | 单元测试 | ⭐⭐⭐⭐⭐ |
| React Testing Library | 组件测试 | ⭐⭐⭐⭐⭐ |
| Playwright | E2E 测试 | ⭐⭐⭐⭐⭐ |
| Jest | 单元测试(老牌) | ⭐⭐⭐⭐ |
八、总结
React 19 + TypeScript 学习路径
第 1 周:React 基础 + TypeScript 基础
- JSX、组件、Props、State
- TypeScript 基本类型、接口、泛型
第 2 周:Hooks 深入
- useState、useEffect、useContext
- useReducer、useCallback、useMemo
- 自定义 Hook
第 3-4 周:React 19 新特性
- Server Components
- Actions、useActionState
- useOptimistic、use Hook
第 2 个月:状态管理 + 性能优化
- Zustand、TanStack Query
- memo、useMemo、useCallback
- 代码分割、虚拟滚动
第 3 个月:工程化 + 最佳实践
- 项目架构、代码规范
- 测试、CI/CD
- 性能监控、错误边界核心要点回顾
- 拥抱 Server Components:数据获取和展示用 Server Component,交互用 Client Component
- 类型安全第一:尽量避免 any,充分利用 TypeScript 的类型推断
- 合理选择状态方案:小项目用 useState,中项目用 Zustand,大项目用 Redux Toolkit
- 服务端状态用 TanStack Query:不要自己管理服务器数据
- 性能优化要适度:先测量再优化,不要过早优化
- 保持组件小而专注:每个组件只做一件事
【相关推荐】
- Vite 5 项目构建与性能优化指南 - 构建工具
- AI 编程助手横向对比 - AI 辅助编程
- 虚拟信用卡使用指南 - 海外服务支付
React 19 + TypeScript 是 2026 年前端开发的黄金组合。掌握这些新特性和最佳实践,你的开发效率和代码质量都会有质的飞跃。祝你编码愉快!⚛️
