Filed under
Frontend / React
返回专栏
📚 什么是 Next.js? Next.js 是一个基于 React 的全栈框架 ,由 Vercel 开发维护。它让你用 React 写前端的同时,也能轻松处理后端逻辑。
一句话理解
React 是造积木的工具Next.js 是用这些积木盖好的房子框架
🎯 为什么选择 Next.js?
痛点
React 原生
Next.js
路由
需要装 react-router
✅ 内置文件路由
SEO
CSR 不友好
✅ SSR/SSG 支持
首屏加载
慢(需要下载 JS)
✅ 服务端渲染,秒开
API 接口
需要单独后端
✅ 内置 API Routes
打包优化
手动配置
✅ 自动优化
图片优化
手动处理
✅ 内置 Image 组件
🚀 快速开始 创建项目 1 2 3 4 5 6 7 8 pnpm create next-app@latest my-app npx create-next-app@latest my-app yarn create next-app my-app
创建时会问你:
1 2 3 4 5 6 ✔ Would you like to use TypeScript? Yes ✔ Would you like to use ESLint? Yes ✔ Would you like to use Tailwind CSS? Yes ✔ Would you like to use `src/` directory? Yes ✔ Would you like to use App Router? Yes ← 推荐选 Yes ✔ Would you like to customize the default import alias? No
启动开发
访问 http://localhost:3000 🎉
📁 项目结构(App Router) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 my-app/ ├── src/ │ └── app/ # 核心目录 │ ├── layout.tsx # 根布局 │ ├── page.tsx # 首页 / │ ├── globals.css # 全局样式 │ ├── about/ │ │ └── page.tsx # /about │ ├── blog/ │ │ ├── page.tsx # /blog │ │ └── [slug]/ │ │ └── page.tsx # /blog/xxx(动态路由) │ └── api/ │ └── hello/ │ └── route.ts # API: /api/hello ├── public/ # 静态资源 ├── next.config.js # Next.js 配置 ├── tailwind.config.js └── package.json
🛤️ 路由系统 基于文件的路由 文件路径 = URL 路径 ,超级直观!
文件路径
URL
app/page.tsx
/
app/about/page.tsx
/about
app/blog/page.tsx
/blog
app/blog/[slug]/page.tsx
/blog/hello-world
app/shop/[...slug]/page.tsx
/shop/a/b/c
动态路由 1 2 3 4 5 6 7 export default function BlogPost ({ params }: { params: { slug: string } } ) { return <h1 > 文章: {params.slug}</h1 > ; }
路由组(不影响 URL) 1 2 3 4 5 6 app/ ├── (marketing)/ # 括号表示分组,不出现在 URL │ ├── about/page.tsx # /about │ └── blog/page.tsx # /blog └── (shop)/ └── cart/page.tsx # /cart
页面跳转 1 2 3 4 5 6 7 8 9 10 11 import Link from 'next/link' import { useRouter } from 'next/navigation' <Link href="/about" >关于我们</Link > <Link href ={ `/blog /${slug }`}> 查看文章</Link > const router = useRouter ()router.push ('/dashboard' ) router.back ()
🎨 布局系统 根布局(必须) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 export default function RootLayout ({ children, }: { children: React.ReactNode; } ) { return ( <html lang ="zh-CN" > <body > <header > 导航栏</header > <main > {children}</main > <footer > 页脚</footer > </body > </html > ); }
嵌套布局 1 2 3 4 5 6 7 8 9 10 11 12 13 export default function DashboardLayout ({ children, }: { children: React.ReactNode; } ) { return ( <div className ="flex" > <aside > 侧边栏</aside > <main > {children}</main > </div > ); }
布局会自动嵌套:
1 2 3 RootLayout └── DashboardLayout └── page.tsx
🔄 渲染模式 Next.js 支持多种渲染方式,这是它的核心优势 !
对比一览
模式
全称
渲染时机
适用场景
SSG
Static Site Generation
构建时
博客、文档
SSR
Server-Side Rendering
请求时
个性化页面
ISR
Incremental Static Regeneration
按需更新
电商、新闻
CSR
Client-Side Rendering
浏览器
仪表盘、后台
1️⃣ 服务端组件(默认) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 export default async function Posts ( ) { const posts = await fetch ("https://api.example.com/posts" ).then ((r ) => r.json () ); return ( <ul > {posts.map((post) => ( <li key ={post.id} > {post.title}</li > ))} </ul > ); }
2️⃣ 客户端组件 1 2 3 4 5 6 7 8 9 "use client" ; import { useState } from "react" ;export default function Counter ( ) { const [count, setCount] = useState (0 ); return <button onClick ={() => setCount(count + 1)}>点击: {count}</button > ; }
3️⃣ 静态生成 (SSG) 1 2 3 4 5 6 7 export default async function About ( ) { const data = await fetch ("https://api.example.com/about" , { cache : "force-cache" , }); return <div > {data}</div > ; }
4️⃣ 动态渲染 (SSR) 1 2 3 4 5 6 7 export default async function Dashboard ( ) { const data = await fetch ("https://api.example.com/user" , { cache : "no-store" , }); return <div > {data}</div > ; }
5️⃣ 增量静态再生 (ISR) 1 2 3 4 5 6 7 export default async function Products ( ) { const products = await fetch ("https://api.example.com/products" , { next : { revalidate : 60 }, }); return <div > {products}</div > ; }
revalidate: 60 的意思:
60 秒内:直接返回缓存的静态页面
60 秒后:返回旧页面,同时后台悄悄更新
更新完成后:下次访问就是新页面
📡 数据获取 服务端获取(推荐) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 async function getUsers ( ) { const res = await fetch ("https://api.example.com/users" ); if (!res.ok ) throw new Error ("获取失败" ); return res.json (); } export default async function UsersPage ( ) { const users = await getUsers (); return ( <ul > {users.map((user) => ( <li key ={user.id} > {user.name}</li > ))} </ul > ); }
客户端获取(交互数据) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 "use client" ;import { useEffect, useState } from "react" ;export default function Comments ( ) { const [comments, setComments] = useState ([]); useEffect (() => { fetch ("/api/comments" ) .then ((r ) => r.json ()) .then (setComments); }, []); return <div > {/* 渲染评论 */}</div > ; }
使用 SWR(推荐) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 "use client" ;import useSWR from "swr" ;const fetcher = (url : string ) => fetch (url).then ((r ) => r.json ());export default function Profile ( ) { const { data, error, isLoading } = useSWR ("/api/user" , fetcher); if (isLoading) return <div > 加载中...</div > ; if (error) return <div > 加载失败</div > ; return <div > 你好, {data.name}</div > ; }
🔌 API Routes Next.js 可以直接写后端接口!
创建 API 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import { NextResponse } from "next/server" ;export async function GET ( ) { const users = [ { id : 1 , name : "张三" }, { id : 2 , name : "李四" }, ]; return NextResponse .json (users); } export async function POST (request : Request ) { const body = await request.json (); return NextResponse .json ({ success : true , data : body }); }
动态 API 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import { NextResponse } from "next/server" ;export async function GET ( request : Request , { params }: { params: { id: string } } ) { const user = await getUserById (params.id ); return NextResponse .json (user); } export async function DELETE ( request : Request , { params }: { params: { id: string } } ) { await deleteUser (params.id ); return NextResponse .json ({ success : true }); }
🖼️ 图片优化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 import Image from "next/image" ;import heroImage from "@/public/hero.jpg" ;export default function Hero ( ) { return ( <> {/* 本地图片(自动优化) */} <Image src ={heroImage} alt ="Hero" placeholder ="blur" // 模糊占位 /> {/* 远程图片 */} <Image src ="https://example.com/photo.jpg" alt ="Photo" width ={800} height ={600} priority // 优先加载 (LCP 图片用 ) /> {/* 填充容器 */} <div className ="relative h-64 w-full" > <Image src ="/banner.jpg" alt ="Banner" fill className ="object-cover" /> </div > </> ); }
配置远程图片域名:
1 2 3 4 5 6 7 8 9 10 11 module .exports = { images : { remotePatterns : [ { protocol : "https" , hostname : "example.com" , }, ], }, };
🎭 Loading 和 Error 加载状态 1 2 3 4 5 6 7 8 export default function Loading ( ) { return ( <div className ="flex justify-center items-center h-screen" > <div className ="animate-spin h-8 w-8 border-4 border-blue-500 rounded-full border-t-transparent" /> </div > ); }
错误处理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 "use client" ;export default function Error ({ error, reset, }: { error: Error ; reset: () => void ; } ) { return ( <div className ="text-center py-10" > <h2 > 出错了!</h2 > <p > {error.message}</p > <button onClick ={reset} > 重试</button > </div > ); }
404 页面 1 2 3 4 5 6 7 8 9 export default function NotFound ( ) { return ( <div className ="text-center py-20" > <h1 className ="text-6xl font-bold" > 404</h1 > <p > 页面不存在</p > </div > ); }
🔐 中间件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import { NextResponse } from "next/server" ;import type { NextRequest } from "next/server" ;export function middleware (request : NextRequest ) { const token = request.cookies .get ("token" ); if (!token && request.nextUrl .pathname .startsWith ("/dashboard" )) { return NextResponse .redirect (new URL ("/login" , request.url )); } return NextResponse .next (); } export const config = { matcher : ["/dashboard/:path*" , "/admin/:path*" ], };
🌍 元数据和 SEO 静态元数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import { Metadata } from "next" ;export const metadata : Metadata = { title : "我的网站" , description : "这是一个很棒的网站" , keywords : ["Next.js" , "React" , "Web开发" ], openGraph : { title : "我的网站" , description : "这是一个很棒的网站" , images : ["/og-image.png" ], }, }; export default function Home ( ) { return <div > 首页</div > ; }
动态元数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import { Metadata } from "next" ;type Props = { params : { slug : string }; }; export async function generateMetadata ({ params }: Props ): Promise <Metadata > { const post = await getPost (params.slug ); return { title : post.title , description : post.excerpt , openGraph : { images : [post.coverImage ], }, }; } export default async function BlogPost ({ params }: Props ) { const post = await getPost (params.slug ); return <article > {post.content}</article > ; }
📦 部署 Vercel(推荐,免费) 1 2 3 4 5 6 7 8 pnpm add -g vercel vercel vercel --prod
或者直接在 vercel.com 导入 GitHub 仓库,自动部署!
自托管(Node.js) 1 2 3 4 5 pnpm build pnpm start
Docker 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 FROM node:18 -alpine AS builderWORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build FROM node:18 -alpine AS runnerWORKDIR /app COPY --from=builder /app/.next/standalone ./ COPY --from=builder /app/.next/static ./.next/static COPY --from=builder /app/public ./public EXPOSE 3000 CMD ["node" , "server.js" ]
1 2 3 4 module .exports = { output : "standalone" , };
🎯 最佳实践 1. 服务端 vs 客户端组件 1 2 3 4 5 6 7 8 9 10 11 服务端组件(默认): ✅ 数据获取 ✅ 访问后端资源 ✅ 敏感信息(API Key) ✅ 大型依赖 客户端组件('use client'): ✅ 交互(onClick, onChange) ✅ 状态(useState, useEffect) ✅ 浏览器 API(localStorage) ✅ 自定义 hooks
2. 组件组织 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 "use client" ;export default function Page ( ) { const [count, setCount] = useState (0 ); return ( <div > <Header /> {/* 不需要交互 */} <Counter /> {/* 需要交互 */} <Footer /> {/* 不需要交互 */} </div > ); } export default function Page ( ) { return ( <div > <Header /> {/* 服务端 */} <Counter /> {/* 客户端 */} <Footer /> {/* 服务端 */} </div > ); } ("use client" ); export default function Counter ( ) { const [count, setCount] = useState (0 ); return <button onClick ={() => setCount((c) => c + 1)}>{count}</button > ; }
3. 环境变量 1 2 3 DATABASE_URL=xxx NEXT_PUBLIC_API_URL=xxx
📊 与其他框架对比
特性
Next.js
Nuxt.js
Remix
基于
React
Vue
React
渲染模式
SSG/SSR/ISR/CSR
SSG/SSR/CSR
SSR
路由
文件路由
文件路由
文件路由
数据获取
fetch + cache
useFetch
loader
API
API Routes
Server Routes
Actions
部署
Vercel / 自托管
Vercel / 自托管
多平台
🎯 总结 Next.js 核心概念
概念
说明
App Router
基于文件的路由系统
服务端组件
默认,在服务器渲染
客户端组件
'use client',有交互
SSG/SSR/ISR
多种渲染策略
API Routes
内置后端 API
Middleware
请求拦截处理
记忆口诀
路由看文件,渲染看场景 > 服务端获取,客户端交互 > Image 优化图,Metadata 管 SEO
📚 学习资源