# 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 ``` สำหรับกรณีที่ต้องการการควบคุมมากขึ้น `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