2分钟理解 Next.js:终极 React 框架!
可用语言 :
Next.js 将 React 转变为一套完整的现代 Web 开发工具套件。其最新版本带来了革命性的功能,改变了我们构建 Web 应用的方式。让我们来看看为什么 Next.js 变得如此不可或缺!
Next.js 15 的亮点 🎯
- App Router - 基于文件系统的现代路由系统
- Server Components - 默认服务器端渲染的 React 组件
- Server Actions - 直接从组件中进行服务器端变更
- Streaming - 渐进式页面加载实现流畅的用户体验
- 智能缓存 - 自动数据缓存
- 零配置 - 内置优化,开箱即用
快速开始 ⚙️
bash
1# 创建新项目2npx create-next-app@latest my-app --typescript --tailwind --app34# 启动开发服务器5cd my-app6npm run dev
现代化架构 📂
App Router 引入了更直观的项目结构:
plaintext
1my-app/2├── app/3│ ├── layout.tsx # 根布局4│ ├── page.tsx # 主页 (/)5│ ├── loading.tsx # 全局加载6│ ├── error.tsx # 错误处理7│ ├── not-found.tsx # 404页面8│ ├── blog/9│ │ ├── page.tsx # /blog10│ │ └── [slug]/11│ │ └── page.tsx # /blog/:slug12│ └── api/13│ └── route.ts # API路由14├── components/15│ ├── server/ # 服务器组件16│ ├── client/ # 客户端组件17│ └── ui/ # UI组件18└── lib/19 └── actions.ts # 服务器操作
Server Components ⚡️
组件默认在服务器端渲染以获得更好的性能:
typescript
1// app/blog/page.tsx2import { PostList } from '@/components/server/PostList';3import { Suspense } from 'react';45export default async function BlogPage() {6 return (7 <main className="container mx-auto px-4 py-8">8 <h1 className="text-4xl font-bold mb-8">我们的博客</h1>9 <Suspense fallback={<p>文章加载中...</p>}>10 <PostList />11 </Suspense>12 </main>13 );14}1516// components/server/PostList.tsx17async function getPosts() {18 const posts = await db.post.findMany();19 return posts;20}2122export async function PostList() {23 const posts = await getPosts();2425 return (26 <div className="grid gap-6 md:grid-cols-2">27 {posts.map(post => (28 <article key={post.id} className="p-4 rounded-lg border">29 <h2 className="text-xl font-semibold">{post.title}</h2>30 <p className="mt-2 text-gray-600">{post.excerpt}</p>31 </article>32 ))}33 </div>34 );35}
Client Components 🔄
需要交互性的组件:
typescript
1// components/client/SearchBar.tsx2'use client';34import { useState } from 'react';5import { useRouter } from 'next/navigation';67export function SearchBar() {8 const [query, setQuery] = useState('');9 const router = useRouter();1011 function handleSearch(e: React.FormEvent) {12 e.preventDefault();13 router.push(`/search?q=${encodeURIComponent(query)}`);14 }1516 return (17 <form onSubmit={handleSearch} className="flex gap-2">18 <input19 type="search"20 value={query}21 onChange={(e) => setQuery(e.target.value)}22 placeholder="搜索..."23 className="px-4 py-2 rounded border"24 />25 <button type="submit" className="px-4 py-2 bg-blue-500 text-white rounded">26 搜索27 </button>28 </form>29 );30}
Server Actions ⚡️
直接从组件中进行服务器端变更:
typescript
1// lib/actions.ts2'use server';34import { revalidatePath } from 'next/cache';5import { redirect } from 'next/navigation';6import { z } from 'zod';78const PostSchema = z.object({9 title: z.string().min(3, "标题至少需要3个字符"),10 content: z.string().min(10, "内容至少需要10个字符")11});1213export async function createPost(formData: FormData) {14 const data = {15 title: formData.get('title'),16 content: formData.get('content')17 };1819 const result = PostSchema.safeParse(data);2021 if (!result.success) {22 return { error: result.error.flatten() };23 }2425 const post = await db.post.create({26 data: result.data27 });2829 revalidatePath('/blog');30 redirect('/blog');31}3233// app/blog/new/page.tsx34import { createPost } from '@/lib/actions';3536export default function NewPostPage() {37 return (38 <form action={createPost} className="space-y-4 max-w-lg mx-auto">39 <div>40 <label htmlFor="title" className="block text-sm font-medium">41 标题42 </label>43 <input44 type="text"45 name="title"46 id="title"47 required48 className="mt-1 block w-full rounded-md border-gray-300"49 />50 </div>51 <div>52 <label htmlFor="content" className="block text-sm font-medium">53 内容54 </label>55 <textarea56 name="content"57 id="content"58 required59 rows={5}60 className="mt-1 block w-full rounded-md border-gray-300"61 />62 </div>63 <button64 type="submit"65 className="w-full py-2 px-4 bg-blue-500 text-white rounded-md"66 >67 发布68 </button>69 </form>70 );71}
现代 API 路由 🛠️
typescript
1// app/api/posts/route.ts2import { NextResponse } from 'next/server';3import { z } from 'zod';45const PostSchema = z.object({6 title: z.string().min(3),7 content: z.string().min(10),8});910export async function GET(request: Request) {11 const { searchParams } = new URL(request.url);12 const query = searchParams.get('q');1314 const posts = await db.post.findMany({15 where: query16 ? {17 OR: [18 { title: { contains: query } },19 { content: { contains: query } },20 ],21 }22 : undefined,23 });2425 return NextResponse.json({ posts });26}2728export async function POST(request: Request) {29 try {30 const body = await request.json();31 const result = PostSchema.safeParse(body);3233 if (!result.success) {34 return NextResponse.json(35 { error: result.error.flatten() },36 { status: 400 },37 );38 }3940 const post = await db.post.create({41 data: result.data,42 });4344 return NextResponse.json(post, { status: 201 });45 } catch (error) {46 return NextResponse.json({ error: '服务器错误' }, { status: 500 });47 }48}
优化和最佳实践 🚀
1. Suspense 流式传输
typescript
1import { Suspense } from 'react';2import { PostList } from '@/components/server/PostList';3import { SidebarNav } from '@/components/server/SidebarNav';4import { Loading } from '@/components/ui/Loading';56export default function BlogLayout({7 children8}: {9 children: React.ReactNode10}) {11 return (12 <div className="flex gap-8">13 <Suspense fallback={<Loading />}>14 <SidebarNav />15 </Suspense>16 <main className="flex-1">17 <Suspense fallback={<Loading />}>18 {children}19 </Suspense>20 </main>21 <aside className="w-64">22 <Suspense fallback={<Loading />}>23 <PostList type="recent" />24 </Suspense>25 </aside>26 </div>27 );28}
2. 错误处理
typescript
1// app/error.tsx2'use client';34export default function Error({5 error,6 reset7}: {8 error: Error & { digest?: string };9 reset: () => void;10}) {11 return (12 <div className="flex flex-col items-center justify-center min-h-[400px]">13 <h2 className="text-2xl font-bold mb-4">14 发生错误!15 </h2>16 <button17 onClick={() => reset()}18 className="px-4 py-2 bg-blue-500 text-white rounded"19 >20 重试21 </button>22 </div>23 );24}
3. 优化的图片加载
typescript
1import Image from 'next/image';23export function Avatar({ src, alt }: { src: string; alt: string }) {4 return (5 <div className="relative w-10 h-10">6 <Image7 src={src}8 alt={alt}9 fill10 sizes="40px"11 className="rounded-full object-cover"12 priority13 />14 </div>15 );16}
深入学习 🎈
总结 ✨
Next.js 15 在 React Web 开发中代表了重大进步:
- 服务器优先:默认服务器端组件实现更好的性能
- 简单性:基于文件的直观架构
- 性能:自动优化和流式传输
- 改进的开发体验:Server Actions 和现代 API 路由
App Router、Server Components 和 Server Actions 的组合为构建现代、高性能和可维护的 Web 应用提供了坚实的基础。
现在你已经了解了 Next.js 14 的基础知识,你已经准备好创建现代和高性能的 Web 应用了!请查看官方文档以深入了解每个概念。祝开发愉快!🚀