# Nuxt 4 Trong Năm 2026: Cấu Trúc Thư Mục app/ Mới Và Hướng Dẫn Di Chuyển Từ Nuxt 3 > Hướng dẫn đầy đủ di chuyển từ Nuxt 3 sang Nuxt 4 với cấu trúc thư mục app/ mới, singleton data fetching, shallow reactivity mặc định, và phân tách ngữ cảnh TypeScript. - Published: 2026-05-01 - Updated: 2026-05-01 - Author: SharpSkill - Tags: nuxt, vue, migration, typescript, tutorial - Reading time: 9 min --- Nuxt 4 đánh dấu bước tiến hóa quan trọng của framework Vue dành cho phát triển ứng dụng full-stack. Được phát hành vào tháng 7 năm 2025 và hiện tại đã đạt phiên bản 4.4, bản nâng cấp này tập trung vào việc cải thiện cấu trúc dự án, tối ưu hiệu suất data fetching, và tăng cường type safety thông qua phân tách ngữ cảnh TypeScript. Khác với bước nhảy lớn từ Nuxt 2 sang Nuxt 3, quá trình di chuyển lần này được thiết kế để diễn ra từng bước một, cho phép các dự án production chuyển đổi mà không cần viết lại toàn bộ mã nguồn. Bài viết này trình bày chi tiết các thay đổi cốt lõi trong Nuxt 4 — từ cấu trúc thư mục `app/` mới đến cơ chế singleton data fetching, shallow reactivity mặc định, và các breaking changes trong quản lý head. Mỗi phần bao gồm mã minh họa thực tế để các nhà phát triển có thể áp dụng trực tiếp vào dự án của mình. Đối với những ai đang chuẩn bị cho [câu hỏi phỏng vấn Vue/Nuxt](/technologies/vue-nuxt/interview-questions/nuxt-fundamentals), việc nắm vững các khái niệm này sẽ là lợi thế cạnh tranh đáng kể trong năm 2026. > **Di Chuyển Tự Động Với Codemod** > > Đội ngũ phát triển Nuxt đã hợp tác với Codemod để tự động hóa phần lớn các bước di chuyển. Chạy lệnh `npx codemod@latest nuxt/4/migration-recipe` để xử lý việc tái cấu trúc thư mục, cập nhật cú pháp data fetching, và thay thế các API không còn được hỗ trợ một cách tự động. Công cụ này phân tích mã nguồn hiện tại và áp dụng các phép biến đổi chính xác, giảm thiểu đáng kể thời gian và rủi ro trong quá trình nâng cấp. ## Cấu Trúc Thư Mục app/ Mới Trong Nuxt 4 Thay đổi dễ nhận thấy nhất khi chuyển sang Nuxt 4 là việc toàn bộ mã nguồn ứng dụng được di chuyển vào thư mục `app/`. Sự phân tách này giải quyết một vấn đề thực tế mà nhiều dự án lớn gặp phải: các file watcher trên Linux và Windows hoạt động hiệu quả hơn đáng kể khi mã nguồn ứng dụng nằm trong một thư mục con riêng biệt, thay vì trộn lẫn với `node_modules/`, `.git/`, và các tệp cấu hình ở thư mục gốc. Cấu trúc mới tuân theo sơ đồ sau: ```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 ``` Thư mục `shared/` là một bổ sung đáng chú ý trong Nuxt 4. Bất kỳ composable, utility, hoặc type definition nào được đặt trong `shared/` sẽ được auto-import trong cả ứng dụng Vue phía client lẫn Nitro server. Điều này loại bỏ nhu cầu import thủ công khi chia sẻ validation schema, hằng số, hoặc các hàm tiện ích giữa các ngữ cảnh khác nhau — một nhu cầu rất phổ biến trong các ứng dụng full-stack. Việc phân tách này không chỉ cải thiện hiệu suất công cụ phát triển mà còn tạo ra ranh giới rõ ràng giữa logic nghiệp vụ và cấu hình dự án. Các nhà phát triển mới tham gia dự án có thể nhanh chóng định hướng được cấu trúc mã nguồn: `app/` chứa toàn bộ giao diện người dùng, `server/` chứa API endpoints, `shared/` chứa code dùng chung, và các tệp cấu hình nằm ở thư mục gốc. Cấu trúc này cũng tương đồng với cách tổ chức của nhiều framework hiện đại khác, giúp giảm đường cong học tập cho các nhà phát triển chuyển đổi giữa các công nghệ. ## Các Bước Di Chuyển Từ Nuxt 3 Sang Nuxt 4 Quá trình nâng cấp bắt đầu với lệnh upgrade chính thức. Nuxt 4 tự động phát hiện cấu trúc phẳng của Nuxt 3 và tiếp tục hoạt động bình thường, cho phép quá trình di chuyển diễn ra dần dần mà không gây gián đoạn. ```bash npx nuxt upgrade --dedupe ``` Lệnh này nâng cấp Nuxt lên phiên bản mới nhất và loại bỏ các dependency trùng lặp trong `node_modules/`. Flag `--dedupe` đặc biệt quan trọng vì Nuxt 4 yêu cầu các phiên bản nhất quán của Vue, Vue Router, và các package liên quan. Sau khi nâng cấp package, bước tiếp theo là di chuyển các tệp ứng dụng vào thư mục `app/`: ```bash npx codemod@latest nuxt/4/file-structure ``` Codemod này tự động di chuyển `assets/`, `components/`, `composables/`, `layouts/`, `middleware/`, `pages/`, `plugins/`, `utils/`, `app.vue`, `error.vue`, và `app.config.ts` vào `app/`. Các tệp thuộc về thư mục gốc — bao gồm `nuxt.config.ts`, `server/`, `public/`, và `content/` — vẫn giữ nguyên vị trí. Codemod cũng cập nhật các đường dẫn import tương đối nếu cần thiết. Đối với các dự án lớn cần trì hoãn việc tái cấu trúc thư mục, có thể thiết lập cấu hình tường minh để Nuxt 4 hoạt động với cấu trúc phẳng: ```typescript // nuxt.config.ts export default defineNuxtConfig({ srcDir: '.', dir: { app: 'app' }, }) ``` Cấu hình này yêu cầu Nuxt 4 phân giải các tệp từ thư mục gốc của dự án, khớp chính xác với hành vi của Nuxt 3. Đây là giải pháp tạm thời hữu ích cho các đội ngũ cần thời gian để kiểm tra và điều chỉnh CI/CD pipeline, đường dẫn deploy, hoặc cấu hình Docker trước khi áp dụng cấu trúc mới hoàn toàn. ## Lớp Data Fetching Singleton Và Reactive Keys Nuxt 4 thay đổi căn bản cách `useAsyncData` và `useFetch` quản lý dữ liệu. Trong Nuxt 3, mỗi lần gọi composable tạo ra một reactive reference độc lập. Nuxt 4 chuyển sang mô hình singleton: nhiều component gọi cùng một key giờ đây chia sẻ một reactive reference duy nhất thay vì duy trì các bản sao riêng biệt. ```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] }, }, ) } ``` Ba thay đổi nổi bật trong lớp data mới này: - **Shared refs**: gọi `useProductData('abc')` trong hai component trả về cùng các ref `data`, `error`, và `status`. Cập nhật ở một nơi sẽ phản ánh tức thời ở nơi còn lại, loại bỏ hoàn toàn tình trạng dữ liệu không đồng bộ giữa các phần của giao diện. - **Automatic cleanup**: khi component cuối cùng sử dụng một key bị unmount, Nuxt giải phóng dữ liệu liên quan khỏi bộ nhớ, ngăn ngừa memory leak trong các ứng dụng SPA phức tạp. - **Reactive keys**: bọc key trong computed hoặc ref sẽ kích hoạt refetch tự động khi giá trị thay đổi, không cần viết watcher thủ công. Callback `getCachedData` nhận thêm context object với thuộc tính `cause` (`'initial'`, `'refresh:hook'`, `'refresh:manual'`, hoặc `'watch'`), cho phép kiểm soát chi tiết khi nào cần phục vụ dữ liệu cache và khi nào cần fetch dữ liệu mới từ server. Đây là câu hỏi phỏng vấn thường gặp về caching strategy trong các ứng dụng Nuxt hiện đại. > **Giá Trị Mặc Định Thay Đổi Từ null Sang undefined** > > Các giá trị `data` và `error` từ `useAsyncData` và `useFetch` giờ đây mặc định là `undefined` thay vì `null` như trong Nuxt 3. Cần rà soát và cập nhật tất cả các kiểm tra `=== null` thành `=== undefined` hoặc sử dụng so sánh lỏng (`== null`) để bao quát cả hai trường hợp. Thay đổi này ảnh hưởng đến cả template conditionals và logic trong ` ``` Đối với hầu hết các trang hiển thị dữ liệu chỉ đọc — bao gồm dashboard, danh sách sản phẩm, và trang bài viết — shallow reactivity hoạt động hoàn toàn bình thường mà không cần bất kỳ thay đổi mã nào. Tùy chọn `deep: true` vẫn khả dụng cho các form hoặc giao diện tương tác cần mutate trực tiếp các thuộc tính lồng nhau. Trong các buổi phỏng vấn kỹ thuật, câu hỏi về sự khác biệt giữa `ref` và `shallowRef` cùng tác động đến hiệu suất ứng dụng đang trở nên phổ biến hơn. Hiểu rõ cơ chế này giúp các nhà phát triển đưa ra quyết định đúng đắn về chiến lược quản lý trạng thái trong từng tình huống cụ thể. Chi tiết hơn về cơ chế rendering và hydration có thể tham khảo trong [hướng dẫn SSR và static generation](/blog/vue-nuxt/nuxt-3-ssr-static-generation). ## Phân Tách Ngữ Cảnh TypeScript Và Cải Thiện Type Safety Nuxt 4 tạo ra các cấu hình TypeScript riêng biệt cho từng ngữ cảnh trong dự án, thay vì sử dụng một `tsconfig.json` chung cho toàn bộ mã nguồn như Nuxt 3. Sự phân tách này có ý nghĩa thực tiễn quan trọng: IDE không còn đề xuất các API chỉ dành cho server trong mã client, và ngược lại. Bốn ngữ cảnh được phân tách bao gồm: - `.nuxt/tsconfig.app.json` cho mã nguồn ứng dụng Vue - `.nuxt/tsconfig.server.json` cho mã nguồn Nitro server - `.nuxt/tsconfig.shared.json` cho các tiện ích chia sẻ - `.nuxt/tsconfig.node.json` cho cấu hình thời điểm build Một `tsconfig.json` duy nhất tại thư mục gốc của dự án tham chiếu đến cả bốn config: ```json { "files": [], "references": [ { "path": "./.nuxt/tsconfig.app.json" }, { "path": "./.nuxt/tsconfig.server.json" }, { "path": "./.nuxt/tsconfig.shared.json" }, { "path": "./.nuxt/tsconfig.node.json" } ] } ``` Kiểm tra type trong CI cũng thay đổi. Lệnh `vue-tsc` giờ đây yêu cầu flag `-b` (build mode) để xử lý project references chính xác: ```bash # Before (Nuxt 3) nuxt prepare && vue-tsc --noEmit # After (Nuxt 4) nuxt prepare && vue-tsc -b --noEmit ``` Một thay đổi TypeScript quan trọng khác: `compilerOptions.noUncheckedIndexedAccess` được đặt thành `true` theo mặc định. Truy cập phần tử mảng hoặc thuộc tính đối tượng theo index giờ đây trả về `T | undefined`, buộc các nhà phát triển phải xử lý trường hợp giá trị không tồn tại. Thay đổi này phát hiện nhiều lỗi runtime tiềm ẩn tại thời điểm compile, đặc biệt hữu ích cho các dự án enterprise có codebase lớn. Đối với các dự án lớn, sự phân tách ngữ cảnh này cải thiện đáng kể thời gian phản hồi của IDE khi cung cấp autocompletion và phát hiện lỗi. Các nhà phát triển làm việc trên server code không còn bị phân tâm bởi các gợi ý từ client code, và ngược lại. ## Chuẩn Hóa Tên Component Và Vue Router v5 Nuxt 4.3 đã nâng cấp lên Vue Router v5, loại bỏ sự phụ thuộc vào `unplugin-vue-router`. Đối với hầu hết các ứng dụng, việc nâng cấp này diễn ra minh bạch mà không yêu cầu thay đổi mã nguồn. Quy ước đặt tên component giờ đây được chuẩn hóa nhất quán trong tất cả các ngữ cảnh. Một component tại `components/dashboard/MetricsCard.vue` sẽ có tên `DashboardMetricsCard` trong Vue DevTools, ``, và các tiện ích kiểm thử. Hành vi trước đây của Nuxt 3, nơi tên component có thể thay đổi tùy thuộc vào ngữ cảnh sử dụng, không còn áp dụng. ```vue ``` Các dự án sử dụng `` với bộ lọc tên component cần rà soát và cập nhật các tên để khớp với quy ước mới. Đặc biệt lưu ý rằng tên được tạo từ đường dẫn thư mục kết hợp với tên file, viết theo kiểu PascalCase. Ví dụ, `components/admin/user/ProfileCard.vue` sẽ có tên `AdminUserProfileCard`. Trong các buổi phỏng vấn kỹ thuật về Vue và Nuxt, câu hỏi về cách `` hoạt động và cách tối ưu hóa hiệu suất component rendering thường xuyên xuất hiện. Hiểu rõ quy ước đặt tên mới giúp tránh các lỗi khó debug liên quan đến caching component, đặc biệt khi kết hợp với dynamic imports và lazy loading. ## Xử Lý Breaking Changes Trong Quản Lý Head Nuxt 4 đi kèm với Unhead v2, một bản nâng cấp lớn loại bỏ một số thuộc tính và hành vi không còn được hỗ trợ từ `useHead` và `useSeoMeta`. Các thuộc tính `vmid`, `hid`, `children`, và `body` đã bị loại bỏ hoàn toàn. API `useSeoMeta` tiếp tục hỗ trợ reactive getters cho các giá trị động: ```typescript // app/pages/product/[id].vue ``` Đối với các dự án phụ thuộc vào template parameters (ví dụ `%site.title%`) hoặc alias sorting cho thẻ meta, cần cài đặt các plugin này một cách tường minh thay vì dựa vào hành vi mặc định của Nuxt 3: ```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) }, }) ``` Những thay đổi trong Unhead v2 phản ánh xu hướng chung của hệ sinh thái Vue hướng tới các API đơn giản hơn và dễ bảo trì hơn. Việc loại bỏ các thuộc tính lỗi thời giúp giảm kích thước bundle và cải thiện tính dự đoán của hành vi head management. Các nhà phát triển cần kiểm tra kỹ các trang quan trọng cho SEO sau khi nâng cấp để đảm bảo meta tags được render chính xác. > **Lộ Trình Hỗ Trợ Nuxt 3** > > Nuxt 3 tiếp tục nhận các bản vá bảo mật và sửa lỗi nghiêm trọng cho đến ngày 31 tháng 7 năm 2026. Sau ngày đó, Nuxt 3 sẽ chính thức kết thúc vòng đời hỗ trợ và không còn nhận bất kỳ bản cập nhật nào. Các dự án đang chạy trên production nên lập kế hoạch di chuyển ngay từ bây giờ để tránh rủi ro vận hành ứng dụng trên framework đã hết hạn bảo trì. ## Danh Sách Kiểm Tra Di Chuyển Và Các Lỗi Thường Gặp Codemod tự động xử lý phần lớn các thay đổi, nhưng một số mục yêu cầu rà soát và can thiệp thủ công. Dưới đây là các điểm cần lưu ý: - **Loại bỏ `window.__NUXT__`**: thay thế bằng `useNuxtApp().payload`. Đối tượng global này bị xóa sau khi hydration hoàn tất trong Nuxt 4. - **Hook `pages:extend`**: chuyển sang hook `pages:resolved` mới, chạy sau khi quá trình quét page meta hoàn thành, đảm bảo thông tin route đầy đủ hơn. - **Boolean `dedupe` trong refresh**: thay thế `refresh({ dedupe: true })` bằng `refresh({ dedupe: 'cancel' })` và `false` bằng `'defer'`. Kiểu boolean không còn được chấp nhận. - **Inline styles**: chỉ các style của Vue component được inline theo mặc định; CSS global tải dưới dạng các tệp riêng biệt. Thêm `features: { inlineStyles: true }` vào `nuxt.config.ts` để khôi phục hành vi Nuxt 3. - **`clearNuxtState`**: giờ đây đặt lại về giá trị khởi tạo thay vì `undefined`. Sử dụng `clearNuxtState('key', { reset: false })` cho hành vi cũ. - **Module bên thứ ba**: một số module có thể chưa cập nhật để tương thích đầy đủ với Nuxt 4, đặc biệt là những module phụ thuộc vào các API nội bộ đã thay đổi. Trình tự di chuyển thực tiễn cho các ứng dụng production: 1. Chạy `npx nuxt upgrade --dedupe` và xác minh ứng dụng build thành công trên Nuxt 4 2. Chạy codemod toàn diện: `npx codemod@latest nuxt/4/migration-recipe` 3. Di chuyển các tệp vào `app/` bằng file-structure codemod nếu chưa được thực hiện tự động 4. Cập nhật các kiểm tra `null` thành `undefined` trong logic data fetching 5. Kiểm tra các component `` với tên đã chuẩn hóa theo quy ước mới 6. Cập nhật kiểm tra type trong CI pipeline sử dụng `vue-tsc -b --noEmit` 7. Chạy toàn bộ test suite và sửa các lỗi TypeScript được phát hiện bởi `noUncheckedIndexedAccess` Đối với các câu hỏi phỏng vấn liên quan đến quá trình migration và best practices, việc hiểu rõ từng bước trong trình tự trên cùng lý do đằng sau mỗi thay đổi sẽ thể hiện sự am hiểu sâu sắc về framework. Tham khảo thêm tại [câu hỏi phỏng vấn Vue/Nuxt](/technologies/vue-nuxt/interview-questions/nuxt-fundamentals) để ôn luyện các chủ đề thường gặp. ## Kết Luận Nuxt 4 mang đến một loạt cải tiến có ý nghĩa thực tiễn cho việc phát triển ứng dụng Vue full-stack, tập trung vào hiệu suất, type safety, và trải nghiệm phát triển tốt hơn: - Cấu trúc thư mục `app/` phân tách rõ ràng mã nguồn ứng dụng khỏi cấu hình, cải thiện hiệu suất file watcher và giúp onboarding nhanh hơn cho các thành viên mới - Singleton data fetching loại bỏ request trùng lặp và đảm bảo tính nhất quán dữ liệu khi nhiều component sử dụng cùng một nguồn dữ liệu - Shallow reactivity mặc định thông qua `shallowRef` cải thiện hiệu suất cho các trang đọc nhiều mà không yêu cầu thay đổi mã trong hầu hết các trường hợp - Phân tách ngữ cảnh TypeScript (app, server, shared, node) loại bỏ rò rỉ type giữa các ngữ cảnh và tăng cường autocompletion trong IDE - Vue Router v5 và Unhead v2 mang đến các API sạch hơn, đi kèm với việc loại bỏ các thuộc tính không còn được hỗ trợ cần kiểm tra trong quá trình di chuyển - Nuxt 3 kết thúc vòng đời hỗ trợ vào ngày 31 tháng 7 năm 2026 — lập kế hoạch di chuyển sớm giúp đảm bảo ứng dụng luôn chạy trên phiên bản được bảo trì tích cực - Lệnh `npx codemod@latest nuxt/4/migration-recipe` tự động hóa phần lớn quá trình, giúp giảm thời gian và rủi ro cho các đội ngũ phát triển --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/vi/blog/vue-nuxt/nuxt-4-directory-structure-migration