# Nuxt 4 ในปี 2026: โครงสร้างไดเรกทอรีใหม่และคู่มือการย้ายจาก Nuxt 3 > คู่มือการย้ายจาก Nuxt 3 ไปยัง Nuxt 4 ฉบับสมบูรณ์ ครอบคลุมโครงสร้างไดเรกทอรี app/ ใหม่ singleton data fetching, shallow reactivity และการแยกบริบท TypeScript พร้อมตัวอย่างโค้ดจริง - Published: 2026-05-01 - Updated: 2026-05-01 - Author: SharpSkill - Tags: nuxt, vue, migration, typescript, tutorial - Reading time: 9 min --- 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/` และไฟล์คอนฟิกต่างๆ โครงสร้างไดเรกทอรีใหม่มีรูปแบบดังนี้: ```text 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 ที่มีอยู่แล้วโดยอัตโนมัติ และยังคงทำงานได้ตามปกติโดยไม่ต้องเปลี่ยนแปลงอะไร ทำให้การย้ายระบบสามารถดำเนินการทีละขั้นตอนได้ ```bash npx nuxt upgrade --dedupe ``` หลังจากอัปเดตแพ็กเกจแล้ว ขั้นตอนถัดไปคือการย้ายไฟล์แอปพลิเคชันเข้าไปในไดเรกทอรี `app/`: ```bash npx codemod@latest nuxt/4/file-structure ``` Codemod นี้จะย้าย `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 อย่างชัดแจ้งได้: ```typescript // nuxt.config.ts 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 ตัวเดียวกันแทนที่จะรักษาสำเนาที่เป็นอิสระต่อกัน ```typescript // app/composables/useProductData.ts 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 จะคืนค่า refs `data`, `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 > **ค่าเริ่มต้นเปลี่ยนจาก null เป็น undefined** > > ค่า 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 ขนาดใหญ่ ```typescript // app/pages/dashboard.vue ``` สำหรับหน้าจอส่วนใหญ่ที่แสดงข้อมูลแบบ read-only เช่น dashboard, รายการสินค้า หรือหน้าบทความ shallow reactivity ทำงานได้โดยไม่ต้องเปลี่ยนแปลงโค้ดใดๆ ตัวเลือก `deep: true` ยังคงใช้ได้สำหรับฟอร์มหรือ UI แบบ interactive ที่ต้อง mutate property ที่ซ้อนอยู่โดยตรง ในการสัมภาษณ์งานเกี่ยวกับ Nuxt คำถามเรื่องความแตกต่างระหว่าง `ref` กับ `shallowRef` และผลกระทบต่อประสิทธิภาพของแอปพลิเคชัน กำลังได้รับความนิยมมากขึ้น ความเข้าใจอย่างถ่องแท้เกี่ยวกับกลไกนี้จะช่วยให้นักพัฒนาตัดสินใจได้ถูกต้องเกี่ยวกับกลยุทธ์การจัดการ state ## การแยกบริบท 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 ไปยังทั้งสี่คอนฟิก: ```json { "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 อย่างถูกต้อง: ```bash # 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` อย่างสม่ำเสมอในทุกบริบท ไม่ว่าจะเป็น ``, Vue DevTools หรือ test utilities ```vue ``` โปรเจกต์ที่ใช้ `` พร้อมตัวกรองชื่อ component จำเป็นต้องอัปเดตชื่อให้ตรงกับแบบแผนใหม่นี้ พฤติกรรมก่อนหน้าที่ชื่ออาจเปลี่ยนแปลงตามบริบทจะไม่มีผลอีกต่อไป การทำให้เป็นมาตรฐานนี้ช่วยให้การ debug และ maintenance ง่ายขึ้นในระยะยาว ในการสัมภาษณ์ทางเทคนิคเกี่ยวกับ Vue และ Nuxt คำถามเรื่องการทำงานของ `` และวิธีการเพิ่มประสิทธิภาพ component มักปรากฏอยู่เสมอ ความเข้าใจเกี่ยวกับแบบแผนการตั้งชื่อใหม่ช่วยหลีกเลี่ยงข้อผิดพลาดที่ยากต่อการ debug ที่เกี่ยวข้องกับ component caching ## การจัดการ Breaking Changes ในระบบ Head Management Nuxt 4 มาพร้อมกับ Unhead v2 ซึ่งลบ property ที่เลิกใช้งานแล้วบางตัวออกจาก `useHead` และ `useSeoMeta`: ```typescript // app/pages/product/[id].vue ``` สำหรับโปรเจกต์ที่ยังพึ่งพา template parameters หรือ alias sorting จำเป็นต้องติดตั้งฟีเจอร์เหล่านี้เป็น plugin อย่างชัดแจ้ง: ```typescript // app/plugins/unhead.ts 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** > > 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`**: เปลี่ยนเป็น hook `pages: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: 1. รัน `npx nuxt upgrade --dedupe` และตรวจสอบว่าแอปพลิเคชัน build ได้สำเร็จ 2. รัน codemod: `npx codemod@latest nuxt/4/migration-recipe` 3. ย้ายไฟล์เข้าไปใน `app/` (ดำเนินการอัตโนมัติโดย file-structure codemod) 4. อัปเดตการตรวจสอบ `null` เป็น `undefined` ใน logic ของ data fetching 5. ทดสอบ component `` กับชื่อที่ได้รับการทำให้เป็นมาตรฐานแล้ว 6. อัปเดตการตรวจสอบ type ใน CI ให้ใช้ `vue-tsc -b --noEmit` 7. รัน test suite ทั้งหมดและแก้ไขข้อผิดพลาด TypeScript ที่ถูกตรวจพบโดย `noUncheckedIndexedAccess` สิ่งสำคัญอีกประการหนึ่งคือการตรวจสอบ module ของบุคคลที่สามอย่างละเอียด module บางตัวอาจยังไม่ได้รับการอัปเดตเพื่อรองรับ Nuxt 4 อย่างสมบูรณ์ โดยเฉพาะ module ที่พึ่งพา API ภายในที่มีการเปลี่ยนแปลง สำหรับการศึกษาเพิ่มเติมเกี่ยวกับแนวคิดของ Vue และ Nuxt สามารถดูเนื้อหา [คำถามสัมภาษณ์ Vue/Nuxt](/technologies/vue-nuxt/interview-questions/nuxt-fundamentals) บน SharpSkill นอกจากนี้ [คู่มือ SSR และ static generation](/blog/vue-nuxt/nuxt-3-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 ที่เลิกใช้งานซึ่งต้องตรวจสอบในระหว่างกระบวนการย้ายระบบ --- 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-4-directory-structure-migration