← Cloudflare Workers / workers / static-assets / routing
Jednostránková aplikace (SPA)
Jednostránkové aplikace (SPA) jsou webové aplikace vykreslované na straně klienta (CSR). Často se vytvářejí pomocí frameworku, jako je React, Vue nebo Svelte. Build proces těchto frameworků vygeneruje jediný /index.html soubor a doprovodné klientské zdroje (např. JavaScriptové balíčky, CSS styly, obrázky, fonty atd.). Data se obvykle načítají na straně klienta z API pomocí klientských požadavků.
Když nakonfigurujete single-page-application režimu Cloudflare poskytuje výchozí chování směrování, které automaticky obsluhuje váš /index.html soubor pro navigační požadavky (tedy ty s Sec-Fetch-Mode: navigate hlaviček), které neodpovídají žádnému jinému aktivu. Chcete-li mít větší kontrolu nad tím, které cesty vyvolají váš skript Workeru, můžete použít pokročilé řízení směrování.
Konfigurace
Chcete-li nasadit Single Page Application do Workers, musíte nakonfigurovat assets.directory a assets.not_found_handling možnosti ve svém Konfigurační soubor Wrangler:
{
"name": "my-worker",
// Set this to today's date
"compatibility_date": "2026-08-28",
"assets": {
"directory": "./dist/",
"not_found_handling": "single-page-application"
}
}name = "my-worker"
# Set this to today's date
compatibility_date = "2026-08-28"
[assets]
directory = "./dist/"
not_found_handling = "single-page-application"Konfigurace assets.not_found_handling na single-page-application přepisuje výchozí chování obsluhy Workers pro statické soubory. Pokud příchozí požadavek neodpovídá žádnému souboru ve assets.directory, Workers odešle obsah /index.html soubor s 200 OK stav.
Navigační požadavky
Pokud máte skript Workeru (main), mají nakonfigurováno assets.not_found_handling, a použít assets_navigation_prefers_asset_serving příznak kompatibility (nebo nastavte datum kompatibility na 2025-04-01 nebo novější), navigační požadavky nevyvolá skript Workeru. navigační požadavek je požadavek vytvořený pomocí Sec-Fetch-Mode: navigate hlavičku, kterou prohlížeče automaticky připojují při přechodu na stránku. Díky tomu se sníží počet zpoplatněných vyvolání vašeho skriptu Workeru, což se hodí zejména u aplikací náročných na klientský kód, které by jinak Worker skript vyvolávaly velmi často a zbytečně.
Callbacky na straně klienta
V některých případech může být potřeba předat hodnotu z navigačního požadavku do vašeho Worker skriptu. Pokud například fungujete jako OAuth callback, můžete očekávat požadavky na určitou trasu, například /oauth/callback?code=.... S assets_navigation_prefers_asset_serving příznak budou vaše HTML podklady obsluhovány staticky namísto vašeho Worker skriptu. V takovém případě doporučujeme, ať už jako součást klientské aplikace pro danou trasu, nebo pomocí zjednodušeného HTML souboru určeného pro konkrétní endpoint, předat hodnotu na server pomocí klientského JavaScriptu.
<!DOCTYPE html>
<html>
<head>
<title>OAuth callback</title>
</head>
<body>
<p>Loading...</p>
<script>
(async () => {
const response = await fetch("/api/oauth/callback" + window.location.search);
if (response.ok) {
window.location.href = '/';
} else {
document.querySelector('p').textContent = 'Error: ' + (await response.json()).error;
}
})();
</script>
</body>
</html>import { WorkerEntrypoint } from "cloudflare:workers";
export default class extends WorkerEntrypoint {
async fetch(request) {
const url = new URL(request.url);
if (url.pathname === "/api/oauth/callback") {
const code = url.searchParams.get("code");
const sessionId =
await exchangeAuthorizationCodeForAccessAndRefreshTokensAndPersistToDatabaseAndGetSessionId(
code,
);
if (sessionId) {
return new Response(null, {
headers: {
"Set-Cookie": `sessionId=${sessionId}; HttpOnly; SameSite=Strict; Secure; Path=/; Max-Age=86400`,
},
});
} else {
return Response.json(
{ error: "Invalid OAuth code. Please try again." },
{ status: 400 },
);
}
}
return new Response(null, { status: 404 });
}
}import { WorkerEntrypoint } from "cloudflare:workers";
export default class extends WorkerEntrypoint {
async fetch(request: Request) {
const url = new URL(request.url);
if (url.pathname === "/api/oauth/callback") {
const code = url.searchParams.get("code");
const sessionId = await exchangeAuthorizationCodeForAccessAndRefreshTokensAndPersistToDatabaseAndGetSessionId(code);
if (sessionId) {
return new Response(null, {
headers: {
"Set-Cookie": `sessionId=${sessionId}; HttpOnly; SameSite=Strict; Secure; Path=/; Max-Age=86400`,
},
});
} else {
return Response.json(
{ error: "Invalid OAuth code. Please try again." },
{ status: 400 }
);
}
}
return new Response(null, { status: 404 });
}
}Pokročilé řízení směrování
Pro explicitnější kontrolu nad chováním směrování SPA můžete použít run_worker_first polem vzorů tras. Tento přístup vypíná automatické Sec-Fetch-Mode: navigate detekci a dává vám explicitní kontrolu nad tím, které požadavky má zpracovat váš Worker skript a které mají být obslouženy jako statická aktiva.
{
"name": "my-worker",
// Set this to today's date
"compatibility_date": "2026-08-28",
"main": "./src/index.ts",
"assets": {
"directory": "./dist/",
"not_found_handling": "single-page-application",
"binding": "ASSETS",
"run_worker_first": ["/api/*", "!/api/docs/*"]
}
}name = "my-worker"
# Set this to today's date
compatibility_date = "2026-08-28"
main = "./src/index.ts"
[assets]
directory = "./dist/"
not_found_handling = "single-page-application"
binding = "ASSETS"
run_worker_first = [ "/api/*", "!/api/docs/*" ]Tato konfigurace poskytuje explicitní kontrolu nad směrováním, aniž by se spoléhala na navigační hlavičky prohlížeče, což ji předurčuje pro komplexní SPA vyžadující jemně odstupňované chování směrování. Skript Workeru pak může zpracovat odpovídající trasy a (volitelně za použití assets binding) a obsluhovat dynamický obsah.
Například:
export default {
async fetch(request, env) {
const url = new URL(request.url);
if (url.pathname === "/api/name") {
return new Response(JSON.stringify({ name: "Cloudflare" }), {
headers: { "Content-Type": "application/json" },
});
}
return new Response(null, { status: 404 });
},
};export default {
async fetch(request, env): Promise<Response> {
const url = new URL(request.url);
if (url.pathname === "/api/name") {
return new Response(JSON.stringify({ name: "Cloudflare" }), {
headers: { "Content-Type": "application/json" },
});
}
return new Response(null, { status: 404 });
},
} satisfies ExportedHandler;Můžete také použít run_worker_first k vložení dat do vaší SPA shell dříve, než se dostane do prohlížeče. Kompletní příklad použití HTMLRewriter k předběžnému načtení dat API a jejich vložení do HTML streamu najdete v shellu SPA s bootstrap daty.
Lokální vývoj
Pokud používáte SPA framework postavený na Vite, může vás zajímat Plugin Vite který nabízí vývojářský zážitek nativní pro Vite.
Referenční informace
Ve většině případů stačí nakonfigurovat assets.not_found_handling na single-page-application poskytne požadované chování. Pokud si vytváříte vlastní framework nebo máte specifické požadavky, následující diagram vám ukáže, jak přesně se rozhodování o směrování provádí.
Kompletní diagram rozhodování o směrování
flowchart
Request@{ shape: stadium, label: "Incoming request" }
Request-->RunWorkerFirst
RunWorkerFirst@{ shape: diamond, label: "Run Worker script first?" }
RunWorkerFirst-->|Request matches run_worker_first path|WorkerScriptInvoked
RunWorkerFirst-->|Request matches run_worker_first negative path|AssetServing
RunWorkerFirst-->|No matches|RequestMatchesAsset
RequestMatchesAsset@{ shape: diamond, label: "Request matches asset?" }
RequestMatchesAsset-->|Yes|AssetServing
RequestMatchesAsset-->|No|WorkerScriptPresent
WorkerScriptPresent@{ shape: diamond, label: "Worker script present?" }
WorkerScriptPresent-->|No|AssetServing
WorkerScriptPresent-->|Yes|RequestNavigation
RequestNavigation@{ shape: diamond, label: "Request is navigation request?" }
RequestNavigation-->|No|WorkerScriptInvoked
WorkerScriptInvoked@{ shape: rect, label: "Worker script invoked" }
WorkerScriptInvoked-.->|Asset binding|AssetServing
RequestNavigation-->|Yes|AssetServing
subgraph Asset serving
AssetServing@{ shape: diamond, label: "Request matches asset?" }
AssetServing-->|Yes|AssetServed
AssetServed@{ shape: stadium, label: "**200 OK**<br />asset served" }
AssetServing-->|No|NotFoundHandling
subgraph single-page-application
NotFoundHandling@{ shape: rect, label: "Request rewritten to /index.html" }
NotFoundHandling-->SPAExists
SPAExists@{ shape: diamond, label: "HTML Page exists?" }
SPAExists-->|Yes|SPAServed
SPAExists-->|No|Generic404PageServed
Generic404PageServed@{ shape: stadium, label: "**404 Not Found**<br />null-body response served" }
SPAServed@{ shape: stadium, label: "**200 OK**<br />/index.html page served" }
end
endPožadavky se účtují pouze v případě, že je vyvolán skript Workeru. Odtud je pak možné obsluhovat aktiva pomocí vazby assets (na diagramu výše znázorněné čárkovanou čarou).
Ačkoli to pravděpodobně neovlivní způsob, jakým je SPA obsluhována, více o tom, jak přiřazujeme prostředky, si můžete přečíst v Dokumentace ke zpracování HTML.