CHAPTER 08 / FULL-STACK FRAMEWORK
Next.js / Nuxt:
前後端一起寫
React / Vue 只管前端。但網站要 SEO、要快、要伺服器渲染、要 API endpoint,全部要框架解決。 Next.js(基於 React)和 Nuxt(基於 Vue)是現代全端首選。一個 repo 包前端 + 後端,部署一次上線。
- 會用 Next.js App Router 寫多頁應用
- 分得清 SSR、SSG、ISR、CSR
- 會寫 Server Component、Server Action
- 會寫 API Routes 處理 POST 請求
- 能架一個有後端的部落格 / SaaS MVP
LESSON 8.1
為什麼要用 Next.js?
純 React 的痛點:
- SEO 差:JS 渲染,Google 看到的是空殼
- 首屏慢:要等 JS 下載 + 跑完才有畫面
- 沒後端:要另開 Express / FastAPI 專案
- 要自己配:路由、code splitting、圖片優化都要設定
Next.js 一次解決:檔案路由、SSR/SSG、API endpoint、圖片優化、字型優化都內建。
npx create-next-app@latest my-app
# 選 TypeScript / Tailwind / App Router 都選 yes
LESSON 8.2
App Router:檔案就是路由
app/
├── page.tsx ← /
├── about/
│ └── page.tsx ← /about
├── blog/
│ ├── page.tsx ← /blog
│ └── [slug]/
│ └── page.tsx ← /blog/任何字串
├── (marketing)/ ← 群組(不影響網址)
│ └── pricing/page.tsx
├── api/
│ └── hello/route.ts ← /api/hello
├── layout.tsx ← 共用版型
└── globals.css
page.tsx
// app/page.tsx
export default function HomePage() {
return <h1>首頁</h1>;
}
動態路由
// app/blog/[slug]/page.tsx
export default function BlogPost({
params
}: { params: { slug: string } }) {
return <h1>文章 {params.slug}</h1>;
}
共用 layout
// app/layout.tsx
export default function RootLayout({
children
}: { children: React.ReactNode }) {
return (
<html lang="zh-Hant">
<body>
<Nav />
<main>{children}</main>
<Footer />
</body>
</html>
);
}
LESSON 8.3
渲染策略:SSR / SSG / ISR / CSR
| 策略 | 意思 | 適合 |
|---|---|---|
| SSG | build 時產 HTML | 部落格、行銷頁、文件 |
| SSR | 每次請求即時產 | 個人化、即時資料 |
| ISR | 定期重新生成(快取) | 商品列表、新聞 |
| CSR | 瀏覽器渲染(傳統 SPA) | 後台、儀表板 |
SSG 是「便當前一天做好放冷藏」,SSR 是「客人點了現炒」,ISR 是「半小時做一批熱著」,CSR 是「給你食材自己回家煮」。
App Router 的設定方式
// 預設:靜態(SSG)
export default async function Page() {
const data = await fetch('https://api.x.com/posts');
return <Posts data={data} />;
}
// 改成每次請求
export const dynamic = 'force-dynamic';
// ISR:60 秒重新生成
export const revalidate = 60;
LESSON 8.4
Server Component vs Client Component
App Router 的元件預設在伺服器跑。沒有事件、沒有 useState、可以直接 await。
// Server Component(預設)
export default async function Posts() {
const posts = await db.posts.findMany(); // 直接讀資料庫
return <ul>{posts.map(p => <li key={p.id}>{p.title}</li>)}</ul>;
}
需要互動時加 'use client':
'use client';
import { useState } from 'react';
export default function LikeButton() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(c => c + 1)}>❤ {count}</button>;
}
原則:盡量 Server Component。只在「真的要互動」(onClick、useState、useEffect)才標 client。bundle size 會小很多。
LESSON 8.5
API Routes & Server Actions
API Route(傳統 REST)
// app/api/users/route.ts
import { NextResponse } from 'next/server';
export async function GET() {
const users = await db.users.findMany();
return NextResponse.json(users);
}
export async function POST(req: Request) {
const body = await req.json();
const user = await db.users.create({ data: body });
return NextResponse.json(user, { status: 201 });
}
Server Action(現代)
不用寫 API endpoint,直接從表單呼叫伺服器函式:
async function createPost(formData: FormData) {
'use server';
const title = formData.get('title');
await db.posts.create({ data: { title } });
}
export default function Page() {
return (
<form action={createPost}>
<input name="title" />
<button>送出</button>
</form>
);
}
LESSON 8.6
Nuxt:Vue 版的 Next
Nuxt 3 的概念跟 Next 99% 一樣,名詞不同。
| Next.js | Nuxt 3 |
|---|---|
| app/page.tsx | pages/index.vue |
| app/layout.tsx | layouts/default.vue |
| app/api/x/route.ts | server/api/x.ts |
| Server Action | server route + useFetch |
| middleware.ts | middleware/auth.ts |
npx nuxi@latest init my-app
<!-- pages/index.vue -->
<script setup>
const { data: posts } = await useFetch('/api/posts');
</script>
<template>
<ul>
<li v-for="p in posts" :key="p.id">{{ p.title }}</li>
</ul>
</template>
// server/api/posts.ts
export default defineEventHandler(async () => {
return await db.posts.findMany();
});
LESSON 8.7
常用內建功能
// 圖片優化
import Image from 'next/image';
<Image src="/cat.jpg" width={400} height={300} alt="貓" />
// 字型優化
import { Inter } from 'next/font/google';
const inter = Inter({ subsets: ['latin'] });
// 連結(自動預載)
import Link from 'next/link';
<Link href="/about">關於</Link>
// SEO metadata
export const metadata = {
title: '我的網站',
description: '最棒的',
openGraph: { images: ['/og.png'] }
};
練習:個人部落格
用 Next.js App Router 做一個有後端的部落格:
- 首頁列出所有文章(從 markdown 檔讀取)
/blog/[slug]顯示單篇/admin用 Server Action 新增文章- SEO metadata 動態生成
- 用 ISR,每 60 秒重整
- RSS feed at
/rss.xml
常見卡關 FAQ
// Next 學員最常問的問題
Pages Router 還是 App Router?
新專案一律 App Router。它是未來。Pages Router 只在維護舊專案才碰。
為什麼一加 useState 就報錯?
忘記在檔案最上方加
'use client';。Server Component 不能用 hook。fetch 拿到的資料一直是舊的?
Next 把 fetch 加上強快取。要新鮮資料:
fetch(url, {{ cache: 'no-store' }}) 或 {{ next: {{ revalidate: 60 }} }}。放 Vercel 之外的地方部署可以嗎?
可以!詳見第 11 章。Cloudflare Pages、Zeabur、Railway 都支援。Vercel 整合最順但不便宜。
SvelteKit、Astro、Remix 也是同類型嗎?
是!全端框架百花齊放。Astro 適合內容站、SvelteKit 體積最小、Remix 已併入 React Router。先學 Next/Nuxt 一個,其他一週上手。