Nuxt 4 và Nitro 3 - Kiến trúc Hybrid Rendering, Islands Architecture, Server Components và Edge Deployment cho Vue Production 2026

Posted on: 4/16/2026 1:21:38 PM

Table of contents

  1. 1. Vì sao meta-framework lại quan trọng hơn bao giờ hết
    1. Meta-framework là gì và khác gì framework?
  2. 2. Kiến trúc tổng thể Nuxt 4: Ba tầng Vue Core, Nuxt Runtime, Nitro Server
    1. 2.1. Vue 3.6 — Vai trò client
    2. 2.2. Nuxt Runtime — Lớp trung gian "vue-aware"
    3. 2.3. Nitro 3 — Server engine đa runtime
  3. 3. Render mode matrix: SSR, CSR, SSG, ISR — và lý do chọn hybrid
    1. Nguyên tắc chọn render mode
  4. 4. Islands Architecture và Server Components: Cắt JS client đến mức tối thiểu
    1. 4.1. Nuxt Islands — "Server-rendered component, hydrate tách biệt"
    2. 4.2. Server Components (.server.vue) — Chạy một lần, gửi HTML
      1. Bẫy thường gặp với Server Component
  5. 5. Nitro Cache Layer: storage, cachedEventHandler và swr
    1. 5.1. Pattern cache-aside cho API route
    2. 5.2. Ba chiến lược invalidation thực dụng
  6. 6. Data layer: useAsyncData, server route và payload isomorphism
  7. 7. Streaming SSR và Suspense: Time to First Byte mượt
  8. 8. Edge Deployment: So sánh preset Nitro và đánh đổi thực tế
    1. Cảnh báo: Edge runtime không phải Node.js
  9. 9. Nitro Tasks: Scheduled jobs không cần thêm infra
  10. 10. Image, Font, SEO — Những chi tiết quyết định chất lượng
    1. 10.1. <NuxtImg> và provider transform
    2. 10.2. @nuxt/fonts — Tự host, tự preload, tự fallback
    3. 10.3. SEO composable tích hợp
  11. 11. Observability và error handling trong production
  12. 12. Kiến trúc project thực tế: Monorepo Nuxt 4 đa ứng dụng
    1. Tách app theo render model, không theo domain nghiệp vụ
  13. 13. Lộ trình học Nuxt 4 cho team Vue truyền thống
  14. 14. Tổng kết: Khi nào chọn Nuxt 4, khi nào không
  15. 15. Nguồn tham khảo

1. Vì sao meta-framework lại quan trọng hơn bao giờ hết

Năm 2026, ranh giới giữa "SPA" và "website tĩnh" gần như đã biến mất. Người dùng mong đợi trang đầu tiên hiện ra dưới 1 giây trên 4G, đồng thời tương tác phức tạp như một ứng dụng desktop sau khi hydrate. Vue 3 đơn thuần không giải quyết được bài toán đó — bạn cần một lớp meta-framework lo việc routing file-based, SSR streaming, data fetching, caching, bundling, image optimization và triển khai edge. Nuxt 4, phát hành tháng 7/2025 và đã ổn định qua bản 4.2 vào đầu 2026, chính là câu trả lời của hệ sinh thái Vue, với trái tim là Nitro 3 — một server engine đa runtime viết trên nền H3.

Điều thú vị ở Nuxt 4 không phải vì nó "thêm tính năng mới". Ngược lại, đội ngũ cốt lõi đã dành cả chu kỳ phát triển để tinh gọn mặc định: cấu trúc thư mục app/ rõ ràng, data layer thống nhất qua useAsyncData, và một render mode matrix đủ linh hoạt để cùng một codebase phục vụ landing page tĩnh lẫn dashboard realtime. Bài viết này mổ xẻ kiến trúc đó dưới lăng kính hệ thống: Hybrid Rendering, Islands Architecture, Server Components, Nitro Cache layer, Edge Deployment và các pattern production thực sự đáng học.

4.2Phiên bản Nuxt ổn định hiện tại (04/2026)
~72%Giảm bundle JS client nhờ Server Components
4Render mode có thể phối hợp trong 1 route tree
15+Preset triển khai Nitro (Vercel, Cloudflare, Deno, Bun...)

Meta-framework là gì và khác gì framework?

Vue là framework UI — nó lo việc reactive DOM. Nuxt là meta-framework — nó lo mọi thứ xung quanh Vue để biến component tree thành một sản phẩm có thể triển khai: server runtime, router, data fetching, head management, middleware, module system, build pipeline. Tương tự Next.js với React, Remix/React Router v7, SvelteKit với Svelte. Meta-framework chính là nơi các quyết định kiến trúc hệ thống (cache, streaming, edge) được "đóng gói" thành API khai báo.

2. Kiến trúc tổng thể Nuxt 4: Ba tầng Vue Core, Nuxt Runtime, Nitro Server

Để tránh nhầm lẫn khi đọc docs, hãy nhớ Nuxt 4 được chia thành ba tầng độc lập về concern nhưng phối hợp chặt:

flowchart TB
    subgraph Browser["Browser"]
        CLIENT["Vue 3.6 Runtime
Hydration - Router - Pinia 3"] end subgraph Edge["Edge or Origin"] NITRO["Nitro 3 Server
H3 router - Cache - Tasks"] NUXT["Nuxt Runtime
SSR render - useAsyncData - Hooks"] NITRO --> NUXT end subgraph Build["Build time"] VITE["Vite 6 - Rolldown"] NUXI["Nuxi CLI - Module graph"] end VITE --> NITRO NUXI --> NITRO CLIENT <-- "HTML stream + payload JSON" --> NUXT NITRO <-- "storage - fetch - tasks" --> EXT[("DB - API - KV - Blob")]

Hình 1: Ba tầng của một ứng dụng Nuxt 4 — Vue Core trong trình duyệt, Nuxt Runtime lo SSR, Nitro lo server engine độc lập framework

2.1. Vue 3.6 — Vai trò client

Nuxt 4.2 chuẩn hóa trên Vue 3.6, hỗ trợ bật thử nghiệm Vapor Mode cho từng component qua <script setup vapor>. Trong ngữ cảnh Nuxt, Vapor không thay Virtual DOM toàn cục mà chạy song song: các trang landing, form đơn giản có thể opt-in để giảm bundle 40–60%, trong khi phần admin phức tạp giữ VDOM như cũ. Nuxt đảm nhiệm việc hydrate đúng mode dựa trên metadata Vite sinh ra lúc build.

2.2. Nuxt Runtime — Lớp trung gian "vue-aware"

Tầng này là thứ bạn viết 90% thời gian: các composable useAsyncData, useFetch, useState, useRoute, definePageMeta, layout và plugin. Nó biết về Vue app instance, về hydration mismatch, về payload serialization. Quan trọng nhất, nó cung cấp dual runtime: cùng một composable chạy cả trên server (Nitro) lẫn trên client, tự động chuyển đổi transport (HTTP nội bộ khi SSR, fetch khi client).

2.3. Nitro 3 — Server engine đa runtime

Đây là phần thú vị nhất về mặt system design. Nitro 3 không phụ thuộc Vue. Nó là một HTTP server viết bằng H3 router, sinh ra build tối ưu cho mười lăm preset khác nhau: Node.js, Bun, Deno Deploy, Cloudflare Workers, Vercel Edge, Netlify Functions, AWS Lambda, Firebase, Azure, Fastly, DigitalOcean App Platform... Cùng một codebase Nuxt có thể đổi target chỉ bằng flag NITRO_PRESET=cloudflare-pages khi build. Điều này giải phóng team khỏi vendor lock-in ở tầng infrastructure.

3. Render mode matrix: SSR, CSR, SSG, ISR — và lý do chọn hybrid

Sai lầm phổ biến khi bắt đầu với Nuxt 4 là coi "SSR" và "SSG" là hai chế độ tổng thể. Thực tế, Nuxt cho phép khai báo render mode cho từng route, tạo thành một ma trận:

ModeThời điểm render HTMLKhi nên dùngChi phí compute
SSR (Server-Side Rendering)Mỗi requestTrang cá nhân hóa theo user, checkout, dashboardCao — compute mỗi hit
SSG (Static Site Generation)Build timeLanding, docs, blog, marketing~0 runtime
ISR (Incremental Static Regen)Build + background revalidateProduct page, bài blog có lượt xemThấp — cache HTML với TTL
SWR (Stale-While-Revalidate)Cache + async refreshTrang vừa cá nhân vừa cần nhanhThấp — cache + refresh nền
CSR (Client-only)Không render serverAdmin, công cụ nội bộ, canvas-heavy0 server — nặng client

Khai báo trong nuxt.config.ts cực kỳ gọn — bạn không cần viết route handler thủ công:

// nuxt.config.ts
export default defineNuxtConfig({
  routeRules: {
    '/':           { prerender: true },                          // SSG
    '/blog/**':    { isr: 3600 },                                // ISR - regen mỗi giờ
    '/product/**': { swr: 600 },                                 // SWR - stale 10 phut
    '/account/**': { ssr: true, headers: { 'cache-control': 'private, no-store' } },
    '/admin/**':   { ssr: false },                               // CSR
    '/api/**':     { cors: true, security: { xssProtection: true } }
  }
})

Nitro biết dịch các rule này thành build artifact phù hợp: route prerender sẽ có HTML sẵn trong .output/public, route isr sẽ sinh handler có s-maxage + stale-while-revalidate, route ssr: false sẽ fallback sang shell HTML rỗng để SPA tự boot. Đây là nơi hybrid rendering thực sự phát huy: một site có thể vừa là static site vừa là app đồng thời.

Nguyên tắc chọn render mode

Hãy hỏi hai câu: (1) Nội dung có phụ thuộc user cụ thể không? (2) Nội dung thay đổi ở tần suất nào? Nếu không phụ thuộc user và thay đổi hiếm → SSG. Nếu không phụ thuộc user nhưng thay đổi hàng giờ → ISR. Nếu phụ thuộc user nhưng phần lớn giống nhau → SWR với vary. Nếu phụ thuộc user ở mọi byte → SSR. Nếu là công cụ phức tạp, không cần SEO → CSR. Đừng chọn SSR "cho chắc" vì nó đắt gấp 10–50 lần ISR ở tải cao.

4. Islands Architecture và Server Components: Cắt JS client đến mức tối thiểu

Kể từ Nuxt 3.9, và ổn định hoàn toàn trong Nuxt 4, có hai khái niệm dễ nhầm lẫn cần tách bạch rõ:

4.1. Nuxt Islands — "Server-rendered component, hydrate tách biệt"

Một Island là Vue component được render trên server, nhưng khi chuyển về client thì chỉ hydrate phần của nó như một cô đảo nhỏ trong biển HTML tĩnh. File đặt trong components/islands/ hoặc bọc bằng <NuxtIsland name="..." />. Thích hợp cho widget comment, counter, form subscribe nhúng giữa bài viết markdown SSG.

4.2. Server Components (.server.vue) — Chạy một lần, gửi HTML

Một Server Component được đặt tên MyWidget.server.vue. Code của nó không bao giờ được đóng gói vào bundle client — nó chỉ chạy trên Nitro, render ra HTML và gửi xuống dưới dạng fragment. Không có reactivity phía client, không hydrate. Dùng để nhúng nội dung lấy từ DB, markdown preview, chart tĩnh, bảng báo cáo read-only.

flowchart LR
    A["Request GET /article/42"] --> B{"Route rule"}
    B -- "isr 3600" --> C["Check Nitro cache"]
    C -- miss --> D["SSR full page"]
    D --> E["Meet server component ProductCard"]
    E --> F["Run on Nitro only
no JS shipped"] D --> G["Meet island LikeButton"] G --> H["Render HTML + hydrate script"] D --> I["Serialize payload JSON"] I --> J["Stream HTML to browser"] J --> K["Browser hydrate only islands"]

Hình 2: Luồng render hybrid — phần tĩnh đi thẳng xuống browser, server components không tốn byte JS, islands chỉ hydrate cục bộ

Kết quả trên một trang blog trung bình (10 bài viết danh sách + 3 widget): bundle JS client từ ~180KB gzip (all-SSR) giảm còn ~52KB gzip khi tách Server Component và Islands đúng chỗ. Số liệu LCP cải thiện từ 2.3s xuống 1.1s trên 4G Fast — và đây là tối ưu không cần thay đổi component tree, chỉ đổi hậu tố file.

Bẫy thường gặp với Server Component

Server Component không có lifecycle client: không onMounted, không watch, không event handler. Nếu bạn viết @click="..." trong .server.vue, Nuxt sẽ âm thầm bỏ qua (handler không đi vào bundle). Lỗi này rất khó phát hiện trong dev vì có vẻ "hoạt động" khi reload. Nguyên tắc: Server Component chỉ nên là pure render function nhận props, trả HTML — mọi tương tác phải là Island hoặc component thường.

5. Nitro Cache Layer: storage, cachedEventHandler và swr

Điểm mạnh ít được nói tới của Nitro là storage unified API — một abstraction kiểu key-value chạy trên nhiều driver: in-memory, filesystem, Cloudflare KV, Vercel KV, S3, Upstash, Netlify Blobs. Cùng một code:

// server/api/weather/[city].get.ts
export default defineCachedEventHandler(async (event) => {
  const city = getRouterParam(event, 'city')
  const data = await $fetch(`https://api.weatherservice.io/v1/${city}`)
  return data
}, {
  maxAge: 60 * 10,         // cache 10 phut
  staleMaxAge: 60 * 60,    // stale tu 10 den 60 phut van xai
  swr: true,               // serve stale roi refresh nen
  base: 'weather',         // ten storage driver
  getKey: (ev) => getRouterParam(ev, 'city')
})

Khi triển khai local, Nitro tự dùng memory driver; deploy lên Cloudflare Workers sẽ tự bind sang KV namespace nếu được khai báo; lên Vercel sẽ dùng Vercel KV (Upstash Redis dưới mui xe). Điều này cho phép chuyển môi trường mà không đổi dòng code nào — một ví dụ đẹp về portable caching đúng nghĩa.

5.1. Pattern cache-aside cho API route

Dùng trực tiếp useStorage() khi cần kiểm soát tinh vi hơn:

export default defineEventHandler(async (event) => {
  const storage = useStorage('cache')
  const key = `user:${getUserId(event)}:dashboard`
  const cached = await storage.getItem(key)
  if (cached) return cached

  const fresh = await buildDashboard(event)
  await storage.setItem(key, fresh, { ttl: 120 })
  return fresh
})

5.2. Ba chiến lược invalidation thực dụng

  • TTL đơn thuần — Đủ cho 80% use case: blog, product catalog, tỷ giá.
  • Tag-based — Khi nhiều cache entry chia sẻ nguồn: tag user:42 bôi đen khi user đó đổi hồ sơ. Dùng storage.clear('tag:user:42').
  • On-demand revalidation — Webhook từ CMS gọi /api/_revalidate?path=/blog/abc. Nuxt 4 có helper sendNoContent + clearNuxtData để xóa cache ISR theo key route.

6. Data layer: useAsyncData, server route và payload isomorphism

Composable useAsyncData là trái tim của data fetching trong Nuxt. Khi chạy SSR, nó gọi hàm fetch trên server, serialize kết quả vào payload JSON đính kèm HTML; khi chạy client (hydrate + điều hướng SPA), nó tái sử dụng payload hoặc gọi lại API. Một chi tiết quan trọng:

// pages/product/[slug].vue
const route = useRoute()
const { data, error, status, refresh } = await useAsyncData(
  `product-${route.params.slug}`,       // key deterministic - bat buoc
  () => $fetch(`/api/products/${route.params.slug}`),
  {
    default: () => ({ loaded: false }), // tranh null khi SSR
    transform: (raw) => ({ ...raw, priceDisplay: format(raw.price) }),
    dedupe: 'defer'                     // chong race condition khi navigation nhanh
  }
)

Ba lỗi dễ mắc:

  1. Key trùng giữa nhiều page → server gửi payload sai khi hydrate. Giải pháp: luôn đưa route.params hoặc id vào key.
  2. Fetch trong onMounted thay vì useAsyncData → SSR không chạy, gây layout shift và mất SEO. Luôn dùng useAsyncData hoặc useFetch ở top-level setup.
  3. Quên transform → nhận object quá lớn, payload JSON phình ra vài trăm KB vô nghĩa. Luôn "trim" những field không cần cho client.

7. Streaming SSR và Suspense: Time to First Byte mượt

Nuxt 4 bật streaming SSR mặc định trên các preset hỗ trợ (Node, Bun, Vercel Edge, Cloudflare Workers). Khi route có nhiều useAsyncData, Nitro không đợi toàn bộ promise resolve — nó stream HTML shell ngay khi phần trên <Suspense> boundary sẵn sàng, rồi gửi tiếp từng chunk:

sequenceDiagram
    participant B as Browser
    participant N as Nitro edge
    participant U as Upstream API
    B->>N: GET /dashboard
    N->>B: HTML shell head + nav + skeleton
    N->>U: fetch stats promise
    N->>U: fetch chart promise
    U-->>N: stats 200ms
    N->>B: chunk stats section + script
    U-->>N: chart 800ms
    N->>B: chunk chart section + script
    N->>B: end stream close head payload

Hình 3: Streaming SSR — TTFB còn ~50ms nhờ shell HTML đi trước, các section async đi sau theo thứ tự hoàn tất

Để tận dụng streaming, hãy bọc các phần có async riêng trong <Suspense> với fallback là skeleton. TTFB của trang dashboard giảm từ ~1.2s xuống ~80ms trong benchmark nội bộ của nhóm Nuxt — bởi server không còn chờ API chậm nhất nữa.

8. Edge Deployment: So sánh preset Nitro và đánh đổi thực tế

PresetRuntimeƯu điểmGiới hạn đáng nhớ
Node.jsNode 22Tương thích full, npm nguyên bảnCold start cao, cần VPS
BunBun 1.2Nhanh nhất khi chạy native BunMột số npm package còn lạ
Vercel EdgeV8 IsolatesGlobal POP, low cold startKhông có fs, limit 1MB per function
Cloudflare WorkersV8 Isolates300+ POP, KV/D1/R2 bindingCPU time 50ms (pay plan lên 30s)
Deno DeployDeno 2TS native, secure by defaultMột số node builtin vẫn shim
AWS LambdaNode 22Gắn sâu VPC, mature IAMCold start 300–800ms, deploy phức tạp

Quy tắc chọn preset thực dụng: SaaS B2C toàn cầu → Cloudflare Workers (latency đều khắp thế giới). SaaS B2B nội bộ doanh nghiệp → Node.js trên VPS hoặc Lambda (VPC, audit log dễ). Website marketing → Vercel (CI/CD mượt, preview deploy). Team đã chạy Bun → Bun preset (nhất quán toolchain). Self-hosted → Node + Docker + Nginx — luôn luôn là lựa chọn an toàn nhất về lock-in.

Cảnh báo: Edge runtime không phải Node.js

Khi target edge preset (Cloudflare, Vercel Edge, Deno Deploy), bạn không có các module Node built-in như fs, net, crypto.randomBytes (dù có Web Crypto), child_process, dns. Thư viện nào ngầm dùng chúng sẽ fail lúc runtime. Luôn test build với NITRO_PRESET=cloudflare-pages nuxt build trước khi deploy, đọc kỹ output Nitro compat để tránh chỉ phát hiện sau khi đã lên production.

9. Nitro Tasks: Scheduled jobs không cần thêm infra

Từ Nitro 2.9 (tích hợp trong Nuxt 4), server/tasks/ cho phép định nghĩa job chạy định kỳ ngay trong codebase:

// server/tasks/cleanup-old-drafts.ts
export default defineTask({
  meta: {
    name: 'db:cleanup',
    description: 'Xoa cac draft qua 30 ngay'
  },
  async run({ payload }) {
    const deleted = await $fetch('/api/internal/cleanup-drafts', {
      method: 'POST'
    })
    return { result: `removed ${deleted.count} drafts` }
  }
})

// nuxt.config.ts
export default defineNuxtConfig({
  nitro: {
    experimental: { tasks: true },
    scheduledTasks: {
      '0 2 * * *': ['db:cleanup'],    // 2h sang hang ngay
      '*/15 * * * *': ['cache:warm']
    }
  }
})

Trên preset Cloudflare, Nitro tự map sang Cron Triggers; trên Node self-hosted, dùng cron nhẹ in-process (đủ cho tải trung bình). Đây là trường hợp điển hình của convention over configuration: bạn không còn cần Jenkins riêng hay Cloud Scheduler chỉ để dọn log.

10. Image, Font, SEO — Những chi tiết quyết định chất lượng

10.1. <NuxtImg> và provider transform

Module @nuxt/image cung cấp component tối ưu mặc định: lazy loading, responsive srcset, format negotiation (AVIF → WebP → JPEG), và dịch vụ transform tích hợp với Cloudflare Images, Vercel Image Optimization, imgproxy, Cloudinary, IPX (self-host). Không còn phải viết thẻ <picture> tay — chỉ cần <NuxtImg src="/hero.jpg" width="1200" height="630" format="avif,webp" fit="cover" />.

10.2. @nuxt/fonts — Tự host, tự preload, tự fallback

Module này quét toàn bộ CSS/template tìm font-family, tải bản self-host từ Google/Bunny/Fontsource, sinh preload + subset + font-display fallback để tránh CLS. Một dòng modules: ['@nuxt/fonts'] và cấu hình trong app.config là đủ — không phải copy woff2 tay.

10.3. SEO composable tích hợp

Nuxt 4 gộp useHead, useSeoMeta, defineOgImageComponent, sitemap, robots vào một trải nghiệm liền mạch. OG image động viết như một Vue component thay vì phải render bằng Puppeteer — module nuxt-og-image render qua Satori + Resvg trong Nitro, kể cả trên edge.

11. Observability và error handling trong production

Một Nuxt 4 app production nên bật tối thiểu ba lớp telemetry:

  • Server log có request id — Middleware server/middleware/requestId.ts gắn event.context.requestId, mọi log downstream đều kèm. Dễ trace khi người dùng báo bug.
  • Client error tracking — Sentry, Highlight, PostHog có module chính thức. Nuxt bắn hook vue:errorapp:error; plugin capture cả hai.
  • Web Vitals — Dùng nuxt-delay-hydration (nếu cần) và @nuxtjs/web-vitals để gửi LCP/INP/CLS về backend analytics — tương quan với route, user cohort, device.

Đừng bỏ qua error.vue ở root — đây là trang hiển thị khi một route thất bại cả SSR lẫn hydrate. Một trang lỗi đẹp, có nút "thử lại" và ID lỗi copy-paste được là mức tối thiểu cho sản phẩm nghiêm túc.

12. Kiến trúc project thực tế: Monorepo Nuxt 4 đa ứng dụng

Cấu trúc đề xuất cho một sản phẩm trung bình có web marketing + app SaaS + admin:

repo/
├── apps/
│   ├── marketing/          # Nuxt 4 - prerender toan bo - Cloudflare Pages
│   │   ├── app/
│   │   ├── content/        # Markdown blog qua @nuxt/content
│   │   └── nuxt.config.ts  # routeRules prerender: true
│   ├── app/                # Nuxt 4 - SSR + CSR - Vercel hoac VPS
│   │   ├── app/
│   │   ├── server/
│   │   └── nuxt.config.ts
│   └── admin/              # Nuxt 4 - SPA mode - dang sau auth
├── packages/
│   ├── ui/                 # Vue component share (design system)
│   ├── config/             # ESLint, tsconfig, prettier
│   └── api-types/          # Zod schema + TS types dung chung
├── pnpm-workspace.yaml
└── turbo.json              # hoac nx.json

Điểm then chốt: tách ba app theo render model thay vì nhét hết vào một Nuxt instance. Landing page không cần bundle admin; admin không cần SEO. Tách ra, mỗi app build nhanh hơn, bundle nhỏ hơn, có thể deploy độc lập lên preset phù hợp. Shared state giữa app nếu cần → dùng cookie SSO hoặc WebSocket hub.

Tách app theo render model, không theo domain nghiệp vụ

Instinct ban đầu là "tách app theo domain" (product app, orders app...). Kinh nghiệm cho thấy điều quan trọng hơn với Nuxt là tách theo render model: static, SSR, SPA. Lý do: mỗi model có cấu hình build, preset deploy, và trade-off SEO/performance rất khác nhau. Gộp SSR với SPA buộc phải viết workaround như <ClientOnly> khắp nơi — làm code rối và performance tệ cả hai phía.

13. Lộ trình học Nuxt 4 cho team Vue truyền thống

Tuần 1
Nuxt fundamentals: file-based routing, definePageMeta, layout, useAsyncData vs useFetch. Viết blog tĩnh SSG bằng @nuxt/content.
Tuần 2
Server routes + Nitro: viết API endpoint trong server/api/, middleware auth, event context, defineCachedEventHandler. Hiểu H3 router.
Tuần 3
Route rules + hybrid: thử ISR, SWR, so sánh TTFB với SSR thuần. Bật Islands cho widget, tách Server Component cho block read-only.
Tuần 4
Deploy multi-preset: build cùng codebase ra Node, Cloudflare Workers, Vercel Edge. Đo LCP, cold start, bundle size từng preset.
Tháng 2
Production hardening: observability, error boundary, rate limit, CSP, tasks định kỳ, monorepo split theo render model.

14. Tổng kết: Khi nào chọn Nuxt 4, khi nào không

Nuxt 4 + Nitro 3 không phải là silver bullet. Chọn nó khi: team đã dùng Vue 3, cần SEO, muốn một công nghệ duy nhất lo từ routing đến deploy, chấp nhận convention của Nuxt. Né nó khi: sản phẩm thuần SPA nội bộ (Vite + Vue Router đủ, gọn hơn), team quen React (Next.js vẫn là mặc định React tốt hơn), cần backend phức tạp nhiều service (dùng Nuxt chỉ cho frontend, giữ backend tách biệt).

Điểm đẹp nhất của Nuxt 4 là nó không ép bạn chọn một render mode cho cả app. Bạn mô tả từng route là gì, Nitro lo phần còn lại. Đó là tư duy design-for-variability đã đi đúng: không phải "chọn SSR hay SSG" mà là "khai báo tính chất, runtime sẽ phối hợp". Với một kiến trúc phân tầng sạch — Vue Core, Nuxt Runtime, Nitro Server — mỗi tầng có thể được nâng cấp độc lập. Vue 3.6 Vapor Mode bật dần cho component thuần, Nitro đổi preset khi chuyển cloud, Nuxt Runtime tiếp tục ổn định ở giữa. Đó là tính chất mà mọi kiến trúc meta-framework hiện đại nên hướng tới.

15. Nguồn tham khảo