⚛️ 前端开发
“最佳的用户界面是隐形的 —— 它们只是自然而然地工作。”
本章节旨在指导您使用 React 和 Next.js 构建现代、响应式且高性能的 Web 应用程序。
⚛️ React 基础
组件模式 (Component Patterns)
// 函数式组件 (推荐)
export function UserCard({ user, onSelect }) {
const [isHovered, setIsHovered] = useState(false);
return (
<div
className="user-card"
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
onClick={() => onSelect(user.id)}
>
<Avatar src={user.avatar} />
<h3>{user.name}</h3>
{isHovered && <UserDetails user={user} />}
</div>
);
}
常用 Hooks
| Hook | 用途 | 示例用例 |
|---|---|---|
useState | 局部状态 | 表单输入、开关 |
useEffect | 副作用 | API 调用、订阅 |
useContext | 消费上下文 | 主题、认证状态 |
useReducer | 复杂状态逻辑 | 拥有多个字段的表单 |
useMemo | 记忆计算值 | 昂贵的计算 |
useCallback | 记忆函数 | 传递给子组件的事件处理函数 |
useRef | 可变引用 | DOM 访问、记录 前一个值 |
自定义 Hooks
// useDebounce - 延迟值更新
function useDebounce(value, delay) {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const timer = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => clearTimeout(timer);
}, [value, delay]);
return debouncedValue;
}
// 用法示例
function SearchInput() {
const [query, setQuery] = useState('');
const debouncedQuery = useDebounce(query, 300);
useEffect(() => {
if (debouncedQuery) {
searchAPI(debouncedQuery);
}
}, [debouncedQuery]);
return <input value={query} onChange={e => setQuery(e.target.value)} />;
}
🔲 Next.js
App Router (Next.js 14+)
app/
├── layout.tsx # 根布局
├── page.tsx # 首页 (/)
├── loading.tsx # 加载 UI
├── error.tsx # 错误 UI
├── blog/
│ ├── page.tsx # /blog 页面
│ └── [slug]/
│ └── page.tsx # /blog/:slug 动态路由
└── api/
└── users/
└── route.ts # API 路由
Server Components vs Client Components
// Server Component (默认) - 在服务器端运行
// 无 "use client" 指令
export default async function UserPage({ params }) {
// 可以直接使用 async/await 获取数据
const user = await fetchUser(params.id);
return <UserProfile user={user} />;
}
// Client Component - 在浏览器端运行
"use client";
import { useState } from 'react';
export function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
计数: {count}
</button>
);
}
数据获取模式 (Data Fetching)
// Server Component - 直接获取数据
async function BlogPosts() {
const posts = await fetch('https://api.example.com/posts', {
cache: 'no-store', // 或 'force-cache', revalidate: 3600
}).then(res => res.json());
return (
<ul>
{posts.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
);
}
// Client Component - 使用 SWR 或 React Query
"use client";
import useSWR from 'swr';
function UserProfile({ userId }) {
const { data, error, isLoading } = useSWR(
`/api/users/${userId}`,
fetcher
);
if (error) return <div>加载失败</div>;
if (isLoading) return <div>加载中...</div>;
return <div>{data.name}</div>;
}
API 路由 (API Routes)
// app/api/users/route.ts
import { NextRequest, NextResponse } from 'next/server';
export async function GET(request: NextRequest) {
const users = await prisma.user.findMany();
return NextResponse.json(users);
}
export async function POST(request: NextRequest) {
const body = await request.json();
const user = await prisma.user.create({ data: body });
return NextResponse.json(user, { status: 201 });
}
🎨 Tailwind CSS
Utility-First 方法论
// 传统 CSS
<div className="card">...</div>
// .card { padding: 1rem; border-radius: 0.5rem; ... }
// Tailwind CSS
<div className="p-4 rounded-lg bg-white shadow-md hover:shadow-lg transition-shadow">
...
</div>
常用模式
// 响应式设计
<div className="w-full md:w-1/2 lg:w-1/3">
{/* 移动端全宽,平板半宽,桌面端三分之一宽 */}
</div>
// 暗模式
<div className="bg-white dark:bg-gray-800 text-black dark:text-white">
{/* 自动适应系统偏好或主题切换 */}
</div>
// 悬停与焦点状态
<button className="bg-blue-500 hover:bg-blue-600 focus:ring-2 focus:ring-blue-300">
点击我
</button>
// Flexbox 居中
<div className="flex items-center justify-center h-screen">
<Content />
</div>
使用 clsx 进行组件组合
import { clsx } from 'clsx';
function Button({ variant = 'primary', size = 'md', className, children }) {
return (
<button
className={clsx(
'rounded font-medium transition-colors',
{
'bg-blue-500 text-white hover:bg-blue-600': variant === 'primary',
'bg-gray-200 text-gray-800 hover:bg-gray-300': variant === 'secondary',
'px-2 py-1 text-sm': size === 'sm',
'px-4 py-2': size === 'md',
'px-6 py-3 text-lg': size === 'lg',
},
className
)}
>
{children}
</button>
);
}
🔄 状态管理
| 方案 | 复杂度 | 最适合 |
|---|---|---|
useState | 低 | 组件局部状态 |
useContext | 低 | 简单全局状态(主题、认证) |
useReducer | 中 | 复杂组件内部状态 |
| Zustand | 低 | 轻量级全局状态 |
| Jotai | 低 | 原子化状态模型 |
| Redux Toolkit | 高 | 大型企业应用 |
Zustand 示例
import { create } from 'zustand';
const useStore = create((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
}));
function Counter() {
const { count, increment, decrement } = useStore();
return (
<div>
<span>{count}</span>
<button onClick={increment}>+</button>
<button onClick={decrement}>-</button>
</div>
);
}
📝 详细主题
前端最佳实践
- 组件组合 - 优先使用小型、可复用的组件。
- 状态提升 - 在正确的层级共享状态。
- 合理记忆化 - 避免过度优化,按需使用
useMemo/useCallback。 - 可访问性 - 使用语义化的 HTML 和 ARIA 属性。
- 渐进增强 - 确保在禁用 JavaScript 时也能基本可用。