logo
🎨 前端开发

Next.js

Next.js Cheat Sheet - 快速参考指南,收录常用语法、命令与实践。

📂 分类 · 前端开发🧭 Markdown 速查🏷️ 3 个标签
#nextjs#react#ssr
向下滚动查看内容
返回全部 Cheat Sheets

入门

创建项目
BASH
滚动查看更多
# 使用 create-next-app
npx create-next-app@latest my-app

# 使用 TypeScript
npx create-next-app@latest my-app --typescript

# 使用 Tailwind CSS
npx create-next-app@latest my-app --tailwind

# 完整选项
npx create-next-app@latest my-app --ts --tailwind --eslint --app --src-dir
项目结构 (App Router)
BASH
滚动查看更多
my-app/
├── app/                 # App Router 目录
│   ├── layout.tsx       # 根布局
│   ├── page.tsx         # 首页
│   ├── globals.css      # 全局样式
│   ├── api/             # API 路由
│   │   └── route.ts
│   └── [slug]/          # 动态路由
│       └── page.tsx
├── components/          # 组件目录
├── lib/                 # 工具函数
├── public/              # 静态资源
├── next.config.js       # Next.js 配置
└── package.json
开发命令
BASH
滚动查看更多
# 开发模式
npm run dev

# 构建生产版本
npm run build

# 启动生产服务器
npm run start

# 代码检查
npm run lint

路由

文件系统路由
文件路由
app/page.tsx/
app/about/page.tsx/about
app/blog/[slug]/page.tsx/blog/:slug
app/shop/[...slug]/page.tsx/shop/*
app/(marketing)/about/page.tsx/about (分组)
动态路由
TSX
滚动查看更多
// app/blog/[slug]/page.tsx
interface Props {
	params: { slug: string };
}

export default function BlogPost({ params }: Props) {
	return <h1>Post: {params.slug}</h1>;
}

// 生成静态参数
export async function generateStaticParams() {
	const posts = await getPosts();
	return posts.map(post => ({
		slug: post.slug
	}));
}
Catch-all 路由
TSX
滚动查看更多
// app/shop/[...slug]/page.tsx
// 匹配 /shop/a, /shop/a/b, /shop/a/b/c

interface Props {
	params: { slug: string[] };
}

export default function Shop({ params }: Props) {
	// /shop/a/b/c => slug = ['a', 'b', 'c']
	return <div>{params.slug.join('/')}</div>;
}
路由组
BASH
滚动查看更多
app/
├── (marketing)/        # 不影响 URL
│   ├── about/page.tsx  # /about
│   └── blog/page.tsx   # /blog
├── (shop)/
│   └── cart/page.tsx   # /cart
└── layout.tsx
并行路由
TSX
滚动查看更多
// app/layout.tsx
export default function Layout({
	children,
	team,
	analytics
}: {
	children: React.ReactNode;
	team: React.ReactNode;
	analytics: React.ReactNode;
}) {
	return (
		<>
			{children}
			{team}
			{analytics}
		</>
	);
}

// app/@team/page.tsx
// app/@analytics/page.tsx

页面与布局

页面组件
TSX
滚动查看更多
// app/page.tsx
export default function Home() {
	return (
		<main>
			<h1>Welcome to Next.js</h1>
		</main>
	);
}
布局组件
TSX
滚动查看更多
// app/layout.tsx
import { Inter } from 'next/font/google';

const inter = Inter({ subsets: ['latin'] });

export const metadata = {
	title: 'My App',
	description: 'My awesome app'
};

export default function RootLayout({ children }: { children: React.ReactNode }) {
	return (
		<html lang="en">
			<body className={inter.className}>{children}</body>
		</html>
	);
}
嵌套布局
TSX
滚动查看更多
// app/dashboard/layout.tsx
export default function DashboardLayout({ children }: { children: React.ReactNode }) {
	return (
		<div className="dashboard">
			<nav>Dashboard Nav</nav>
			<main>{children}</main>
		</div>
	);
}
模板 (Template)
TSX
滚动查看更多
// app/template.tsx
// 每次导航都会重新挂载

export default function Template({ children }: { children: React.ReactNode }) {
	return <div>{children}</div>;
}
Loading UI
TSX
滚动查看更多
// app/dashboard/loading.tsx
export default function Loading() {
	return (
		<div className="loading">
			<div className="spinner" />
			<p>Loading...</p>
		</div>
	);
}
Error 处理
TSX
滚动查看更多
'use client';

// app/dashboard/error.tsx
export default function Error({
	error,
	reset
}: {
	error: Error & { digest?: string };
	reset: () => void;
}) {
	return (
		<div>
			<h2>Something went wrong!</h2>
			<button onClick={() => reset()}>Try again</button>
		</div>
	);
}
Not Found
TSX
滚动查看更多
// app/not-found.tsx
import Link from 'next/link';

export default function NotFound() {
	return (
		<div>
			<h2>Not Found</h2>
			<p>Could not find requested resource</p>
			<Link href="/">Return Home</Link>
		</div>
	);
}

数据获取

服务端组件获取数据
TSX
滚动查看更多
// 默认是服务端组件
async function getData() {
	const res = await fetch('https://api.example.com/data');
	if (!res.ok) throw new Error('Failed to fetch');
	return res.json();
}

export default async function Page() {
	const data = await getData();
	return <main>{data.title}</main>;
}
缓存策略
TSX
滚动查看更多
// 默认缓存 (等同于 SSG)
fetch('https://api.example.com/data');

// 不缓存 (等同于 SSR)
fetch('https://api.example.com/data', {
	cache: 'no-store'
});

// 定时重新验证 (ISR)
fetch('https://api.example.com/data', {
	next: { revalidate: 3600 } // 1小时
});

// 基于标签重新验证
fetch('https://api.example.com/data', {
	next: { tags: ['posts'] }
});
重新验证
TSX
滚动查看更多
// app/actions.ts
'use server';

import { revalidatePath, revalidateTag } from 'next/cache';

// 重新验证路径
export async function updatePost() {
	// 更新数据库
	revalidatePath('/blog');
}

// 重新验证标签
export async function refreshPosts() {
	revalidateTag('posts');
}
并行数据获取
TSX
滚动查看更多
async function Page() {
	// 并行请求
	const [user, posts] = await Promise.all([getUser(), getPosts()]);

	return (
		<div>
			<UserProfile user={user} />
			<PostList posts={posts} />
		</div>
	);
}
客户端数据获取
TSX
滚动查看更多
'use client';

import useSWR from 'swr';

const fetcher = (url: string) => fetch(url).then(res => res.json());

export default function Profile() {
	const { data, error, isLoading } = useSWR('/api/user', fetcher);

	if (isLoading) return <div>Loading...</div>;
	if (error) return <div>Error</div>;
	return <div>Hello {data.name}</div>;
}

Server Actions

定义 Server Action
TSX
滚动查看更多
// app/actions.ts
'use server';

export async function createPost(formData: FormData) {
	const title = formData.get('title');
	const content = formData.get('content');

	await db.post.create({
		data: { title, content }
	});

	revalidatePath('/posts');
}
在表单中使用
TSX
滚动查看更多
// app/page.tsx
import { createPost } from './actions';

export default function Page() {
	return (
		<form action={createPost}>
			<input name="title" />
			<textarea name="content" />
			<button type="submit">Create</button>
		</form>
	);
}
带参数的 Action
TSX
滚动查看更多
'use server';

export async function updatePost(id: string, formData: FormData) {
	const title = formData.get('title');
	await db.post.update({
		where: { id },
		data: { title }
	});
}

// 使用 bind
<form action={updatePost.bind(null, post.id)}>
	<input name="title" />
	<button>Update</button>
</form>;
使用 useFormState
TSX
滚动查看更多
'use client';

import { useFormState } from 'react-dom';
import { createPost } from './actions';

const initialState = { message: '' };

export default function Form() {
	const [state, formAction] = useFormState(createPost, initialState);

	return (
		<form action={formAction}>
			<input name="title" />
			<button>Create</button>
			<p>{state.message}</p>
		</form>
	);
}
使用 useFormStatus
TSX
滚动查看更多
'use client';

import { useFormStatus } from 'react-dom';

function SubmitButton() {
	const { pending } = useFormStatus();

	return <button disabled={pending}>{pending ? 'Submitting...' : 'Submit'}</button>;
}

API 路由

基本路由
TSX
滚动查看更多
// app/api/hello/route.ts
import { NextResponse } from 'next/server';

export async function GET() {
	return NextResponse.json({ message: 'Hello' });
}

export async function POST(request: Request) {
	const body = await request.json();
	return NextResponse.json({ received: body });
}
动态路由
TSX
滚动查看更多
// app/api/posts/[id]/route.ts
import { NextResponse } from 'next/server';

export async function GET(request: Request, { params }: { params: { id: string } }) {
	const post = await getPost(params.id);
	return NextResponse.json(post);
}
请求处理
TSX
滚动查看更多
// app/api/search/route.ts
import { NextRequest, NextResponse } from 'next/server';

export async function GET(request: NextRequest) {
	// 查询参数
	const searchParams = request.nextUrl.searchParams;
	const query = searchParams.get('q');

	// Headers
	const headersList = request.headers;
	const token = headersList.get('authorization');

	// Cookies
	const cookieStore = request.cookies;
	const session = cookieStore.get('session');

	return NextResponse.json({ query });
}
设置响应头
TSX
滚动查看更多
export async function GET() {
	return new NextResponse('Hello', {
		status: 200,
		headers: {
			'Content-Type': 'text/plain',
			'Cache-Control': 'no-store'
		}
	});
}
设置 Cookies
TSX
滚动查看更多
import { cookies } from 'next/headers';

export async function GET() {
	const cookieStore = cookies();

	// 读取
	const token = cookieStore.get('token');

	// 设置
	cookieStore.set('token', 'value', {
		httpOnly: true,
		secure: true,
		maxAge: 60 * 60 * 24 // 1 day
	});

	// 删除
	cookieStore.delete('token');

	return NextResponse.json({ success: true });
}

导航

Link 组件
TSX
滚动查看更多
import Link from 'next/link';

export default function Nav() {
	return (
		<nav>
			<Link href="/">Home</Link>
			<Link href="/about">About</Link>
			<Link href="/blog/hello-world">Blog Post</Link>

			{/* 预取 */}
			<Link href="/dashboard" prefetch={false}>
				Dashboard
			</Link>

			{/* 替换历史 */}
			<Link href="/login" replace>
				Login
			</Link>
		</nav>
	);
}
useRouter Hook
TSX
滚动查看更多
'use client';

import { useRouter } from 'next/navigation';

export default function Page() {
	const router = useRouter();

	return (
		<div>
			<button onClick={() => router.push('/dashboard')}>Dashboard</button>
			<button onClick={() => router.replace('/login')}>Login</button>
			<button onClick={() => router.back()}>Go Back</button>
			<button onClick={() => router.refresh()}>Refresh</button>
		</div>
	);
}
usePathname
TSX
滚动查看更多
'use client';

import { usePathname } from 'next/navigation';

export default function Nav() {
	const pathname = usePathname();

	return (
		<nav>
			<Link href="/" className={pathname === '/' ? 'active' : ''}>
				Home
			</Link>
		</nav>
	);
}
useSearchParams
TSX
滚动查看更多
'use client';

import { useSearchParams } from 'next/navigation';

export default function Search() {
	const searchParams = useSearchParams();
	const query = searchParams.get('q');

	return <div>Search: {query}</div>;
}
redirect
TSX
滚动查看更多
import { redirect } from 'next/navigation';

async function Page() {
	const user = await getUser();

	if (!user) {
		redirect('/login');
	}

	return <div>Welcome {user.name}</div>;
}

Metadata

静态 Metadata
TSX
滚动查看更多
// app/layout.tsx
import type { Metadata } from 'next';

export const metadata: Metadata = {
	title: 'My App',
	description: 'My app description',
	keywords: ['Next.js', 'React'],
	authors: [{ name: 'Author' }],
	openGraph: {
		title: 'My App',
		description: 'My app description',
		images: ['/og-image.png']
	},
	twitter: {
		card: 'summary_large_image'
	}
};
动态 Metadata
TSX
滚动查看更多
// app/blog/[slug]/page.tsx
import type { Metadata } from 'next';

interface 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.image]
		}
	};
}
Title 模板
TSX
滚动查看更多
// app/layout.tsx
export const metadata: Metadata = {
	title: {
		template: '%s | My App',
		default: 'My App'
	}
};

// app/about/page.tsx
export const metadata: Metadata = {
	title: 'About' // => "About | My App"
};

优化

Image 组件
TSX
滚动查看更多
import Image from 'next/image';

export default function Page() {
	return (
		<>
			{/* 本地图片 */}
			<Image src="/hero.png" alt="Hero" width={800} height={600} priority />

			{/* 远程图片 */}
			<Image
				src="https://example.com/photo.jpg"
				alt="Photo"
				width={400}
				height={300}
				placeholder="blur"
				blurDataURL="data:image/..."
			/>

			{/* 填充容器 */}
			<div style={{ position: 'relative', height: 300 }}>
				<Image src="/bg.png" alt="Background" fill style={{ objectFit: 'cover' }} />
			</div>
		</>
	);
}
Font 优化
TSX
滚动查看更多
// app/layout.tsx
import { Inter, Roboto_Mono } from 'next/font/google';

const inter = Inter({
	subsets: ['latin'],
	display: 'swap',
	variable: '--font-inter'
});

const robotoMono = Roboto_Mono({
	subsets: ['latin'],
	variable: '--font-roboto-mono'
});

export default function RootLayout({ children }: { children: React.ReactNode }) {
	return (
		<html className={`${inter.variable} ${robotoMono.variable}`}>
			<body>{children}</body>
		</html>
	);
}
Script 组件
TSX
滚动查看更多
import Script from 'next/script';

export default function Page() {
	return (
		<>
			{/* 延迟加载 */}
			<Script src="https://example.com/script.js" strategy="lazyOnload" />

			{/* 页面交互后加载 */}
			<Script src="https://example.com/analytics.js" strategy="afterInteractive" />

			{/* 内联脚本 */}
			<Script id="inline-script">{`console.log('Hello')`}</Script>
		</>
	);
}

Middleware

基本 Middleware
TSX
滚动查看更多
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export function middleware(request: NextRequest) {
	// 重定向
	if (request.nextUrl.pathname === '/old') {
		return NextResponse.redirect(new URL('/new', request.url));
	}

	// 重写
	if (request.nextUrl.pathname.startsWith('/api')) {
		return NextResponse.rewrite(new URL('/api/proxy', request.url));
	}

	return NextResponse.next();
}

// 配置匹配路径
export const config = {
	matcher: ['/old', '/api/:path*']
};
认证检查
TSX
滚动查看更多
// middleware.ts
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*'
};
设置请求头
TSX
滚动查看更多
export function middleware(request: NextRequest) {
	// 克隆请求头
	const requestHeaders = new Headers(request.headers);
	requestHeaders.set('x-custom-header', 'value');

	// 返回带新头的响应
	const response = NextResponse.next({
		request: {
			headers: requestHeaders
		}
	});

	// 设置响应头
	response.headers.set('x-response-header', 'value');

	return response;
}

配置

next.config.js
JS
滚动查看更多
/** @type {import('next').NextConfig} */
const nextConfig = {
	// 严格模式
	reactStrictMode: true,

	// 图片域名
	images: {
		remotePatterns: [
			{
				protocol: 'https',
				hostname: 'example.com'
			}
		]
	},

	// 环境变量
	env: {
		API_URL: process.env.API_URL
	},

	// 重定向
	async redirects() {
		return [
			{
				source: '/old',
				destination: '/new',
				permanent: true
			}
		];
	},

	// 重写
	async rewrites() {
		return [
			{
				source: '/api/:path*',
				destination: 'https://api.example.com/:path*'
			}
		];
	},

	// Headers
	async headers() {
		return [
			{
				source: '/:path*',
				headers: [
					{
						key: 'X-Frame-Options',
						value: 'DENY'
					}
				]
			}
		];
	}
};

module.exports = nextConfig;
环境变量
BASH
滚动查看更多
# .env.local
DATABASE_URL=postgresql://...
API_SECRET=secret

# 客户端可见 (需要 NEXT_PUBLIC_ 前缀)
NEXT_PUBLIC_API_URL=https://api.example.com
TSX
滚动查看更多
// 服务端
const dbUrl = process.env.DATABASE_URL;

// 客户端
const apiUrl = process.env.NEXT_PUBLIC_API_URL;

部署

Vercel 部署
BASH
滚动查看更多
# 安装 Vercel CLI
npm i -g vercel

# 部署
vercel

# 生产部署
vercel --prod
静态导出
JS
滚动查看更多
// next.config.js
const nextConfig = {
	output: 'export'
};
BASH
滚动查看更多
# 构建静态文件
npm run build
# 输出到 out/ 目录
Docker 部署
DOCKERFILE
滚动查看更多
# Dockerfile
FROM node:18-alpine AS base

FROM base AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci

FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build

FROM base AS runner
WORKDIR /app
ENV NODE_ENV production

COPY --from=builder /app/public ./public
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static

EXPOSE 3000
CMD ["node", "server.js"]
JS
滚动查看更多
// next.config.js
const nextConfig = {
	output: 'standalone'
};

常用 Hooks

路由相关
TSX
滚动查看更多
'use client';

import { useRouter, usePathname, useSearchParams, useParams } from 'next/navigation';

export function Component() {
	const router = useRouter();
	const pathname = usePathname();
	const searchParams = useSearchParams();
	const params = useParams();

	// router.push('/path')
	// router.replace('/path')
	// router.back()
	// router.refresh()

	return null;
}
服务端工具函数
TSX
滚动查看更多
import { headers, cookies } from 'next/headers';

async function ServerComponent() {
	// 读取请求头
	const headersList = headers();
	const userAgent = headersList.get('user-agent');

	// 读取 cookies
	const cookieStore = cookies();
	const token = cookieStore.get('token');

	return null;
}

另见

相关 Cheat Sheets

1v1免费职业咨询
logo

Follow Us

linkedinfacebooktwitterinstagramweiboyoutubebilibilitiktokxigua

We Accept

/image/layout/pay-paypal.png/image/layout/pay-visa.png/image/layout/pay-master-card.png/image/layout/pay-airwallex.png/image/layout/pay-alipay.png

地址

Level 10b, 144 Edward Street, Brisbane CBD(Headquarter)
Level 2, 171 La Trobe St, Melbourne VIC 3000
四川省成都市武侯区桂溪街道天府大道中段500号D5东方希望天祥广场B座45A13号
Business Hub, 155 Waymouth St, Adelaide SA 5000

Disclaimer

footer-disclaimerfooter-disclaimer

JR Academy acknowledges Traditional Owners of Country throughout Australia and recognises the continuing connection to lands, waters and communities. We pay our respect to Aboriginal and Torres Strait Islander cultures; and to Elders past and present. Aboriginal and Torres Strait Islander peoples should be aware that this website may contain images or names of people who have since passed away.

匠人学院网站上的所有内容,包括课程材料、徽标和匠人学院网站上提供的信息,均受澳大利亚政府知识产权法的保护。严禁未经授权使用、销售、分发、复制或修改。违规行为可能会导致法律诉讼。通过访问我们的网站,您同意尊重我们的知识产权。 JR Academy Pty Ltd 保留所有权利,包括专利、商标和版权。任何侵权行为都将受到法律追究。查看用户协议

© 2017-2025 JR Academy Pty Ltd. All rights reserved.

ABN 26621887572