1.Next.js是什麼?
Next.js 是一個用於生產的 React 框架,提供 SSR、SSG、基於檔案的路由、API 路由和許多其他開箱即用的功能。由維塞爾開發。
| 特點 | 反應(Vite) | Next.js |
|---|---|---|
| 渲染 | 僅限客戶端 | SSR、SSG、ISR、串流媒體 |
| 路由 | React 路由器(手動) | 基於文件(內建) |
| 搜尋引擎優化 | 差(SPA) | 優(SSR/SSG) |
| 應用程式介面 | 獨立後端 | 路線處理程序(內建) |
| 圖片/字體 | 手動優化 | 下一個/圖像,下一個/字體 |
| 部署 | 任何靜態主機 | Vercel、Docker、任何 Node.js 主機 |
2. 設定
npx create-next-app@latest my-app --typescript --tailwind --app --src-dir
cd my-app
npm run dev
3. App Router-基於檔案的路由
src/app/
├── layout.tsx ← Root layout (required)
├── page.tsx ← Home page (/)
├── loading.tsx ← Loading UI
├── error.tsx ← Error UI
├── not-found.tsx ← 404 page
├── globals.css
│
├── about/
│ └── page.tsx ← /about
│
├── blog/
│ ├── page.tsx ← /blog
│ └── [slug]/
│ └── page.tsx ← /blog/:slug (dynamic)
│
├── dashboard/
│ ├── layout.tsx ← Nested layout
│ ├── page.tsx ← /dashboard
│ ├── settings/
│ │ └── page.tsx ← /dashboard/settings
│ └── [...catchAll]/
│ └── page.tsx ← /dashboard/anything/else
│
└── (marketing)/ ← Route group (no URL segment)
├── layout.tsx
├── pricing/
│ └── page.tsx ← /pricing
└── features/
└── page.tsx ← /features
4. 佈局
// src/app/layout.tsx — Root Layout
import type { Metadata } from 'next';
import { Inter } from 'next/font/google';
import './globals.css';
const inter = Inter({ subsets: ['latin'] });
export const metadata: Metadata = {
title: { default: 'My App', template: '%s | My App' },
description: 'My awesome Next.js application',
};
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="vi">
<body className={inter.className}>
<Header />
<main>{children}</main>
<Footer />
</body>
</html>
);
}
// src/app/dashboard/layout.tsx — Nested Layout
export default function DashboardLayout({ children }: { children: React.ReactNode }) {
return (
<div className="flex">
<Sidebar />
<div className="flex-1">{children}</div>
</div>
);
}
5. 頁面
// src/app/page.tsx — Home page
export default function HomePage() {
return <h1>Welcome to My App</h1>;
}
// src/app/blog/[slug]/page.tsx — Dynamic route
interface Props {
params: Promise<{ slug: string }>;
}
export default async function BlogPost({ params }: Props) {
const { slug } = await params;
const post = await getPost(slug);
if (!post) notFound(); // → not-found.tsx
return (
<article>
<h1>{post.title}</h1>
<div dangerouslySetInnerHTML={{ __html: post.content }} />
</article>
);
}
// Generate static params for SSG
export async function generateStaticParams() {
const posts = await getAllPosts();
return posts.map(post => ({ slug: post.slug }));
}
6. 載入和錯誤 UI
// src/app/dashboard/loading.tsx
export default function Loading() {
return <div className="animate-pulse">Loading dashboard...</div>;
}
// src/app/dashboard/error.tsx
'use client'; // Error components must be Client Components
import { useEffect } from 'react';
export default function Error({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
useEffect(() => {
console.error(error);
}, [error]);
return (
<div>
<h2>Something went wrong!</h2>
<button onClick={reset}>Try again</button>
</div>
);
}
7. 導航
import Link from 'next/link';
import { useRouter, usePathname } from 'next/navigation';
// Link component (prefetching built-in)
<Link href="/about">About</Link>
<Link href={`/blog/${post.slug}`}>{post.title}</Link>
<Link href="/dashboard" prefetch={false}>Dashboard</Link>
// Programmatic navigation
'use client';
function SearchForm() {
const router = useRouter();
const pathname = usePathname();
const handleSubmit = (query: string) => {
router.push(`/search?q=${query}`);
// router.replace('/dashboard'); // Replace history
// router.back(); // Go back
// router.refresh(); // Re-fetch server components
};
}
下一篇: 伺服器元件和客戶端元件 — RSC,「使用客戶端」、邊界和組合模式。