Az a modul, aminek a végén el tudod dönteni, hogy egyáltalán akarod-e. Nem meggyőzni akarlak: megmutatom, mit ad hozzá a Vue SPA + Express felálláshoz, mit veszítesz vele, mikor nem éri meg, és mi az a konkrét dolog, amitől pont a Cloudflare-en lesz több, mint máshol.
A jelenlegi felállás nagyjából így néz ki: egy Vue SPA, amit Vite épít statikus fájlokká, kikerül egy CDN mögé, és böngészőből hívja az Express API-t, ami külön fut. Ez működő architektúra, sok nagy termék így üzemel. Nem az a kérdés, hogy rossz-e, hanem hogy hol fizetsz érte.
Négy visszatérő költség van benne:
any).<title>-re és OG-tagre. SPA-ban ez vagy prerender-szolgáltatás, vagy külön statikus oldalak, vagy sehogy.A Nuxt mind a négyre válaszol. Nézzük meg, hogyan — és utána azt, hogy mit kérdez cserébe.
A Nuxt alapból universal rendering módban fut: az első kérésre a szerveren lefuttatja a Vue-komponenseidet, és kész HTML-t küld. A böngésző ezt azonnal megjeleníti, majd letölti a JS-t, és „ráhidratálja” ugyanazt a komponensfát — innentől minden interakció ugyanúgy kliensoldali, mint eddig.
A route-tábla eltűnik. A könyvtárszerkezet a route-tábla:
app/pages/
index.vue → /
login.vue → /login
projects/index.vue → /projects
projects/[id].vue → /projects/:id
projects/[id]/settings.vue → /projects/:id/settings
[...slug].vue → minden más (404 vagy CMS-oldal)
Ehhez jönnek a layoutok (app/layouts/) és a route middleware-ek (app/middleware/), amiket oldalanként rendelsz hozzá. Nincs többé „elfelejtettem regisztrálni a route-ot” hiba, viszont cserébe a fájlnevek konvenciója lesz a szabályrendszer, amit ismerned kell. A 4. modul ezt vezeti végig.
Ez a legnagyobb napi különbség. Ma valószínűleg így néz ki egy oldalad:
<script setup>
const route = useRoute()
const project = ref(null)
const loading = ref(true)
const error = ref(null)
onMounted(async () => {
try {
const res = await fetch(`/api/projects/${route.params.id}`)
if (!res.ok) throw new Error(res.statusText)
project.value = await res.json()
} catch (e) { error.value = e }
finally { loading.value = false }
})
</script>
<script setup lang="ts">
const route = useRoute()
// lefut a szerveren az SSR alatt, az eredmény a HTML-lel érkezik
const { data: project, status, error } = await useFetch(
() => `/api/projects/${route.params.id}`
)
// és a meta is szerveroldalon dől el — ez az, ami SPA-ban nem megy
useSeoMeta({
title: () => project.value?.name ?? 'Projekt',
description: () => project.value?.summary,
})
</script>
Nem csak rövidebb. A lényeg, hogy a lekérés a szerveren megtörténik, mielőtt a HTML elmegy — így nincs villanó spinner, a robot látja a tartalmat, és a <title> a valódi projektnév. Az 5. modul erről szól, mert van benne bőven csapda is (például hogy mi utazik ki a HTML-be a lekérés eredményéből).
A Nuxt saját szerver-rétege a Nitro. Ugyanabban a projektben, ugyanazokkal a típusokkal írod az API-t:
// routes/projects.js
router.get('/:id', requireAuth, async (req, res, next) => {
try {
const project = await db.project.findFirst({
where: { id: req.params.id, tenantId: req.session.tenantId },
})
if (!project) return res.status(404).json({ error: 'Not found' })
res.json(project)
} catch (err) { next(err) }
})
// server/api/projects/[id].get.ts ← a fájlnév adja az útvonalat ÉS a metódust
export default defineEventHandler(async (event) => {
const { tenantId } = await requireSession(event) // server/utils/, auto-import
const id = getRouterParam(event, 'id')
const project = await useDb(event).query.projects.findFirst({
where: and(eq(projects.id, id), eq(projects.tenantId, tenantId)),
})
if (!project) throw createError({ statusCode: 404, statusMessage: 'Nincs ilyen projekt' })
return project // a szerializálás automatikus
})
Amit itt nyersz: a válasz típusa végigmegy a useFetch-ig, tehát a komponensben tudja a TypeScript, mi van a project-ben. Nincs kétszer leírt típus, nincs CORS, nincs külön deploy. A 8. modul a teljes Express→h3 megfeleltetést hozza.
Auto-import (komponensek, composable-ok, server/utils), kész felállás a TypeScripthez, és egy modul-ökoszisztéma, ahol egy sor a nuxt.config.ts-ben elintézi a képoptimalizálást, az i18n-t vagy a fontkezelést. Ez a rész az, ami a legjobban gyorsít — és amiért cserébe a legtöbbet fizetsz kontrollban. Lásd rögtön a következő szakaszt.
Ezt a részt komolyan vedd, mert a Nuxt-marketing nem fogja elmondani.
.nuxt/ könyvtárban generált kódban van. Egy ismeretlen useValami() hívásnál nem látod az importból, honnan jön.vite build helyett kliens- és szerverbuild, prerender-fázis, Nitro-csomagolás. A CI-d lassabb lesz, a hibaüzenetek távolabbiak.| Helyzet | Miért nem éri meg | Mit csinálj helyette |
|---|---|---|
| Az app 100%-ban auth-fal mögött van, nulla publikus tartalom | Az SSR két fő haszna (SEO, első festés idegennek) nem érvényesül. Marad a DX-nyereség és a teljes komplexitás. | Vite + Vue Router + külön API. Vagy Nuxt ssr: false-szal — de akkor mérlegeld, mit adott. |
| A csapat React-es | A keretrendszer-váltás mellé jön egy nyelvi/ökoszisztéma-váltás. Két kockázat egyszerre. | Next.js OpenNext adapterrel Workers-en, vagy React Router / TanStack Start. |
| Kicsi belső admin vagy dashboard | A Nuxt overhead-je nagyobb, mint a haszna, ha 8 oldalról és 3 felhasználóról van szó. | Vite + Vue, statikusan a Workers static assetsre. Ez is egy deploy, és sokkal egyszerűbb. |
| Nagy, nem-JS backend (Rails, Django, Go), ami marad | A Nitro fele kihasználatlan lesz; a Nuxt csak SSR/BFF réteg. | Ez még lehet jó választás — de a 9. modul BFF-ágát olvasd, ne a full-stack ágat. |
| Tartalom-központú oldal (blog, dokumentáció, marketing) | Nuxttal megoldható, de nehezebb, mint kell. | Astro — kevesebb JS megy ki, egyszerűbb modell. |
Röviden és elfogultság nélkül, kifejezetten a „Cloudflare-en fogok hostolni” szempontból:
| Keretrendszer | Erőssége | Workers-en |
|---|---|---|
| Nuxt | Vue, kiforrott konvenciók, a Nitro miatt kiemelkedő platform-hordozhatóság | Natív Nitro-preset — nincs köztes adapter-réteg |
| Next.js | Legnagyobb ökoszisztéma és munkaerőpiac, React | OpenNext adapterrel — plusz réteg, ami lemarad a Next fejlődése mögött |
| SvelteKit | Kisebb bundle, kevesebb mágia, tiszta modell | Van adapter, jól működik; kisebb ökoszisztéma és kevesebb kész modul |
| Astro | Tartalom-központú oldalakra a legjobb (island architecture) | Jól fut; alkalmazás-jellegű termékhez viszont kevesebbet ad |
Nálad a Vue-tudás adott, tehát a valódi kérdés nem a Nuxt vs. Next, hanem a Nuxt vs. a mostani Vue SPA + Express. Erre válaszol az 1.2 és az 1.3.
Ez az a rész, ami miatt ez a tananyag nem általános Nuxt-kurzus. Négy konkrét ok:
wrangler deploy lesz.server/api/-ban a D1, R2, KV, Queues nem SDK-n és API-kulcson keresztül érhető el, hanem közvetlen referenciaként (event.context.cloudflare.env). Nincs hálózati ugrás, nincs kulcskezelés.isr / swr route rule nem úgy működik, mint Vercelen (7. modul). Egyik sem showstopper, de mindegyik meglepetés, ha deploy után derül ki.Multitenant SaaS-nál három dolog számít a fentiekből igazán:
nuxt build-kor, mi kerül a .output/-ba, és — a legfontosabb — melyik kódod hol fut. Ez a modell az, ami nélkül a későbbi SSR-hibák érthetetlenek maradnak, úgyhogy azt a modult ne ugord át.Nyered: a felhasználó azonnal tartalmat lát (nem spinnert), a keresőrobot és a link-előnézet kész HTML-t kap, és a <title>/OG-tagek szerveroldalon, valós adatból generálódnak. Nem nyered: a JS ugyanúgy letöltődik és lefut, a HTML nagyobb lesz, és a szerveredre több munka kerül. Az SSR észlelt sebességet és indexelhetőséget ad, nem kevesebb kódot a kliensen.
Mert a kényelemért keretrendszer-kötöttséggel fizetsz: kb. 2-3 évente egy major migráció (a Nuxt 3 támogatása épp most, 2026. július 31-én járt le), nehezebb hibakeresés a szerver+kliens kettősség miatt (hydration mismatch), függés a modul-ökoszisztémától, lassabb build. Ha a hasznot nem kapod meg — például mert az app teljesen auth mögött van —, akkor csak a költséget vetted meg.
① Hydration mismatch: a szerveren és a kliensen renderelt kimenet eltér (például Date.now(), véletlenszám vagy window-függő logika miatt). ② Cross-request state pollution: modulszintű változóban tárolt állapot a szerveren megosztott a kérések között, így az egyik felhasználó adata átszivároghat a másikhoz. Mindkettőre visszatérünk (3. és 6. modul).
Mert a Nitro preset-rendszere miatt a Workers első osztályú build-célpont, míg a Next.js-hez egy külön adapter-projekt (OpenNext) kell, ami szükségszerűen követő pozícióban van. Nem számít ez viszont akkor, ha a csapat React-es: a keretrendszer-előny nem éri meg egy teljes ökoszisztéma-váltás kockázatát.
Bármelyik három: ① nem minden npm-csomag működik, mert a runtime nem Node (sharp, bcrypt, fájlrendszer-igényes libek); ② van bundle-méret limit; ③ a CPU-idő korlátos és mérve van, tehát a nehéz SSR-nek ára van; ④ az isr/swr route rule nem platform-natívan működik, mint Vercelen/Netlifyon; ⑤ nincs fájlrendszer, helyette useStorage() KV/R2 driverrel.