Nuxt 4 ในปี 2026: โครงสร้างไดเรกทอรีใหม่และคู่มือการย้ายจาก Nuxt 3
คู่มือการย้ายจาก Nuxt 3 ไปยัง Nuxt 4 ฉบับสมบูรณ์ ครอบคลุมโครงสร้างไดเรกทอรี app/ ใหม่ singleton data fetching, shallow reactivity และการแยกบริบท TypeScript พร้อมตัวอย่างโค้ดจริง

Nuxt 4 นำเสนอการเปลี่ยนแปลงสถาปัตยกรรมครั้งสำคัญที่ชุมชน Vue.js รอคอยมาอย่างยาวนาน หลังจากเปิดตัวในเดือนกรกฎาคม 2025 และปัจจุบันอยู่ที่เวอร์ชัน 4.4 การอัปเดตครั้งใหญ่นี้มาพร้อมกับโครงสร้างไดเรกทอรีที่ออกแบบใหม่ทั้งหมด โดยแยกโค้ดแอปพลิเคชันออกจากไฟล์คอนฟิกอย่างชัดเจน นอกจากนี้ยังเปิดตัว singleton data fetching layer, shallow reactivity เป็นค่าเริ่มต้น และการแยกบริบท TypeScript ที่เข้มงวดยิ่งขึ้น สิ่งที่น่าสนใจคือแนวทางของ Nuxt 4 เน้นการวิวัฒนาการ (evolution) มากกว่าการปฏิวัติ (revolution) ทำให้กระบวนการย้ายจาก Nuxt 3 ราบรื่นกว่าการเปลี่ยนผ่านจาก Nuxt 2 ไปยัง Nuxt 3 อย่างมาก
ทีมพัฒนา Nuxt ได้ร่วมมือกับ Codemod เพื่อทำให้ขั้นตอนการย้ายระบบส่วนใหญ่เป็นอัตโนมัติ สามารถรันคำสั่ง npx codemod@latest nuxt/4/migration-recipe เพื่อจัดการการปรับโครงสร้างไดเรกทอรี อัปเดต data fetching และแทนที่ API ที่เลิกใช้งานแล้วได้โดยอัตโนมัติ
โครงสร้างไดเรกทอรี app/ แบบใหม่ใน Nuxt 4
การเปลี่ยนแปลงที่เห็นได้ชัดที่สุดใน Nuxt 4 คือการย้ายโค้ดแอปพลิเคชันทั้งหมดเข้าไปอยู่ในไดเรกทอรี app/ เป็นค่าเริ่มต้น การตัดสินใจทางสถาปัตยกรรมนี้มีเหตุผลรองรับอย่างชัดเจน กล่าวคือ file watcher บนระบบปฏิบัติการ Linux และ Windows ทำงานได้อย่างมีประสิทธิภาพมากขึ้นเมื่อโค้ดแอปพลิเคชันอยู่ในไดเรกทอรีย่อยเฉพาะ แยกออกจาก node_modules/, .git/ และไฟล์คอนฟิกต่างๆ
โครงสร้างไดเรกทอรีใหม่มีรูปแบบดังนี้:
my-nuxt-app/
├─ app/
│ ├─ assets/
│ ├─ components/
│ ├─ composables/
│ ├─ layouts/
│ ├─ middleware/
│ ├─ pages/
│ ├─ plugins/
│ ├─ utils/
│ ├─ app.vue
│ ├─ app.config.ts
│ └─ error.vue
├─ content/
├─ public/
├─ shared/
├─ server/
└─ nuxt.config.tsไดเรกทอรี shared/ เป็นส่วนเพิ่มเติมที่ควรให้ความสนใจเป็นพิเศษ composable หรือ utility ใดก็ตามที่อยู่ภายใน shared/ จะถูก auto-import ทั้งในแอปพลิเคชัน Vue และ Nitro server โดยอัตโนมัติ สิ่งนี้ช่วยขจัดความจำเป็นในการ import แบบ manual เมื่อต้องการแชร์ validation schema, type definition หรือฟังก์ชัน utility ระหว่างบริบทต่างๆ
การแยกโครงสร้างนี้ไม่เพียงแต่ปรับปรุงประสิทธิภาพของเครื่องมือพัฒนาเท่านั้น แต่ยังสร้างขอบเขตที่ชัดเจนระหว่างโค้ดที่ทำงานบนเบราว์เซอร์ โค้ดที่ทำงานบนเซิร์ฟเวอร์ และโค้ดที่ใช้ร่วมกัน นักพัฒนาที่เพิ่งเข้าร่วมโปรเจกต์สามารถทำความเข้าใจสถาปัตยกรรมของแอปพลิเคชันได้อย่างรวดเร็ว โดยไม่ต้องค้นหาผ่านหลายชั้นของไดเรกทอรีที่ซับซ้อน
ขั้นตอนการย้ายจาก Nuxt 3 ไปยัง Nuxt 4
กระบวนการอัปเกรดเริ่มต้นด้วยคำสั่งเดียว Nuxt สามารถตรวจจับโครงสร้างแบบ flat ที่มีอยู่แล้วโดยอัตโนมัติ และยังคงทำงานได้ตามปกติโดยไม่ต้องเปลี่ยนแปลงอะไร ทำให้การย้ายระบบสามารถดำเนินการทีละขั้นตอนได้
npx nuxt upgrade --dedupeหลังจากอัปเดตแพ็กเกจแล้ว ขั้นตอนถัดไปคือการย้ายไฟล์แอปพลิเคชันเข้าไปในไดเรกทอรี app/:
npx codemod@latest nuxt/4/file-structureCodemod นี้จะย้าย assets/, components/, composables/, layouts/, middleware/, pages/, plugins/, utils/, app.vue, error.vue และ app.config.ts เข้าไปใน app/ ส่วนไฟล์ที่ควรอยู่ที่ root อย่าง nuxt.config.ts, server/, public/ และ content/ จะยังคงอยู่ที่เดิม
สำหรับโปรเจกต์ที่ต้องการเลื่อนการปรับโครงสร้างออกไปก่อน สามารถกำหนด source directory อย่างชัดแจ้งได้:
export default defineNuxtConfig({
srcDir: '.',
dir: { app: 'app' },
})การตั้งค่านี้สั่งให้ Nuxt 4 ค้นหาไฟล์จาก root ของโปรเจกต์ ซึ่งให้พฤติกรรมเหมือนกับ Nuxt 3 ทุกประการ วิธีนี้เป็นทางเลือกชั่วคราวที่เหมาะสำหรับโปรเจกต์ขนาดใหญ่ที่ต้องการเวลาในการทดสอบและปรับแต่งก่อนนำโครงสร้างใหม่มาใช้อย่างเต็มรูปแบบ
Singleton Data Fetching และ Reactive Keys
Nuxt 4 เปลี่ยนแปลงวิธีการจัดการข้อมูลของ useAsyncData และ useFetch อย่างรากฐาน เมื่อหลาย component เรียกใช้ key เดียวกัน ตอนนี้จะแชร์ reactive reference ตัวเดียวกันแทนที่จะรักษาสำเนาที่เป็นอิสระต่อกัน
export function useProductData(productId: string) {
return useAsyncData(
`product-${productId}`,
() => $fetch(`/api/products/${productId}`),
{
getCachedData: (key, nuxtApp, ctx) => {
if (ctx.cause === 'refresh:manual') return undefined
return nuxtApp.payload.data[key]
},
},
)
}การเปลี่ยนแปลงที่โดดเด่นสามประการใน data layer ใหม่นี้:
- Shared refs: การเรียก
useProductData('abc')ในสอง component จะคืนค่า refsdata,errorและstatusตัวเดียวกัน เมื่ออัปเดตที่จุดหนึ่ง อีกจุดหนึ่งก็จะอัปเดตตามโดยอัตโนมัติ - Automatic cleanup: เมื่อ component สุดท้ายที่ใช้ key นั้นถูก unmount Nuxt จะปลดปล่อยข้อมูลที่เกี่ยวข้องออกจากหน่วยความจำ ป้องกัน memory leak ที่มักเกิดขึ้นในแอปพลิเคชัน SPA ที่มีความซับซ้อน
- Reactive keys: การห่อ key ด้วย computed หรือ ref จะทำให้เกิดการ refetch โดยอัตโนมัติเมื่อค่าเปลี่ยนแปลง
Callback getCachedData ตอนนี้รับ context object ที่มี property cause ซึ่งอาจมีค่าเป็น 'initial', 'refresh:hook', 'refresh:manual' หรือ 'watch' ข้อมูลนี้ช่วยให้ควบคุมได้อย่างละเอียดว่าเมื่อไรควรให้บริการข้อมูลจาก cache และเมื่อไรควร fetch ข้อมูลใหม่จากเซิร์ฟเวอร์
สำหรับแอปพลิเคชันที่มีหลาย component แสดงชุดข้อมูลเดียวกัน โมเดล singleton นี้ช่วยลดจำนวน request ที่ซ้ำซ้อนได้อย่างมาก พร้อมทั้งรับประกันความสอดคล้องของ state ทั่วทั้ง UI
ค่า data และ error จาก useAsyncData/useFetch ตอนนี้เริ่มต้นเป็น undefined แทนที่จะเป็น null จำเป็นต้องอัปเดตการตรวจสอบ === null เป็น === undefined หรือใช้ loose equality เพื่อความเข้ากันได้
Shallow Reactivity เป็นค่าเริ่มต้นเพื่อประสิทธิภาพที่ดีขึ้น
Nuxt 4 เปลี่ยน data จาก useAsyncData และ useFetch ให้เป็น shallowRef แทนที่จะเป็น ref ธรรมดา ผลก็คือ Vue จะไม่ทำการ tracking แบบ recursive ลงไปทุก property ที่ซ้อนอยู่ภายใน ซึ่งให้การปรับปรุงประสิทธิภาพที่วัดผลได้อย่างชัดเจน โดยเฉพาะสำหรับ API response ที่มี object ซ้อนลึกหรือ array ขนาดใหญ่
<script setup lang="ts">
const { data: metrics } = await useFetch('/api/dashboard/metrics')
// Direct property mutation won't trigger reactivity
// metrics.value.visits = 100 // Won't trigger re-render
// Replace the entire value to trigger updates
metrics.value = { ...metrics.value, visits: 100 }
// Or opt into deep reactivity for this specific call
const { data: settings } = await useFetch('/api/settings', {
deep: true,
})
</script>สำหรับหน้าจอส่วนใหญ่ที่แสดงข้อมูลแบบ read-only เช่น dashboard, รายการสินค้า หรือหน้าบทความ shallow reactivity ทำงานได้โดยไม่ต้องเปลี่ยนแปลงโค้ดใดๆ ตัวเลือก deep: true ยังคงใช้ได้สำหรับฟอร์มหรือ UI แบบ interactive ที่ต้อง mutate property ที่ซ้อนอยู่โดยตรง
ในการสัมภาษณ์งานเกี่ยวกับ Nuxt คำถามเรื่องความแตกต่างระหว่าง ref กับ shallowRef และผลกระทบต่อประสิทธิภาพของแอปพลิเคชัน กำลังได้รับความนิยมมากขึ้น ความเข้าใจอย่างถ่องแท้เกี่ยวกับกลไกนี้จะช่วยให้นักพัฒนาตัดสินใจได้ถูกต้องเกี่ยวกับกลยุทธ์การจัดการ state
พร้อมที่จะพิชิตการสัมภาษณ์ Vue.js / Nuxt.js แล้วหรือยังครับ?
ฝึกฝนด้วยตัวจำลองแบบโต้ตอบ, flashcards และแบบทดสอบเทคนิคครับ
การแยกบริบท TypeScript และการปรับปรุง Type Safety
Nuxt 4 สร้างคอนฟิก TypeScript แยกต่างหากสำหรับแต่ละบริบทในโปรเจกต์:
.nuxt/tsconfig.app.json-- โค้ดแอปพลิเคชัน Vue.nuxt/tsconfig.server.json-- โค้ด Nitro server.nuxt/tsconfig.shared.json-- utility ที่ใช้ร่วมกัน.nuxt/tsconfig.node.json-- คอนฟิก build-time
การแยกนี้ส่งผลดีอย่างมีนัยสำคัญ IDE จะไม่แนะนำ API ที่มีเฉพาะบน server เมื่อนักพัฒนากำลังเขียนโค้ด client และในทางกลับกัน ข้อผิดพลาดจากการ import ผิดบริบทที่เคยเกิดขึ้นบ่อยจะถูกลดลงอย่างมาก
ไฟล์ tsconfig.json ตัวเดียวที่ root ของโปรเจกต์จะ reference ไปยังทั้งสี่คอนฟิก:
{
"files": [],
"references": [
{ "path": "./.nuxt/tsconfig.app.json" },
{ "path": "./.nuxt/tsconfig.server.json" },
{ "path": "./.nuxt/tsconfig.shared.json" },
{ "path": "./.nuxt/tsconfig.node.json" }
]
}การตรวจสอบ type ใน CI ก็มีการเปลี่ยนแปลงเช่นกัน คำสั่ง vue-tsc ตอนนี้ต้องใช้ flag -b (build mode) เพื่อประมวลผล project references อย่างถูกต้อง:
# Before (Nuxt 3)
nuxt prepare && vue-tsc --noEmit
# After (Nuxt 4)
nuxt prepare && vue-tsc -b --noEmitการเปลี่ยนแปลง TypeScript อีกประการที่ควรทราบคือ compilerOptions.noUncheckedIndexedAccess ถูกตั้งค่าเป็น true โดยค่าเริ่มต้น การเข้าถึงสมาชิกของ array หรือ property ของ object ด้วย index จะคืนค่าเป็น T | undefined ซึ่งช่วยตรวจจับข้อผิดพลาด runtime ที่ซ่อนอยู่ได้ตั้งแต่ขั้นตอน compile
สำหรับโปรเจกต์ระดับ enterprise ที่มี codebase ขนาดใหญ่ การแยกบริบทนี้ช่วยลดเวลารอของ IDE ในการให้บริการ autocompletion และการตรวจจับข้อผิดพลาดได้อย่างเห็นได้ชัด นักพัฒนาที่ทำงานกับ server code จะไม่ถูกรบกวนด้วยคำแนะนำที่ไม่เกี่ยวข้องจาก client code อีกต่อไป
การทำให้ชื่อ Component เป็นมาตรฐานและ Vue Router v5
Nuxt 4.3 ได้อัปเกรดเป็น Vue Router v5 โดยเอา dependency ของ unplugin-vue-router ออก สำหรับแอปพลิเคชันส่วนใหญ่ การอัปเกรดนี้เป็นไปอย่างราบรื่นโดยไม่ต้องเปลี่ยนแปลงโค้ด
แบบแผนการตั้งชื่อ component ได้รับการทำให้เป็นมาตรฐานแล้ว component ที่อยู่ที่ components/dashboard/MetricsCard.vue จะมีชื่อเป็น DashboardMetricsCard อย่างสม่ำเสมอในทุกบริบท ไม่ว่าจะเป็น <KeepAlive>, Vue DevTools หรือ test utilities
<!-- app/pages/dashboard.vue -->
<template>
<NuxtPage :keepalive="{
include: ['DashboardMetricsCard', 'DashboardRecentActivity'],
}" />
</template>โปรเจกต์ที่ใช้ <KeepAlive> พร้อมตัวกรองชื่อ component จำเป็นต้องอัปเดตชื่อให้ตรงกับแบบแผนใหม่นี้ พฤติกรรมก่อนหน้าที่ชื่ออาจเปลี่ยนแปลงตามบริบทจะไม่มีผลอีกต่อไป การทำให้เป็นมาตรฐานนี้ช่วยให้การ debug และ maintenance ง่ายขึ้นในระยะยาว
ในการสัมภาษณ์ทางเทคนิคเกี่ยวกับ Vue และ Nuxt คำถามเรื่องการทำงานของ <KeepAlive> และวิธีการเพิ่มประสิทธิภาพ component มักปรากฏอยู่เสมอ ความเข้าใจเกี่ยวกับแบบแผนการตั้งชื่อใหม่ช่วยหลีกเลี่ยงข้อผิดพลาดที่ยากต่อการ debug ที่เกี่ยวข้องกับ component caching
การจัดการ Breaking Changes ในระบบ Head Management
Nuxt 4 มาพร้อมกับ Unhead v2 ซึ่งลบ property ที่เลิกใช้งานแล้วบางตัวออกจาก useHead และ useSeoMeta:
<script setup lang="ts">
const route = useRoute()
const { data: product } = await useFetch(`/api/products/${route.params.id}`)
useSeoMeta({
title: () => product.value?.name ?? 'Product',
ogTitle: () => product.value?.name ?? 'Product',
description: () => product.value?.description ?? '',
ogImage: () => product.value?.imageUrl ?? '',
})
</script>สำหรับโปรเจกต์ที่ยังพึ่งพา template parameters หรือ alias sorting จำเป็นต้องติดตั้งฟีเจอร์เหล่านี้เป็น plugin อย่างชัดแจ้ง:
import { TemplateParamsPlugin, AliasSortingPlugin } from '@unhead/vue/plugins'
export default defineNuxtPlugin({
setup() {
const unhead = injectHead()
unhead.use(TemplateParamsPlugin)
unhead.use(AliasSortingPlugin)
},
})การเปลี่ยนแปลงใน Unhead v2 สะท้อนถึงแนวโน้มของ ecosystem ของ Vue ที่มุ่งสู่ API ที่เรียบง่ายและง่ายต่อการดูแลรักษา การลบ property ที่ล้าสมัยออกช่วยลดขนาด bundle และปรับปรุงความสามารถในการคาดเดาพฤติกรรมของ head management
Nuxt 3 จะยังคงได้รับ security patch และ critical bug fix ต่อไปจนถึงวันที่ 31 กรกฎาคม 2026 หลังจากวันดังกล่าว Nuxt 3 จะไม่ได้รับการสนับสนุนอีกต่อไป การวางแผนการย้ายระบบตั้งแต่ตอนนี้จะช่วยป้องกันไม่ให้แอปพลิเคชัน production ทำงานบน framework ที่สิ้นสุดอายุการใช้งานแล้ว
รายการตรวจสอบการย้ายระบบและข้อผิดพลาดที่พบบ่อย
แม้ว่า codemod อัตโนมัติจะจัดการกับการเปลี่ยนแปลงส่วนใหญ่ได้ แต่บางรายการต้องการการแก้ไขด้วยตนเอง:
- ลบการใช้
window.__NUXT__: เปลี่ยนเป็นuseNuxtApp().payloadแทน object global นี้ถูกลบออกหลัง hydration ใน Nuxt 4 - Hook
pages:extend: เปลี่ยนเป็น hookpages:resolvedตัวใหม่ที่ทำงานหลังจากสแกน page meta เสร็จสิ้น - Boolean
dedupe: เปลี่ยนrefresh({ dedupe: true })เป็นrefresh({ dedupe: 'cancel' })และfalseเป็น'defer' - Inline styles: เฉพาะ style ของ Vue component เท่านั้นที่ถูก inline โดยค่าเริ่มต้น CSS global จะโหลดเป็นไฟล์แยก เพิ่ม
features: { inlineStyles: true }เพื่อกลับไปใช้พฤติกรรมของ Nuxt 3 clearNuxtState: ตอนนี้ reset กลับไปเป็นค่าเริ่มต้นแทนที่จะเป็นundefinedใช้clearNuxtState('key', { reset: false })สำหรับพฤติกรรมแบบเดิม
ลำดับขั้นตอนการย้ายระบบที่แนะนำสำหรับแอปพลิเคชัน production:
- รัน
npx nuxt upgrade --dedupeและตรวจสอบว่าแอปพลิเคชัน build ได้สำเร็จ - รัน codemod:
npx codemod@latest nuxt/4/migration-recipe - ย้ายไฟล์เข้าไปใน
app/(ดำเนินการอัตโนมัติโดย file-structure codemod) - อัปเดตการตรวจสอบ
nullเป็นundefinedใน logic ของ data fetching - ทดสอบ component
<KeepAlive>กับชื่อที่ได้รับการทำให้เป็นมาตรฐานแล้ว - อัปเดตการตรวจสอบ type ใน CI ให้ใช้
vue-tsc -b --noEmit - รัน test suite ทั้งหมดและแก้ไขข้อผิดพลาด TypeScript ที่ถูกตรวจพบโดย
noUncheckedIndexedAccess
สิ่งสำคัญอีกประการหนึ่งคือการตรวจสอบ module ของบุคคลที่สามอย่างละเอียด module บางตัวอาจยังไม่ได้รับการอัปเดตเพื่อรองรับ Nuxt 4 อย่างสมบูรณ์ โดยเฉพาะ module ที่พึ่งพา API ภายในที่มีการเปลี่ยนแปลง
สำหรับการศึกษาเพิ่มเติมเกี่ยวกับแนวคิดของ Vue และ Nuxt สามารถดูเนื้อหา คำถามสัมภาษณ์ Vue/Nuxt บน SharpSkill นอกจากนี้ คู่มือ SSR และ static generation ยังให้ข้อมูลพื้นฐานเกี่ยวกับกลยุทธ์การ render ที่ยังคงมีความเกี่ยวข้องใน Nuxt 4
บทสรุป
การย้ายจาก Nuxt 3 ไปยัง Nuxt 4 นำมาซึ่งการเปลี่ยนแปลงหลายประการที่นักพัฒนา Vue.js ทุกคนควรทำความเข้าใจอย่างถ่องแท้:
- Nuxt 4.4 ที่เปิดตัวในเดือนเมษายน 2026 ได้ทำให้โครงสร้างไดเรกทอรี
app/, singleton data fetching และการแยกบริบท TypeScript เป็นค่าเริ่มต้นที่พร้อมใช้งานใน production - คำสั่ง
npx codemod@latest nuxt/4/migration-recipeทำให้การปรับโครงสร้างไดเรกทอรี การแทนที่ API ที่เลิกใช้งาน และการอัปเดต data fetching เป็นไปโดยอัตโนมัติ - Shallow reactivity ผ่าน
shallowRefปรับปรุงประสิทธิภาพสำหรับหน้าจอที่อ่านข้อมูลเป็นหลักโดยไม่ต้องเปลี่ยนแปลงโค้ดในกรณีส่วนใหญ่ - คอนฟิก TypeScript แยกตามบริบท (app, server, shared, node) ขจัดการรั่วไหลของ type ระหว่างบริบทและปรับปรุง autocompletion ใน IDE
- Nuxt 3 สิ้นสุดอายุการใช้งานในวันที่ 31 กรกฎาคม 2026 การดำเนินการย้ายระบบก่อนวันดังกล่าวจะช่วยให้แอปพลิเคชันยังคงทำงานบนเวอร์ชันที่ได้รับการสนับสนุนและดูแลรักษาอย่างต่อเนื่อง
- Vue Router v5 และ Unhead v2 นำเสนอ API ที่สะอาดยิ่งขึ้น พร้อมกับการลบ property ที่เลิกใช้งานซึ่งต้องตรวจสอบในระหว่างกระบวนการย้ายระบบ
เริ่มฝึกซ้อมเลย!
ทดสอบความรู้ของคุณด้วยตัวจำลองสัมภาษณ์และแบบทดสอบเทคนิคครับ
แท็ก
แชร์
บทความที่เกี่ยวข้อง

Vue 3 Pinia vs Vuex: State Management สมัยใหม่และคำถามสัมภาษณ์ 2026
วิเคราะห์เปรียบเทียบ Pinia กับ Vuex: สถาปัตยกรรม, TypeScript, Composition API, ประสิทธิภาพ, กลยุทธ์การย้าย และคำถามสัมภาษณ์ Vue state management 2026

Nuxt 3: SSR และการสร้างเพจแบบ Static คู่มือฉบับสมบูรณ์
เชี่ยวชาญ SSR และการสร้างเพจแบบ Static ด้วย Nuxt 3 ตั้งแต่ useFetch ไปจนถึง route rules เรียนรู้วิธีเพิ่มประสิทธิภาพแอปพลิเคชัน Vue.js

คำถามสัมภาษณ์ Vue.js: 25 ข้อสำคัญเพื่อคว้างาน
เตรียมสัมภาษณ์ Vue.js ด้วย 25 คำถามสำคัญ ตั้งแต่ระบบ reactivity ถึง composables เพื่อพิชิตการสัมภาษณ์ครั้งต่อไป