16. modulTesztelés és hibakeresés
Nuxt for Devs · 16. modul

Tesztelés és hibakeresés

A Nuxt tesztelő-eszközkészlete jó, és négy rétegben fedi le az alkalmazást. A gond az, hogy mind a négy réteg Node-ban fut — vagyis pontosan azokat a hibákat nem látja, amiktől egy Nuxt app Workers-en elhasal. Ez a modul először felépíti a négy réteget, aztán megmutatja, hogyan hidald át az ötödiket: a valódi workerd-t.

16.1Négy réteg — és ahol a tesztkészlet véget ér

Egy Nuxt-alkalmazás annyiféle helyen tud eltörni, hogy egyetlen teszttípus reménytelen. A @nuxt/test-utils ezért négy, egyre drágább és egyre valósághűbb réteget kínál. Érdemes mind a négyet használni, de tudni kell, melyik mit fog meg — és mit nem.

① UNIT — environment: 'node' tiszta függvények, validáció, shared/ · ezredmásodperc ② NUXT-KÖRNYEZET — environment: 'nuxt' komponensek, composable-ök, auto-import · másodperc ③ NITRO — setup() + $fetch('/api/…') server routes, middleware, teljes build · tíz másodperc ④ E2E — Playwright, valódi böngésző hydration, navigáció, auth-folyamat · perc ▲ eddig minden Node-ban fut ▲ ⑤ VALÓDI workerd — amit a fenti négy nem lát · hiányzó Node API · bundle-limit · 1 s indulási keret · isolate-újrahasználat · CPU-limit · subrequest-limit · valódi D1/KV viselkedés · éles bindingok
A zöld tesztpiramis teljes és hasznos — de a piros szaggatott vonal alatt semmit sem bizonyít. A 16.6 arról szól, hogyan lépsz át rajta.
RétegMit fog megMit nem fog meg
① unitlogikai hibák, rossz élesetek, elrontott validációbármit, ami Nuxt-kontextust igényel
② Nuxt-envkomponens-render, composable-viselkedés, props/emit-szerződésvalódi hálózat, valódi SSR-lánc, hydration
③ Nitroroute-ok, státuszkódok, middleware-sorrend, szerver-oldali logikaböngésző-oldali működés, workerd-viselkedés
④ E2Ehydration-hiba, kliensnavigáció, teljes user flowmindent, ami csak Cloudflare-en romlik el
⑤ workerda fenti táblázat harmadik oszlopának a maradékát
A gyakorlati arány. Sok Nuxt-projektben a ② réteg a legkevésbé kifizetődő: komponens-teszteket írni drága, és a legtöbbjük valójában azt teszteli, hogy a Vue működik. A pénzért kapott érték rangsora tapasztalatból: ① unit > ③ Nitro > ⑤ workerd-smoke > ④ E2E > ② komponens. A ② akkor éri meg, ha a komponensnek tényleg van saját logikája — form-állapotgép, komplex számított érték, feltételes renderelés sok ággal.

16.2A felállás: egy Vitest, több projekt

A négy réteg négyféle környezetet igényel, és ha egy konfigba zsúfolod őket, minden teszt a leglassabb réteg árát fizeti. A megoldás a Vitest projects mechanizmusa: külön futtatókörnyezet, közös parancs.

# telepítés — a peer dependencyk nem opcionálisak
npm i -D @nuxt/test-utils vitest @vue/test-utils happy-dom playwright-core
// vitest.config.ts
import { defineConfig } from 'vitest/config'
import { defineVitestProject } from '@nuxt/test-utils/config'

export default defineConfig({
  test: {
    projects: [
      {
        test: {
          name: 'unit',
          include: ['test/unit/*.{test,spec}.ts'],
          environment: 'node',        // gyors: nincs Nuxt-bootstrap
        },
      },
      await defineVitestProject({
        test: {
          name: 'nuxt',
          include: ['test/nuxt/*.{test,spec}.ts'],
          environment: 'nuxt',        // lassabb: felhúzza a Nuxt-kontextust
        },
      }),
    ],
  },
})

A könyvtárszerkezet ezt tükrözze, mert a Nuxt TypeScript-kontextusa automatikusan beleveszi a test/nuxt/ és tests/nuxt/ könyvtárakat — ott működnek az auto-importok a tesztfájlban is:

test/
├── unit/     # environment: 'node' — tiszta függvények
├── nuxt/     # environment: 'nuxt' — komponensek, composable-ök
└── e2e/      # setup() — épít és futtat egy szervert

Opcionálisan bekapcsolhatod a test-utils Nuxt-modulját is; ez az IDE- és típusintegrációt javítja:

// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@nuxt/test-utils/module'],
})
Mi kerüljön az ① rétegbe? Minden, ami nem igényel se böngészőt, se Nuxt-kontextust: a shared/utils/ tartalma, a Zod/Valibot sémák, a server/utils/ tiszta függvényei, ár- és dátumszámítás, tenant-azonosító feloldás, jogosultság-döntés. Ha a 11. modul tanácsát követted és a tenant-szűrés egy külön függvényben van, az itt egy tízsoros teszttel bizonyítható — és ez a projekt legfontosabb tesztje lesz.

16.3Komponensek és composable-ök Nuxt-környezetben

A environment: 'nuxt' projekt egy majdnem teljes Nuxt-futásidőt ad: működnek az auto-importok, a #components alias, a composable-ök. A belépőpont két függvény, aszerint, hogy Vue Test Utils vagy Testing Library stílusban dolgozol.

// test/nuxt/price-badge.spec.ts
import { mountSuspended } from '@nuxt/test-utils/runtime'
import { PriceBadge } from '#components'

it('a kedvezményt százalékban mutatja', async () => {
  const c = await mountSuspended(PriceBadge, {
    props: { original: 10000, current: 7500 },
  })
  expect(c.text()).toContain('-25%')
})

A mountSuspended lényege a névben van: megvárja az aszinkron setup-ot. Ha a komponensed <script setup>-ban await-el (mert például useAsyncData-t hív), a sima mount egy félig kész komponenst adna vissza. Van egy route opciója is — alapból /, de megadhatsz útvonalat, vagy false-szal kihagyhatod a router inicializálását.

Testing Library-vel ugyanez renderSuspended:

import { renderSuspended } from '@nuxt/test-utils/runtime'
import { screen } from '@testing-library/vue'

await renderSuspended(PriceBadge, { props: { original: 10000, current: 7500 } })
expect(screen.getByText('-25%')).toBeDefined()

A három mockoló eszköz

import { mockNuxtImport, mockComponent, registerEndpoint } from '@nuxt/test-utils/runtime'

// ① auto-importált composable kicserélése
mockNuxtImport('useUserSession', () => {
  return () => ({ loggedIn: ref(true), user: ref({ id: 'u1', role: 'admin' }) })
})

// típusbiztosan, az eredetire építve
mockNuxtImport<typeof useState>('useState', (original) => {
  return (...args) => ({ ...original('key'), value: 'mocked' })
})

// ② komponens kicserélése — név vagy útvonal alapján
mockComponent('MapWidget', {
  setup() { return () => h('div', '[térkép]') },
})
mockComponent('~/components/heavy-chart.vue', () => import('./ChartStub.vue'))

// ③ Nitro-végpont kigúnyolása — a komponens $fetch-e ezt kapja
registerEndpoint('/api/products', () => [{ id: 'p1', name: 'Bögre' }])
registerEndpoint('/api/orders', {
  method: 'POST',
  handler: () => ({ id: 'o1', status: 'created' }),
})
A mockNuxtImport hoistolódik. A háttérben vi.mock-ot használ, amit a Vitest a fájl tetejére emel. Ebből két szabály következik: (1) egy importra csak egyszer hívhatod egy fájlban — ha több teszt más-más viselkedést akar, a mockon belül kell változót olvasnod (és a változót vi.hoisted-del létrehoznod); (2) a mock-gyáron kívüli fájlszintű változókra nem hivatkozhatsz, mert azok még nem léteznek, amikor a mock lefut. Ez a leggyakoribb „miért undefined?” a Nuxt-tesztekben.

Beépített böngésző-mockok

Két olyan böngésző-API van, amit a happy-dom nem ad meg, viszont a valós komponensek gyakran használják. Ezeket a Nuxt tesztkörnyezete pótolja, konfigurálhatóan:

// vitest.config.ts — a nuxt projekten belül
environmentOptions: {
  nuxt: {
    mock: {
      intersectionObserver: true,   // alap: true — üres osztály
      indexedDb: true,             // alap: false — fake-indexeddb, működő
    },
  },
}

Az intersectionObserver-mock azért van bekapcsolva alapból, mert enélkül minden hydrate-on-visible komponens és minden lazy-load kép elhasalna a tesztben. Ha viszont a 15. modul stratégiáit használod, jegyezd meg: a mock nem figyel semmit, tehát a láthatóságra épülő viselkedést nem tudod ezen a rétegen tesztelni — az a ④ réteg dolga.

16.4Server routes tesztelése

Ez a réteg lép ki először a szimulációból: a setup() lefordítja a teljes alkalmazást és elindít egy valódi szervert, aztán HTTP-n keresztül beszélsz vele. Cserébe lassú — külön projektbe való, és nem érdemes minden mentésre futtatni.

// test/e2e/api.spec.ts
import { describe, test, expect } from 'vitest'
import { setup, $fetch, fetch, url } from '@nuxt/test-utils/e2e'

describe('termék-API', async () => {
  await setup({ setupTimeout: 120_000 })

  test('listáz', async () => {
    const data = await $fetch('/api/products')
    expect(data).toHaveLength(3)
  })

  test('auth nélkül 401', async () => {
    // fetch() — teljes válaszobjektum, fejlécekkel és státusszal
    const res = await fetch('/api/admin/users')
    expect(res.status).toBe(401)
  })

  test('a SSR HTML tartalmazza a címet', async () => {
    const html = await $fetch('/')   // oldalon: nyers HTML jön vissza
    expect(html).toContain('<h1>Bolt</h1>')
  })
})
setup() opcióAlapMire jó
rootDir'.'monorepóban a Nuxt-app útvonala
configFile'nuxt.config'külön teszt-konfig használata
buildtruefalse: ne fordítson újra (előre buildelt kimenet)
servertruefalse: ne indítson szervert
hostmeglévő URL-t céloz build helyett — lásd 16.6
portvéletlenfix port, ha külső eszköz is csatlakozik
browserfalsePlaywright-példány indítása (createPage-hez)
setupTimeout120000lassú CI-n emeld; a build ideje ebbe számít
teardownTimeout30000leállítási türelem

A szerver logjainak elkapása

Ha azt akarod bizonyítani, hogy egy handler naplózott valamit (audit-log, hibaág), nem elég a válaszkódot nézni:

import { getServerLogs, clearServerLogs } from '@nuxt/test-utils/e2e'

await setup({ captureServerLogs: true })

test('sikertelen belépést naplóz', async () => {
  clearServerLogs()
  await fetch('/api/login', { method: 'POST', body: JSON.stringify({ email: 'x@y.z', password: 'rossz' }) })
  expect(getServerLogs().join('\n')).toContain('auth.failed')
})
Ez a réteg a legjobb ár-érték arányú a négy közül egy Cloudflare-re szánt Nuxt-appban. A server route-ok azok, ahol az üzleti logika, az auth és az adatbázis-hozzáférés lakik — vagyis ahol a valódi kár keletkezik. Egy komponens elrontott margója bosszantó; egy hiányzó tenant-szűrő a /api/orders-ben adatszivárgás.

16.5E2E Playwright-tal

Két út van. Vitest-runnerrel a createPage()-et hívod a setup({ browser: true }) után; a másik — és tisztább — út a Playwright saját runnerje, amihez a test-utils külön belépőpontot ad.

npm i -D @playwright/test @nuxt/test-utils
// playwright.config.ts
import { defineConfig } from '@playwright/test'
import type { ConfigOptions } from '@nuxt/test-utils/playwright'

export default defineConfig<ConfigOptions>({
  use: {
    nuxt: { rootDir: '.' },
  },
})
// test/e2e/checkout.spec.ts
import { expect, test } from '@nuxt/test-utils/playwright'

test('a kosár túléli az oldalfrissítést', async ({ page, goto }) => {
  await goto('/termek/bogre', { waitUntil: 'hydration' })
  await page.getByRole('button', { name: 'Kosárba' }).click()
  await page.reload()
  await expect(page.getByTestId('cart-count')).toHaveText('1')
})

A waitUntil: 'hydration' az egyetlen ok, amiért érdemes a test-utils goto-ját használni a natív page.goto helyett. A Playwright alap várakozásai (load, networkidle) nem tudják, mikor fejeződött be a Vue hydratálása — ezért ír az ember flaky teszteket tele waitForTimeout-tal. Ez a beállítás pontosan arra a pillanatra vár.

Mit érdemes E2E-vel tesztelni?

Az E2E drága és törékeny, ezért ne az üzleti szabályokat teszteld vele — azokat a ① és ③ réteg olcsóbban lefedi. Az E2E arra való, ami csak a teljes láncban derül ki:

16.6A workerd-rés — és a két áthidalás

Most jön a modul lényege. Fussuk át még egyszer, mit csinál a setup(): lefordítja az appot az alapértelmezett presettel, és elindítja Node-ban. Nem workerd-ben. Nem a te wrangler.jsonc-d szerint. Nem a te bindingjaiddal.

Ebből az következik, hogy egy tökéletesen zöld tesztkészlet mellett is elhasalhat az éles deploy, mégpedig pontosan azokon, amikről az előző modulok szóltak:

HibaosztályNode-tesztbenWorkers-en
Node-only API (fs, natív addon)működikfutásidejű hiba vagy build-hiba
Bundle túllépi a 3/10 MB gzip limitetnem érdeklia deploy elutasítva
Modul-scope inicializálás > 1 snem érdekliindulási hiba
Modul-szintű mutable állapottöbbnyire nem látszikkereszt-kérés szivárgás
Kérésenként > 50 (ill. 1000) subrequestműködika további hívások elhasalnak
Hosszú szinkron CPU-blokklassú, de lefutCPU-limit túllépés
D1/KV konzisztencia-feltételezésa mock mindig konzisztensKV: eventual consistency

Áthidalás ①: valódi workerd lokálisan

A wrangler dev nem szimulátor: ugyanazt a workerd bináris futtatókörnyezetet indítja el, ami élesben is fut, csak a bindingokat emulálja Miniflare-rel. Ez elkap mindent, ami a fenti tábla első hat sora.

# build a Cloudflare-presettel, majd futtatás valódi workerd-ben
npx nuxt build
npx wrangler dev

# hasznos kapcsolók
npx wrangler dev --remote            # valódi, éles bindingok (nem emulált)
npx wrangler dev --test-scheduled    # /__scheduled útvonal a cron-handlerhez
npx wrangler dev --log-level debug
npx wrangler dev --inspector-port 9229

Ez a lépés a CI-ben is elfér, mint füstteszt: build → wrangler dev háttérben → néhány curl a kritikus útvonalakra → leállítás. Ha az app el sem indul workerd alatt, ezt a merge előtt akarod megtudni.

Áthidalás ②: a saját E2E-készleted egy valódi deploy ellen

Ez a legjobb trükk a modulban, és alig ismert. A setup() host opciója azt mondja: ne buildelj, ne indíts szervert — ezt az URL-t célozd. Ami azt jelenti, hogy ugyanaz a tesztfájl, amit lokálisan Node ellen futtatsz, változtatás nélkül futtatható egy valódi Workers-deploy ellen.

// test/e2e/smoke.spec.ts — kettős életű teszt
import { setup, $fetch } from '@nuxt/test-utils/e2e'

describe('füstteszt', async () => {
  await setup(
    process.env.PREVIEW_URL
      ? { host: process.env.PREVIEW_URL }   // CI: valódi workerd, valódi bindingok
      : { setupTimeout: 120_000 },        // lokál: Node-build
  )

  test('a főoldal SSR-el', async () => {
    expect(await $fetch('/')).toContain('<h1')
  })
  test('az API él és az adatbázist látja', async () => {
    expect(await $fetch('/api/health/db')).toMatchObject({ ok: true })
  })
})

A CI-lánc ehhez a 8. modul verzió-alapú deployára épül: feltöltesz egy verziót anélkül, hogy forgalmat kapna, megkapod a preview URL-t, ráengeded a füsttesztet, és csak siker esetén léptetsz elő.

# .github/workflows/deploy.yml — a lényegi rész
- name: Verzió feltöltése (forgalom nélkül)
  id: upload
  run: |
    npx nuxt build
    npx wrangler versions upload --json > version.json
    echo "url=$(jq -r '.preview_url' version.json)" >> $GITHUB_OUTPUT

- name: Füstteszt a valódi Workers-deploy ellen
  env:
    PREVIEW_URL: ${{ steps.upload.outputs.url }}
  run: npx vitest run test/e2e/smoke.spec.ts

- name: Előléptetés 100%-ra
  run: npx wrangler versions deploy --yes
Miért ez a helyes sorrend Cloudflare-en? Mert a Workers deploy globális és azonnali — nincs „egy régióban élesítjük, aztán nézzük”. A verzió-feltöltés az egyetlen pont, ahol a valódi futásidőben, valódi bindingokkal, de forgalom nélkül tudsz mérni. Ha kihagyod, a füstteszted helyét a felhasználóid veszik át.

És a @cloudflare/vitest-pool-workers?

A Cloudflare saját Vitest-integrációja workerd-ben futtatja a teszteket, teljes binding-hozzáféréssel — papíron pont az, amit keresünk:

// vitest.config.ts — Cloudflare-integráció (Vitest 4.1+ szükséges)
import { cloudflareTest } from '@cloudflare/vitest-pool-workers'
import { defineConfig } from 'vitest/config'

export default defineConfig({
  plugins: [cloudflareTest({ wrangler: { configPath: './wrangler.jsonc' } })],
})
import { env, exports } from 'cloudflare:workers'
import { createExecutionContext, waitOnExecutionContext } from 'cloudflare:test'

it('a KV-binding valódi', async () => {
  await env.CACHE.put('k', 'v')
  expect(await env.CACHE.get('k')).toBe('v')
})
Nuxt-appra viszont kényelmetlen. Ez az eszköz kézzel írt Workerre van tervezve, ahol a forrásfájl a belépőpont. Egy Nuxt-app belépőpontja a .output/server/index.mjs — egy generált, több ezer soros bundle, amit előbb le kell fordítani, és amiben egy-egy handler nem címezhető külön. Használható a Nuxt-tól független Worker-kódra (külön Durable Object, külön queue-consumer, cron-worker), és arra érdemes is; a Nuxt-alkalmazás maga viszont a 16.6-os két áthidaláson keresztül tesztelhető józanul.

16.7Lokális fejlesztés bindingokkal — és amiben mégis hazudik

A nuxt dev régen nem látta a Cloudflare-bindingokat, ezért terjedt el a nitro-cloudflare-dev modul, ami a wrangler getPlatformProxy API-jával pótolta őket. Erre már nincs szükség: a Nitro újabb verziói beépítve emulálják a Cloudflare-környezetet Miniflare-rel — ugyanazzal a workerd-vel, amit a wrangler is használ.

Elég, ha a bindingok szerepelnek a wrangler.jsonc-ben; ha nem akarsz külön fájlt, inline is megadhatod:

// nuxt.config.ts
export default defineNuxtConfig({
  nitro: {
    preset: 'cloudflare_module',
    cloudflare: {
      wrangler: {
        vars: { APP_MODE: 'dev' },
        kv_namespaces: [{ binding: 'CACHE', id: 'xxx' }],
      },
      wranglerEnv: 'preview',   // melyik wrangler-környezetet emulálja
    },
  },
})

A lokális állapot a .wrangler/state/v3 alá kerül — ez a te lokális D1-ed, KV-d, R2-d. Tedd .gitignore-ba, és tudd, hogy törölhető: ha összekuszálódott a lokális adatbázisod, a könyvtár törlése tiszta lapot ad.

BindingLokálisanAmiben eltér az élestől
D1helyi SQLitenincs hálózati késleltetés, nincs sorlimit, nincs régió-kérdés
KVSQLite-alapú emulációazonnal konzisztens — élesben eventual consistency
R2helyi fájlrendszernincs valódi latencia, nincs multipart-korlát
Queueshelyi emulációa batch-viselkedés és az újrapróbálkozás időzítése más
Hyperdriveközvetlen kapcsolatnincs pooling és nincs cache — a legnagyobb eltérés
Secrets.dev.varsélesben wrangler secret; könnyű elfelejteni feltölteni
A KV-sor a legveszélyesebb. Lokálisan írsz egy kulcsot, azonnal olvasod, megvan — a teszt zöld. Élesben a KV globálisan eventually consistent: az írás után közvetlenül olvasva még a régi értéket kaphatod. Ha a logikád „írok, majd olvasom vissza” mintára épül, az lokálisan sosem fog elbukni, élesben viszont véletlenszerűen igen. Ez a fajta hiba az, amiért a 16.6-os füstteszt valódi deploy ellen fut.

16.8Hibakeresés: négy eszköz, négy helyzet

① Chrome DevTools — lokális töréspontok és profilozás

A wrangler dev futása közben nyomj D-t: megnyílik a Chrome DevTools a Workeredre kötve. Ez nem konzol-tükrözés, hanem teljes értékű debugger:

Ha Vite-tal futtatod a Cloudflare-pluginnal, ugyanez a http://localhost:5173/__debug címen érhető el.

wrangler tail — élő logfolyam éles forgalomból

# minden, ami hibával végződik
npx wrangler tail --status error

# egy konkrét szövegre szűrve, gépi feldolgozásra
npx wrangler tail --search "tenant:acme" --format json | jq '.logs[].message'

# csak a saját kéréseim (fejlesztés közben aranyat ér)
npx wrangler tail --ip self

# egy frissen feltöltött verzió viselkedése, forgalom előtt
npx wrangler tail --version-id <id>
KapcsolóÉrtékMikor
--formatjson | prettyjson + jq, ha keresel; pretty, ha olvasol
--statusok | error | canceledhibavadászat forgalmas Workeren
--searchszövega console.log üzenetekben keres
--method / --headerszövegegy konkrét endpoint izolálása
--ipIP vagy selfsaját kérés kiszűrése éles forgalomból
--version-idverzió-azonosítófokozatos bevezetés megfigyelése
--sampling-rate0–1ha a nagy forgalom elnyomja a logokat
Három korlát, amit tudni kell. (1) Egy Workert egyszerre legfeljebb 10 kliens figyelhet — a dashboard és a CLI együtt számít; csapatban ez elfogy. (2) Nagy forgalomnál a tail mintavételi módba vált, és üzeneteket dob el — a szűrés nem kényelmi funkció, hanem ez ellen véd. (3) A tail nem tárol semmit: amit nem néztél, az elveszett. WebSocket-handlerben ráadásul a logok a kapcsolat lezárásáig visszatartva gyűlnek, majd egyszerre ömlenek ki.

③ Workers Logs — a visszakereshető réteg

Amit a tail nem tud (tárolás, visszamenőleges keresés), azt a Workers Logs adja. Bekapcsolni egy sor:

// wrangler.jsonc
{
  "observability": {
    "enabled": true,
    "head_sampling_rate": 1     // 1 = minden kérés, 0.01 = 1%
  }
}
TulajdonságFreePaid
Napi keret200 000 loghavi 20 millió benne, utána 0,60 $ / millió
Megőrzés3 nap7 nap
Max. logméret256 KB / bejegyzés
Fiókszintű plafonnapi 5 milliárd log fölött 1%-os mintavétel
Minimum wrangler3.78.6
Strukturáltan naplózz, ne összefűzött szövegként. A console.log({ event: 'order.created', orderId, tenantId, ms }) mezőnként kereshető lesz; a console.log('Order ' + id + ' created in ' + ms + 'ms') viszont csak szövegkeresésre alkalmas. Ugyanannyi munka, és a különbség akkor derül ki, amikor éjjel fél háromkor keresel valamit.

④ Source map — hogy a stack trace ne bundle-sorszámokat mondjon

Minifikált bundle-ben egy stack trace körülbelül annyit ér, mint a semmi: index.mjs:1:284471. A Cloudflare fel tudja tölteni és használni a source mapet, de két helyen kell bekapcsolni, mert alapból egyik sincs bekapcsolva:

// nuxt.config.ts — a Nitro alapból NEM generál source mapet (alap: false)
export default defineNuxtConfig({
  nitro: { sourceMap: true },
})
// wrangler.jsonc — a feltöltés külön kapcsoló (wrangler 3.46.0+)
{ "upload_source_maps": true }

Ezután a nem kezelt kivételek stack trace-e az eredeti forrásfájlokra és sorszámokra mutat, és ez látszik a valós idejű logokban és a Tail Workerekben is. A visszafejtés a kérés lefutása után, aszinkron történik, tehát nem terheli a Worker CPU-idejét — nincs teljesítménybeli ok kihagyni. A source map maximális mérete 15 MB gzip.

16.9Hibakezelés: mit lát a felhasználó

A hibakeresés másik fele az, hogy amikor mégis eltörik valami, az ne fehér képernyő legyen. A Nuxt eszközei:

// dobás bárhonnan — oldalról, komponensből, server route-ból
throw createError({
  status: 404,
  statusText: 'Ilyen termék nincs',
  data: { sku },        // ← ez ELJUT a klienshez
  // message: '…'      ← ez NEM jut el a klienshez API-route-ból
})
A message szándékosan nem propagál a kliensre szerveroldali hibánál — ez biztonsági döntés, nehogy belső részletek szivárogjanak ki. Ha a kliensnek is kell információ, tedd data-ba vagy statusText-be. Ez a leggyakoribb „miért látok üres hibaüzenetet?” a Nuxt-projektekben. (Megjegyzés: a Nuxt 4 dokumentáció a status / statusText neveket használja; a régebbi statusCode / statusMessage a h3 korábbi elnevezése.)
EszközMit csinál
~/error.vuea teljes képernyős hibaoldal; NuxtError objektumot kap propként
useError()az aktuálisan kezelt globális hiba
showError()hibaoldal kikényszerítése — de a throw createError() az ajánlott
clearError({ redirect })hiba törlése és opcionális átirányítás — a „Vissza a főoldalra” gomb
<NuxtErrorBoundary>lokális hibakezelés: csak egy komponensfa dől el, nem az oldal
fatal: truekliensoldalon is teljes képernyős hibát vált ki (alapból nem)
<!-- egy widget elhasalása ne vigye el az egész oldalt -->
<NuxtErrorBoundary>
  <RecommendationCarousel />
  <template #error="{ error, clearError }">
    <p>Az ajánlások most nem érhetők el.</p>
    <button @click="clearError">Újra</button>
  </template>
</NuxtErrorBoundary>

A chunk-hiba: ami minden deploynál fenyeget

Ez a Cloudflare-en különösen releváns. Amikor deployolsz, a JS-chunkok hash-e megváltozik, és a régi fájlok eltűnnek. Egy felhasználó, akinek nyitva volt az oldal a deploy előtt, kattint egy linkre → a Nuxt letölteni akarja a régi hash-ű chunkot → 404. Mivel a Workers-deploy globálisan azonnali, ez nem néhány szerverre igaz, hanem mindenkire, egyszerre.

A Nuxt alapból ezt kemény újratöltéssel kezeli, ami helyes viselkedés. Ha finomabban akarod (például mentetlen űrlapadat miatt), átveheted az irányítást:

// nuxt.config.ts
experimental: {
  emitRouteChunkError: 'manual',   // alap: automatikus újratöltés; false: kikapcsolva
}

Stabil hibakódok (Nuxt 4.5)

A 4.5 óta a Nuxt hibái és figyelmeztetései stabil azonosítót kapnak: NUXT_E1001, NUXT_B5001 és társaik. Fejlesztésben a kód mellé megkapod, hogy miért történt és hogyan javítható; a bonyolultabbakhoz dokumentációs oldal is tartozik. A klasszikus „a composable Nuxt-kontextuson kívül lett meghívva” például NUXT_E1001.

Ami Workers-en számít: a bőbeszédű magyarázatot a Nuxt kivágja a production buildből, hogy a bundle kisebb legyen — vagyis élesben a logban csak a kódot fogod látni. Ez a bundle-limit szempontjából jó hír, hibakeresés szempontjából viszont azt jelenti, hogy a kódot magát kell keresnie valakinek. Írd bele a hibakódot a monitorozás riasztásaiba, ne a szöveget: a szöveg verzióról verzióra változhat, a kód nem.

16.10Tracing channels — a saját observability alapja

A 4.5 bevezetett egy diszkrét, de fontos képességet: a Nuxt diagnostics-channel nyomokat publikál a szerveroldali műveleteiről. Ez az a horog, amire OpenTelemetry-t (vagy bármi mást) építhetsz — és a lényeg, hogy Cloudflare Workers-en is működik, nem csak Node-on.

// nuxt.config.ts
export default defineNuxtConfig({
  tracingChannel: true,
  // vagy szemcsésebben: tracingChannel: { nuxt: true }
})

Négy csatorna publikál: nuxt.render, nuxt.island, nuxt.data és nuxt.plugin. Ezekkel megválaszolhatóvá válik az a kérdés, amit a 15. modulban felvetettünk, de nem tudtunk megmérni: egy lassú kérésen belül mire ment el az idő — a renderelésre, egy adatlekérésre, vagy egy plugin inicializálására.

Fiatal API. A Nuxt saját dokumentációja jelzi, hogy a csatornanevek, a payload-alakok és az opciókulcsok még változhatnak, amíg a mögöttes registry le nem ül. Használd, de izoláld egy adapter mögé, ne szórd tele a kódbázisod a csatornanevekkel.

16.11Ami eddig kimaradt: a teszt-CI alakja

Összerakva a modult, egy Cloudflare-re szánt Nuxt-projekt teszt-CI-je három sávban fut, és mindegyik más ponton áll meg:

MikorMi futIdő
Minden mentésre (lokál)vitest --project unit figyelő módban< 1 s
Minden pusholt commitraunit + nuxt projekt, lint, nuxt typecheck1–2 perc
Pull requestre+ Nitro-tesztek, + wrangler deploy --dry-run méretellenőrzés3–5 perc
Merge után, deploy előttverzió feltöltése → E2E füstteszt a preview URL ellen → előléptetés5–10 perc
Ha csak egy dolgot viszel el ebből a modulból, az a negyedik sor legyen. A Nuxt tesztkészlete Node-ban fut; a te alkalmazásod nem. Az egyetlen olyan teszt, ami a valóságról szól, az a valódi deploy ellen futó — és mivel a host opció miatt ez ugyanaz a fájl, amit lokálisan is használsz, a bevezetése nagyjából tíz sor CI-konfiguráció.

16.12Ellenőrizd magad

  1. Zöld a teljes tesztkészleted, mégis elhasal az éles deploy. Nevezz meg három hibaosztályt, amit a @nuxt/test-utils elvileg sem tud elkapni.
    ▸ Válasz

    Bármi ebből: hiányzó Node API a workerd-ben, bundle-méret a 3/10 MB gzip limit fölött, 1 másodpercnél hosszabb modul-scope inicializálás, kereszt-kérés állapotszivárgás isolate-újrahasználatnál, subrequest-limit, CPU-limit, KV eventual consistency. A közös ok: a setup() Node-ban buildel és futtat, nem workerd-ben.

  2. Mi a különbség a Lazy prefix és a mockComponent között a tesztelés szempontjából? (Csapdakérdés.)
    ▸ Válasz

    Semmi közük egymáshoz — az egyik futásidejű optimalizálás (15. modul), a másik tesztidejű helyettesítés. A kérdés arra megy, hogy nehéz komponenst (térkép, chart) tesztben mockComponent-tel cserélsz ki, nem Lazy-vel: a Lazy a tesztben is betöltené.

  3. Miért nem elég a page.goto() Playwrightban egy Nuxt-oldalnál?
    ▸ Válasz

    Mert a Playwright alap várakozásai (load, networkidle) nem tudják, mikor fejeződött be a Vue hydratálása. A test-utils goto(url, { waitUntil: 'hydration' }) pontosan erre a pillanatra vár — enélkül flaky tesztek lesznek, tele waitForTimeout-tal.

  4. A setup() melyik opciójával futtatod ugyanazt a tesztfájlt egy valódi Workers-deploy ellen?
    ▸ Válasz

    host. Ha megadod, a test-utils nem buildel és nem indít szervert, hanem a megadott URL-t célozza. Így a lokálisan Node ellen futó füstteszt CI-ben a preview URL ellen fut, változtatás nélkül.

  5. Lokálisan írsz egy KV-kulcsot, azonnal visszaolvasod, megvan. Élesben néha nem. Miért?
    ▸ Válasz

    A lokális KV-emuláció SQLite-alapú és azonnal konzisztens; az éles KV globálisan eventually consistent. Ez a hibaosztály lokálisan sosem reprodukálódik — csak valódi deploy ellen futó teszt fogja meg.

  6. Hány kliens figyelheti egyszerre ugyanannak a Workernek a wrangler tail folyamát, és mi történik nagy forgalomnál?
    ▸ Válasz

    Legfeljebb 10 (a dashboard és a CLI együtt számít). Nagy forgalomnál a tail mintavételi módba vált és üzeneteket dob el — ezért érdemes --status, --search vagy --ip self szűréssel indítani.

  7. Két kapcsolót kell bekapcsolni ahhoz, hogy értelmes stack trace-t láss élesben. Melyeket?
    ▸ Válasz

    nitro.sourceMap: true a Nuxt-konfigban (a Nitro alapból nem generál source mapet) és upload_source_maps: true a wrangler-konfigban (a feltöltés külön kapcsoló). A visszafejtés a kérés után aszinkron történik, tehát nincs CPU-költsége.

  8. Egy API route-ból dobsz createError({ status: 500, message: 'DB timeout a users táblán' })-t. Mit lát a kliens?
    ▸ Válasz

    Nem látja a message-et — az szándékosan nem propagál szerveroldali hibánál, hogy belső részletek ne szivárogjanak. Ha kell neki információ, statusText-be vagy data-ba tedd.

  9. Miért különösen fájó a chunk-hiba Cloudflare Workers-en?
    ▸ Válasz

    Mert a Workers-deploy globálisan azonnali: nincs fokozatos kigördülés régiónként. A deploy pillanatában minden nyitott böngészőlapon egyszerre válnak érvénytelenné a régi hash-ű chunkok. A Nuxt alapból kemény újratöltéssel kezeli; az experimental.emitRouteChunkError: 'manual' adja vissza az irányítást.

  10. Miért nem használható jól a @cloudflare/vitest-pool-workers magára a Nuxt-appra?
    ▸ Válasz

    Mert kézzel írt Workerre tervezték, ahol a forrásfájl a belépőpont. A Nuxt belépőpontja a .output/server/index.mjs: generált bundle, amit előbb le kell fordítani, és amiben az egyes handlerek nem címezhetők külön. A Nuxt-tól független Worker-kódra (Durable Object, queue-consumer, cron-worker) viszont kifejezetten jó választás.

Előző15. modul — Teljesítmény: hydration, payload, bundle Következő 17. modul — Deploy Cloudflare Workers-re