Ez az a modul, ami nélkül a későbbi hibák érthetetlenek maradnak. Három kérdésre válaszol: mi történik pontosan nuxt build-kor, mi kerül a .output/-ba (és mit tölt fel a wrangler), és — a legfontosabb — melyik kódod hol fut le. Ha csak egy táblázatot jegyzel meg a tananyagból, az a 2.4-es legyen.
Vue SPA-ban egyetlen futásidő van: a böngésző. Nuxtban három, és ezek különböző időpontokban, különböző környezetben futnak — más globális objektumokkal, más API-kkal, más korlátokkal.
A középső sáv a kritikus. Egy komponens <script setup> blokkja kétszer fut le: egyszer a szerveren az SSR alatt, egyszer a böngészőben a hydration alatt. Ha a kettő nem ugyanazt az eredményt adja, hydration mismatch lesz belőle. Ha valami olyat használsz, ami csak az egyik helyen létezik (window, vagy fordítva: egy adatbázis-kliens), akkor hiba.
nuxt build-korA build nem egy lépés, hanem négy, és a hibaüzenetek attól függően néznek ki, hogy melyikben akadtál el:
Két dolgot érdemes ebből megjegyezni. Egyrészt a .nuxt/ nem szemét, hanem a fordítás félkész terméke: itt találod a generált route-táblát, az auto-import térképet és a típusokat. Amikor nem érted, honnan jön egy useValami(), ide nézel. Másrészt a kliens- és a szerverkód külön bundle — ezért fordulhat elő, hogy valami a böngészőben megy, a szerveren nem, vagy fordítva.
.output/-baEz a deploy-artefakt. Semmi mást nem kell feltölteni, és semmit nem kell benne kézzel módosítani (a következő build felülírja):
.output/
├─ public/ ← statikus fájlok — ez lesz a Workers static assets
│ ├─ _nuxt/ a buildelt JS/CSS chunkok, hash-elt névvel
│ │ ├─ entry.C3f1a9.js
│ │ └─ default.a71b2c.css
│ ├─ favicon.ico a public/ mappád tartalma változatlanul
│ └─ ... plusz a prerenderelt oldalak, ha vannak
├─ server/
│ ├─ index.mjs ← A BELÉPÉSI PONT. Workers-en ez a Worker modul.
│ └─ chunks/ a szerverkód darabjai (SSR + server/api)
└─ nitro.json metaadat: melyik presettel készült, verziók
Node-szerveren ezt így indítanád: node .output/server/index.mjs. Workers-en viszont ugyanez az index.mjs nem egy szerver, amit elindítasz, hanem egy Worker-modul, amit a platform hív meg — a wrangler.jsonc-ben ez lesz a main, a .output/public pedig az assets.directory. A 17. modulban ezt kirakjuk teljes konfigurációval.
Ezt tedd a könyvjelzők közé. A tananyag hátralévő részében erre fogok visszautalni:
| Hely | Build | Szerver | Kliens | Megjegyzés |
|---|---|---|---|---|
nuxt.config.ts | ✓ | – | – | Csak build-time fut. Az értékei beépülnek — futásidőben nem olvasod. |
modules/ | ✓ | ✗ | ✗ | Saját Nuxt-modulok: a buildet módosítják, nem a futást. |
app/components/*.vue | fordítás | ✓ | ✓ | Kétszer fut. Itt születik a hydration mismatch. |
*.client.vue | fordítás | ✗ | ✓ | Csak böngészőben renderel (térkép, chart, szerkesztő). |
*.server.vue | fordítás | ✓ | ✗ | Nem hidratálódik — statikus marad. |
app/composables/ | – | ✓ | ✓ | Auto-import. Ide ne tegyél titkot vagy DB-hívást. |
app/plugins/x.ts | – | ✓ | ✓ | Mindkét oldalon lefut, app-indításkor. |
app/plugins/x.client.ts | – | ✗ | ✓ | Analitika, böngésző-API-k ide. |
app/middleware/ | – | ✓ | ✓ | Első kérésnél szerveren, utána navigációnál kliensen. Nem biztonsági határ. |
server/api/, server/routes/ | – | ✓ | ✗ | Soha nem kerül a kliens-bundle-be. Itt vannak a titkok. |
server/middleware/ | – | ✓ | ✗ | Minden szerverre érkező kérésnél fut. Tenant-feloldás helye. |
server/utils/ | – | ✓ | ✗ | Auto-import, de csak szerveroldalon. DB-wrapper, session ide. |
shared/ | – | ✓ | ✓ | Nuxt 4 újdonság: közös típusok és tiszta segédfüggvények. |
public/ | másolás | – | letöltés | Változatlanul kikerül a gyökérre. Nem megy át a buildon. |
server/ alatti kód soha nem kerül a böngészőbe — ide, és csak ide való minden titok, adatbázis-hozzáférés és jogosultság-ellenőrzés. ② Az app/middleware/ route middleware nem biztonsági határ: kliensoldali navigációnál a böngészőben fut, tehát megkerülhető. Ha egy adatot védeni akarsz, azt a server/-ben kell ellenőrizni. Erre a 10. modulban visszatérünk, de a hibát most is el lehet követni.import.metaAmikor kódban kell eldöntened, hol vagy:
const width = ref(0)
// ✗ így szokták — működik, de nem tree-shake-elhető, és a szerverbundle-ben is ott marad
if (typeof window !== 'undefined') width.value = window.innerWidth
// ✓ így kell — a build ki tudja dobni a nem oda való ágat
if (import.meta.client) width.value = window.innerWidth
// a teljes készlet
import.meta.client // böngészőben fut
import.meta.server // szerveren fut (SSR vagy server route)
import.meta.dev // nuxt dev alatt
import.meta.prerender // build-time prerender közben
A régi process.server / process.client párost felejtsd el — nem véletlenül tűnt el: Workers-en nincs értelmes process globális, és a Nuxt 5 felé az import.meta.* a járható út.
A Nuxt 4 legfeltűnőbb változása, hogy az alkalmazás-kód bekerült egy app/ mappába. Nem esztétikai döntés: így a fájlfigyelő nem a node_modules/ és a .git/ mellett kutat, és a szerver-, kliens- és megosztott kód szétválasztása a TypeScript-projektek szintjén is megtörténik.
a-projekted/
├─ app/ ← ALKALMAZÁS (kliens + SSR)
│ ├─ app.vue a gyökérkomponens
│ ├─ pages/ route-ok (4. modul)
│ ├─ layouts/
│ ├─ components/ auto-import
│ ├─ composables/ auto-import
│ ├─ middleware/ route middleware
│ ├─ plugins/
│ ├─ utils/ auto-import
│ └─ assets/ amin átmegy a build (SCSS, kép)
├─ server/ ← SZERVER — a gyökérben marad, NEM az app/ alatt
│ ├─ api/
│ ├─ routes/
│ ├─ middleware/
│ ├─ utils/ auto-import, csak szerveroldalon
│ └─ plugins/ Nitro-pluginek (nem app-pluginek!)
├─ shared/ ← KÖZÖS: típusok, tiszta függvények (Nuxt 4)
├─ public/ változatlanul kikerül
├─ modules/ saját build-idejű Nuxt-modulok
├─ nuxt.config.ts
├─ wrangler.jsonc a Cloudflare-oldal (17. modul)
└─ tsconfig.json egyetlen fájl a gyökérben, a többit a Nuxt generálja
server/plugins/ nem ugyanaz, mint az app/plugins/: az előbbi Nitro-szintű (kérés-hookok, hibakezelés), az utóbbi Vue-app-szintű. ② A shared/ csak olyasmit bír el, ami mindkét környezetben értelmes — típusok, validációs sémák, formázó függvények. Ha oda importálsz egy adatbázis-klienst, az bekerül a kliens-bundle-be is, és vagy elszáll, vagy kiszivárogtat.A Nitro a Nuxt szerver-rétege — de önálló projekt, saját életet él. Ő felel a server/api útvonalakért, a fájl-alapú szerver-routingért, a storage-rétegért (useStorage()), a cache-elésért, és a platformra szabott csomagolásért.
Ez utóbbi a preset-rendszer. Ugyanaz a forráskód különböző kimenetté fordul aszerint, hova megy:
// nuxt.config.ts
export default defineNuxtConfig({
nitro: {
preset: 'cloudflare_module', // Workers + static assets — ez kell nekünk
},
})
// vagy környezeti változóból, CI-ben:
// NITRO_PRESET=cloudflare_module nuxt build
A cloudflare_module preset Workersre épít (ez az ajánlott út); a régebbi cloudflare_pages a Pages-hez tartozik, ami a Cloudflare-anyag szerint kifutó irány. A preset dönti el, hogy milyen belépési pont készül, milyen polyfillek kellenek, hogyan viselkedik a useStorage(), és mit tud a beépített cache-réteg.
nuxt dev — és miért „ment lokálisan”A fejlesztői szerver nem ugyanaz, ami élesben fut, és a különbség pont Workers-en a legnagyobb:
nuxt dev | éles Workers | |
|---|---|---|
| Runtime | Node.js a gépeden | workerd az edge-en |
| npm-csomagok | gyakorlatilag mind működik | csak ami Web-API-kkal beéri (19. modul) |
| Fájlrendszer | van | nincs |
| Szerverkód | igény szerint, Vite-tal transzformálva | egyetlen bundle-be fordítva |
| Bindings | nincs — hacsak nem állítod be (18. modul) | van |
| Méret- és CPU-limit | nincs | van |
Innen ered a leggyakoribb frusztráció: „lokálisan tökéletesen ment, deploy után 500-as”. Nem véletlen, hanem szerkezeti: dev alatt a szerverkódod Node-ban fut, élesben a workerd-ben. Két dolog véd ellene, és mindkettőt korán érdemes bevezetni:
nitro-cloudflare-dev vagy a Cloudflare Vite plugin. Ettől lesznek bindingjaid lokálisan is, és sok Node-only feltételezés már itt kibukik. A 18. modul összeveti a kettőt.workerd-ben fut. Ez a Cloudflare-anyag 8. moduljának preview-mechanikája.A Nuxt 5 a Nitro v3-mal együtt érkezik, és még nincs kint; a 4.5 már tartalmaz hozzá előkészítést. Nem érdemes rá várni — viszont van négy szokás, amivel most, ingyen csökkented a jövőbeli migráció költségét:
import.meta.client / import.meta.server párost, ne a process.*-ot..nuxt/ belsőségeihez. Olvasni jó, támaszkodni rá rossz — ezek a generált fájlok verzióról verzióra változnak.<script setup> blokkjában meghívod a localStorage-ot. Mi történik, és hogyan javítod?
SSR alatt a szerveren nincs localStorage, tehát a renderelés hibára fut. Javítás: tedd if (import.meta.client) ágba, vagy onMounted()-be (az csak kliensen fut), vagy ha az egész komponens böngésző-függő, nevezd át *.client.vue-ra. A typeof window !== 'undefined' is működik, de az import.meta.client jobb, mert a build ki tudja dobni a nem oda való ágat.
app/composables/ alatti fájlba?
Mert a composable-ok a kliens-bundle-be is bekerülnek — a felhasználó megnyitja a forrást, és ott a kulcs. Titok csak a server/ alá vagy a runtimeConfig szerveroldali (nem public) részébe kerülhet, és csak szerveroldali kód olvashatja. Erre a 12. modulban külön kitérünk.
server/plugins/ és az app/plugins/ között?
A server/plugins/ Nitro-szintű: a szerver életciklusához és a kérés-hookokhoz kapcsolódik (például globális hibakezelés, minden válaszra fejléc). Az app/plugins/ a Vue-alkalmazás indulásakor fut le, és a Vue-példányhoz ad hozzá dolgokat (direktívák, providek). Nevük hasonló, közük nincs egymáshoz.
Jellemzően a szerver-buildben (③) vagy a Nitro-csomagolásnál (④) — például egy Node-only csomag került a server/ kódba, vagy átlépted a bundle-limitet. Azért csak most derül ki, mert a nuxt dev Node.js-ben futtatja a szerverkódot, élesben viszont workerd-ben fut. Megelőzés: platform-emuláció dev alatt (18. modul) és korai preview-deploy.
wrangler deploy egy Nuxt-projektnél?
Két dolgot, egy műveletben: a .output/server/index.mjs-t Worker-kódként (ez a main), és a .output/public/ tartalmát static assetsként. A forráskódod, a node_modules és a .nuxt/ nem megy sehova — a .output/ a teljes deploy-artefakt.
app/middleware/-be tett jogosultság-ellenőrzés?
Mert a route middleware kliensoldali navigációnál a böngészőben fut, tehát a felhasználó megkerülheti. Jó arra, hogy a UI ne villantson fel olyat, amihez nincs joga — de az adatot a server/ oldalon kell védeni, minden egyes végponton. A kettő nem helyettesíti egymást.