# Nuxt 3: SSR และการสร้างเพจแบบ Static คู่มือฉบับสมบูรณ์
> เชี่ยวชาญ SSR และการสร้างเพจแบบ Static ด้วย Nuxt 3 ตั้งแต่ useFetch ไปจนถึง route rules เรียนรู้วิธีเพิ่มประสิทธิภาพแอปพลิเคชัน Vue.js
- Published: 2026-02-11
- Updated: 2026-04-28
- Author: SharpSkill
- Tags: nuxt 3, vue.js, ssr, การสร้างเพจแบบ static, ประสิทธิภาพเว็บ
- Reading time: 12 min
---
Nuxt 3 ปรับเปลี่ยนวิธีการสร้างแอปพลิเคชัน Vue.js โดยเสนอโหมดการเรนเดอร์หลายแบบที่เหมาะสมกับกรณีการใช้งานที่แตกต่างกัน ตั้งแต่ Server-Side Rendering (SSR) ไปจนถึงการสร้างเพจแบบ Static และการเรนเดอร์แบบไฮบริด เฟรมเวิร์กนี้ให้ความยืดหยุ่นที่โดดเด่นในการเพิ่มประสิทธิภาพและ SEO
> **ข้อกำหนดเบื้องต้น**
>
> บทช่วยสอนนี้สมมุติว่ามีความรู้พื้นฐานเกี่ยวกับ Vue 3 และ Composition API ความคุ้นเคยกับแนวคิดการเรนเดอร์ฝั่งเซิร์ฟเวอร์มีประโยชน์แต่ไม่จำเป็น เพราะแนวคิดพื้นฐานจะถูกอธิบายตลอดทั้งคู่มือ
## ทำความเข้าใจโหมดการเรนเดอร์ของ Nuxt 3
ก่อนที่จะลงลึกในโค้ด สิ่งสำคัญคือต้องเข้าใจความแตกต่างระหว่างโหมดการเรนเดอร์ที่มี โหมดแต่ละแบบตอบสนองความต้องการเฉพาะในด้านประสิทธิภาพ SEO และประสบการณ์ผู้ใช้
SSR (Server-Side Rendering) สร้าง HTML บนเซิร์ฟเวอร์ในทุกคำขอ การสร้างเพจแบบ Static (SSG) สร้างทุกหน้าไว้ล่วงหน้าตอน build โหมดไฮบริดอนุญาตให้รวมแนวทางเหล่านี้เข้าด้วยกันแบบหน้าต่อหน้า
```typescript
// nuxt.config.ts
// การกำหนดค่าโหมดการเรนเดอร์ต่าง ๆ
export default defineNuxtConfig({
// SSR เปิดใช้งานโดยค่าเริ่มต้น (แนะนำสำหรับ SEO)
ssr: true,
// การสร้างเพจแบบ Static: เรนเดอร์ทุกหน้าไว้ล่วงหน้า
// ใช้ 'npm run generate' เพื่อ build
// target: 'static', // ไวยากรณ์ Nuxt 2
// โหมดไฮบริด: ปรับแต่งได้ต่อ route
routeRules: {
// หน้าแรก: เรนเดอร์ล่วงหน้าและแคชไว้
'/': { prerender: true },
// บล็อก: การสร้างเพจแบบ Static
'/blog/**': { prerender: true },
// Dashboard: เรนเดอร์เฉพาะฝั่ง client
'/dashboard/**': { ssr: false },
// API: ไม่มี pre-rendering
'/api/**': { prerender: false }
}
})
```
การกำหนดค่านี้แสดงให้เห็นพลังของโหมดไฮบริด: แต่ละส่วนของแอปพลิเคชันใช้โหมดการเรนเดอร์ที่เหมาะสมกับความต้องการของตนมากที่สุด
## การดึงข้อมูลด้วย useFetch และ useAsyncData
Nuxt 3 มี composable หลักสองตัวสำหรับการดึงข้อมูลแบบ isomorphic composable เหล่านี้ทำงานได้ทั้งฝั่งเซิร์ฟเวอร์และฝั่ง client พร้อมจัดการ hydration อัตโนมัติ
`useFetch` คือ wrapper รอบ `useAsyncData` ที่ทำให้การเรียก HTTP ง่ายขึ้น `useAsyncData` ให้การควบคุมที่มากขึ้นสำหรับกรณีการใช้งานขั้นสูง
```vue
กำลังโหลดบทความ...
{{ article.title }}
```
สำหรับกรณีที่ต้องการการควบคุมมากขึ้น `useAsyncData` ช่วยให้สามารถเรียกใช้ฟังก์ชัน asynchronous ใด ๆ ได้
```vue
```
composable เหล่านี้ป้องกันการ fetch ซ้ำสองครั้ง: ข้อมูลที่ได้จากเซิร์ฟเวอร์จะถูก serialize ลงใน HTML payload และนำกลับมาใช้ระหว่างการ hydrate ที่ฝั่ง client
## การปรับแต่ง SSR ด้วย server hooks
SSR ของ Nuxt 3 สามารถปรับแต่งได้ผ่าน server hooks hook เหล่านี้ช่วยให้สามารถเข้าแทรกแซงในขั้นตอนต่าง ๆ ของวงจรการเรนเดอร์เพื่อปรับเปลี่ยนพฤติกรรมเริ่มต้น
```typescript
// server/plugins/render-hooks.ts
// ปลั๊กอินเซิร์ฟเวอร์เพื่อปรับแต่งการเรนเดอร์ SSR
export default defineNitroPlugin((nitroApp) => {
// hook ที่ทำงานก่อนการเรนเดอร์ของแต่ละหน้า
nitroApp.hooks.hook('render:html', (html, { event }) => {
// ฉีดสคริปต์หรือเมตาดาต้า
html.head.push(`
`)
})
// hook สำหรับการจัดการแคชการเรนเดอร์
nitroApp.hooks.hook('render:response', (response, { event }) => {
// เพิ่ม header แคชแบบกำหนดเอง
const path = event.path
if (path.startsWith('/blog/')) {
// แคชระยะยาวสำหรับบทความบล็อก
response.headers['Cache-Control'] = 'public, max-age=3600, s-maxage=86400'
} else if (path.startsWith('/api/')) {
// ไม่มีแคชสำหรับ API
response.headers['Cache-Control'] = 'no-store'
}
})
})
```
> **ประสิทธิภาพ SSR**
>
> hook `render:response` เหมาะอย่างยิ่งสำหรับการนำกลยุทธ์แคช HTTP มาใช้ การรวม SSR กับ CDN ที่เคารพ header Cache-Control ช่วยให้สามารถให้บริการหน้าที่เรนเดอร์ล่วงหน้าโดยยังคงรักษาความสามารถในการยกเลิกแคชได้
## การสร้างเพจแบบ Static ด้วย nuxt generate
การสร้างเพจแบบ Static สร้างทุกหน้าไว้ล่วงหน้าตอน build วิธีนี้เหมาะสำหรับเว็บไซต์ที่มีเนื้อหาคงที่ เช่น บล็อก เอกสาร หรือเว็บไซต์การตลาด
สำหรับ route แบบไดนามิก Nuxt ต้องรู้ทุก URL ที่จะสร้าง hook `prerender:routes` ช่วยให้กำหนด route เหล่านี้ในเชิงโปรแกรมได้
```typescript
// nuxt.config.ts
// การกำหนดค่าครบถ้วนสำหรับการสร้างเพจแบบ Static
export default defineNuxtConfig({
// เปิดใช้การสร้างเพจแบบ Static
nitro: {
prerender: {
// เปิดการ crawl ลิงก์อัตโนมัติ
crawlLinks: true,
// route ที่ต้องรวมไว้เสมอ
routes: ['/', '/about', '/contact'],
// เพิกเฉย route บางอย่าง
ignore: ['/admin', '/api']
}
},
hooks: {
// hook สำหรับสร้าง route แบบไดนามิก
async 'prerender:routes'(ctx) {
// ดึงบทความจาก API หรือ DB
const articles = await fetch('https://api.example.com/articles')
.then(res => res.json())
// เพิ่ม route บทความ
for (const article of articles) {
ctx.routes.add(`/blog/${article.slug}`)
}
// ดึงหมวดหมู่
const categories = await fetch('https://api.example.com/categories')
.then(res => res.json())
for (const category of categories) {
ctx.routes.add(`/category/${category.slug}`)
}
}
}
})
```
สำหรับโครงการที่มีหน้าจำนวนมาก crawler อัตโนมัติอาจไม่เพียงพอ นี่คือแนวทางที่ทนทานกว่าด้วยไฟล์การกำหนดค่าแยก
```typescript
// server/utils/generate-routes.ts
// ยูทิลิตี้สำหรับสร้างรายการ route แบบไดนามิก
import { prisma } from './prisma'
export async function getAllStaticRoutes(): Promise {
const routes: string[] = []
// บทความบล็อก
const articles = await prisma.article.findMany({
where: { published: true },
select: { slug: true, category: { select: { slug: true } } }
})
for (const article of articles) {
routes.push(`/blog/${article.category.slug}/${article.slug}`)
}
// หน้าสินค้า
const products = await prisma.product.findMany({
where: { active: true },
select: { slug: true }
})
for (const product of products) {
routes.push(`/products/${product.slug}`)
}
// หน้าแท็ก
const tags = await prisma.tag.findMany({
select: { slug: true }
})
for (const tag of tags) {
routes.push(`/tags/${tag.slug}`)
}
return routes
}
```
## การเรนเดอร์แบบไฮบริดด้วย routeRules
การเรนเดอร์แบบไฮบริดเป็นคุณสมบัติเด่นของ Nuxt 3 ช่วยให้กำหนดกฎการเรนเดอร์ที่แตกต่างกันต่อ route ผสมจุดเด่นของ SSR และ SSG เข้าด้วยกัน
```typescript
// nuxt.config.ts
// การกำหนดค่าขั้นสูงสำหรับการเรนเดอร์แบบไฮบริด
export default defineNuxtConfig({
routeRules: {
// หน้าการตลาด: เรนเดอร์ล่วงหน้าและแคชระยะยาว
'/': { prerender: true },
'/pricing': { prerender: true },
'/features/**': { prerender: true },
// บล็อก: ISR (Incremental Static Regeneration)
// ตรวจสอบใหม่ทุกชั่วโมง
'/blog/**': {
isr: 3600,
prerender: true
},
// เอกสาร: แคช CDN พร้อมการตรวจสอบใหม่
'/docs/**': {
swr: 86400, // Stale-while-revalidate
prerender: true
},
// E-commerce: SSR ที่มีแคชระยะสั้น
'/products/**': {
ssr: true,
cache: {
maxAge: 60,
staleMaxAge: 300
}
},
// ตะกร้าและการชำระเงิน: เฉพาะฝั่ง client
'/cart': { ssr: false },
'/checkout/**': { ssr: false },
// Dashboard: โหมด SPA
'/dashboard/**': {
ssr: false,
// ปิดการ pre-rendering
prerender: false
},
// route API: ไม่มีแคชโดยค่าเริ่มต้น
'/api/**': {
cors: true,
headers: {
'Access-Control-Allow-Methods': 'GET,POST,PUT,DELETE'
}
}
}
})
```
การกำหนดค่านี้สะท้อนสถาปัตยกรรมทั่วไปของแอปพลิเคชันสมัยใหม่: หน้าสาธารณะถูกเพิ่มประสิทธิภาพด้วย SSG เพื่อ SEO ในขณะที่ส่วนที่มีปฏิสัมพันธ์ใช้การเรนเดอร์ฝั่ง client
## การเพิ่มประสิทธิภาพด้วยการแคชข้อมูล
นอกเหนือจากการแคชหน้า Nuxt 3 ยังอนุญาตให้แคชข้อมูลที่ดึงมา กลยุทธ์นี้ช่วยลดภาระบน API และปรับปรุงเวลาตอบสนอง
```typescript
// server/api/articles/[slug].get.ts
// API endpoint ที่มีการแคชข้อมูล
import { getArticleBySlug } from '~/server/utils/articles'
export default defineCachedEventHandler(
async (event) => {
const slug = getRouterParam(event, 'slug')
if (!slug) {
throw createError({
statusCode: 400,
message: 'ไม่มี slug'
})
}
const article = await getArticleBySlug(slug)
if (!article) {
throw createError({
statusCode: 404,
message: 'ไม่พบบทความ'
})
}
return article
},
{
// คีย์แคชอ้างอิงจาก slug
getKey: (event) => `article-${getRouterParam(event, 'slug')}`,
// ระยะเวลาแคช: 1 ชั่วโมง
maxAge: 3600,
// Stale-while-revalidate: ให้บริการแคชเก่าระหว่างการอัปเดต
staleMaxAge: 7200,
// การยกเลิกแคชด้วย tag
tags: ['articles']
}
)
```
เพื่อยกเลิกแคชเมื่อเนื้อหาเปลี่ยนแปลง Nuxt มีระบบ tag
```typescript
// server/api/articles/[slug].put.ts
// การอัปเดตบทความพร้อมยกเลิกแคช
import { updateArticle } from '~/server/utils/articles'
export default defineEventHandler(async (event) => {
const slug = getRouterParam(event, 'slug')
const body = await readBody(event)
// อัปเดตบทความ
const article = await updateArticle(slug, body)
// ยกเลิกแคชสำหรับบทความนี้
await useStorage('cache').removeItem(`nitro:handlers:article-${slug}`)
// หรือยกเลิกแคชด้วย tag (บทความทั้งหมด)
// await useStorage('cache').clear('articles')
return article
})
```
> **แคชแบบกระจาย**
>
> ในระบบ production ที่มีหลาย instance แคชในหน่วยความจำไม่เพียงพอ แนะนำให้ตั้งค่า Redis หรือระบบกระจายอื่น ๆ ผ่านการตั้งค่า Nitro เพื่อรับประกันความสอดคล้องระหว่าง instance
## การจัดการ SEO และเมตาดาต้า
SSR ช่วยให้ทำการเพิ่มประสิทธิภาพ SEO ได้โดยสร้างเมตาดาต้าฝั่งเซิร์ฟเวอร์ Nuxt 3 มีหลายวิธีในการจัดการ meta tag แบบไดนามิก
```vue
```
สำหรับหน้าแบบ Static เมตาดาต้าสามารถกำหนดได้โดยตรงในคอมโพเนนต์
```vue
```
## การ Deploy และข้อพิจารณาในระบบ Production
การเลือก deploy ขึ้นอยู่กับโหมดการเรนเดอร์ที่ใช้ ต่อไปนี้คือตัวเลือกหลักและการกำหนดค่า
```typescript
// nuxt.config.ts
// การกำหนดค่าสำหรับสภาพแวดล้อมการ deploy ต่าง ๆ
export default defineNuxtConfig({
nitro: {
// Preset ตามแพลตฟอร์มเป้าหมาย
// preset: 'vercel', // Vercel
// preset: 'netlify', // Netlify
// preset: 'cloudflare-pages', // Cloudflare
// preset: 'node-server', // Node.js แบบดั้งเดิม
// การกำหนดค่าสำหรับ Node.js ใน production
preset: 'node-server',
// การบีบอัด response
compressPublicAssets: true,
// การกำหนดค่าที่เก็บแคช
storage: {
cache: {
driver: 'redis',
url: process.env.REDIS_URL
}
}
},
// ตัวแปรสภาพแวดล้อมขณะรันไทม์
runtimeConfig: {
// ความลับ (ไม่เปิดเผยต่อ client)
apiSecret: process.env.API_SECRET,
// การกำหนดค่าสาธารณะ
public: {
apiBase: process.env.NUXT_PUBLIC_API_BASE || '/api'
}
}
})
```
สำหรับการ deploy แบบ Static คำสั่ง `npm run generate` จะสร้างโฟลเดอร์ `.output/public` ที่พร้อมจะ deploy ไปยังโฮสต์ไฟล์ Static ใด ๆ
```bash
# การสร้างเพจแบบ Static
npm run generate
# เนื้อหาของ .output/public สามารถ deploy ได้ที่:
# - Vercel (ตรวจจับอัตโนมัติ)
# - Netlify (กำหนดค่าอัตโนมัติ)
# - GitHub Pages
# - S3 + CloudFront
# - CDN หรือเซิร์ฟเวอร์ไฟล์ Static ใด ๆ
```
## สรุป
Nuxt 3 มอบความยืดหยุ่นที่ยอดเยี่ยมในการเรนเดอร์แอปพลิเคชัน Vue.js การเลือกระหว่าง SSR, SSG และการเรนเดอร์แบบไฮบริดขึ้นอยู่กับความต้องการเฉพาะของแต่ละโครงการ
**ประเด็นสำคัญ:**
✅ **SSR**: เหมาะสำหรับเนื้อหาแบบไดนามิกที่ต้องการ SEO ที่ดี (e-commerce เว็บไซต์ข่าว)
✅ **SSG**: เหมาะสำหรับเนื้อหาที่คงที่ (บล็อก เอกสาร เว็บไซต์การตลาด)
✅ **ไฮบริด**: แนวทางที่ดีที่สุดสำหรับแอปพลิเคชันที่ซับซ้อนและมีความต้องการหลากหลาย
✅ **useFetch/useAsyncData**: hydration อัตโนมัติและการจัดการแคช
✅ **routeRules**: การกำหนดค่าอย่างละเอียดสำหรับพฤติกรรมของแต่ละ route
✅ **การแคช**: หลายกลยุทธ์เพื่อเพิ่มประสิทธิภาพในระบบ production
การรวมการเรนเดอร์แบบไฮบริดกับกลยุทธ์การแคชที่ออกแบบมาอย่างดีช่วยให้สามารถสร้างแอปพลิเคชันที่มีประสิทธิภาพสูงและเหมาะสมสำหรับ SEO โดยยังคงรักษาการตอบสนองของ Single Page Applications ไว้
---
Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack.
HTML version of this page: https://sharpskill.dev/th/blog/vue-nuxt/nuxt-3-ssr-static-generation