Bis hierher läuft die Kladde-SPA mit ng serve, einem Entwicklungsserver auf Ihrem Rechner. Zu den Menschen kommt sie als Ordner mit statischen Dateien. Diese Lektion baut den Ordner, prüft ihn lokal, zeigt drei Wege der Auslieferung und endet mit einer Entscheidung als ADR (K4 1.3). Gehostet wird nichts; die Wege in Azure sind nach Seiten von Microsoft Learn beschrieben.
Stand der Prüfung. Gelaufen (Angular 22.2, Node 24.22.3, .NET 10.0.400, Kopie des Projekts aus 0402):
ng buildExit-Code 0; ein Build mit absichtlich kleinen Budgets Exit-Code 1; der Ordnerbrowsermitserve14 (MIT, nur lokales Werkzeug) mit und ohne Rückfall; der Test aus 0402 gegen den gebauten Ordner (3 Tests, Exit-Code 0); die Laufzeitkonfiguration mit geänderterconfig.jsonin Chromium; ein ASP.NET-Core-Programm mitMapFallbackToFile(Wegwerfprojekt,dotnet buildExit-Code 0). Nicht ausgeführt: alles in Azure, ein Container, nginx,staticwebapp.config.jsonin Static Web Apps, SSR und Prerender, ein Aspire-AppHost mit Angular (offen, nicht gelesen). Aussagen stammen von Learn (Static Web Apps, Container Apps „Overview“,MapFallbackToFile), angular.dev („Deployment“, „SSR“) und nginx.org (try_files), am Schreibtag gelesen. Zahlen zu Kosten stehen nicht im Text.
ng build
npx ng build nimmt die Konfiguration production (Vorgabe in angular.json): übersetzt, verkleinert, Dateinamen mit Prüfsumme. Die Ausgabe der Kladde (gekürzt):
Initial chunk files | Names | Raw size | Estimated transfer size
main-JAQEW2G7.js | main | 261.02 kB | 70.63 kB
styles-O44RJA43.css | styles | 55 bytes | 55 bytes
| Initial total | 261.07 kB | 70.69 kB
Lazy chunk files | Names | Raw size | Estimated transfer size
chunk-CuCrzbSa.js | buchung | 2.93 kB | 1.25 kB
Die Ausgabe liegt in dist/kladde-angular/; für statisches Hosting gilt der Ordner browser/ darin (angular.dev: „Copy the dist/my-app/browser/ directory to a web server or content delivery network“). Dort stehen index.html, die Bündel und alles aus public/. dist/ steht in der .gitignore der Vorlage.
Budgets lassen den Build scheitern, wenn die Anwendung zu groß wird. Die Vorlage setzt für das Anfangsbündel Warnung 500 kB und Fehler 1 MB. Mit absichtlich kleinen Werten (100 kB und 200 kB in angular.json) sah der Lauf so aus, Exit-Code 1:
▲ [WARNING] bundle initial exceeded maximum budget. Budget 100.00 kB was not met by 161.07 kB with a total of 261.07 kB.
X [ERROR] bundle initial exceeded maximum budget. Budget 200.00 kB was not met by 61.07 kB with a total of 261.07 kB.
``` Setzen Sie Budgets knapp über den heutigen Stand.
## Konfiguration zur Laufzeit, keine Geheimnisse
Ein Angular-Bundle ist **öffentlich**: Wer die Seite lädt, kann es lesen. Darin steht nichts Geheimes; die Client-Id der Anmeldung (0301) und die Adresse der API sind öffentlich. Was je Umgebung abweicht (die Adresse der API), baut man **nicht** ein: Sonst braucht jede Umgebung einen eigenen Build, und das geprüfte Ergebnis ist nicht das ausgelieferte. Besser: Das Bündel ist überall gleich und lädt beim Start eine kleine Datei:
```ts
@Service()
export class Konfiguration {
private werte: KonfigWerte = { apiBasis: '' };
get apiBasis(): string {
return this.werte.apiBasis;
}
async laden(): Promise<void> {
const antwort = await fetch('/config.json', { cache: 'no-store' });
if (!antwort.ok) {
throw new Error(`config.json nicht lesbar (${antwort.status})`);
}
this.werte = (await antwort.json()) as KonfigWerte;
}
}
KonfigWerte ist { apiBasis: string }. In app.config.ts hält provideAppInitializer(() => inject(Konfiguration).laden()) den Start an, bis die Datei da ist. Den Token API_BASIS und den Interceptor apiBasis, der ihn vor jede Adresse mit /api/ stellt, kennen Sie aus 0202; es fehlt der Wert aus der Datei (die Fabrik läuft erst bei der ersten Anfrage, also nach dem Laden):
{ provide: API_BASIS, useFactory: () => inject(Konfiguration).apiBasis },
public/config.json mit { "apiBasis": "" } gilt für den Dev-Server mit Proxy (0101) und für gleichen Ursprung wie die API. Zum Beweis: Die Datei im Ordner browser auf { "apiBasis": "https://api.kladde.example" } geändert, und der Browser sendete die erste Anfrage an https://api.kladde.example/api/contacts?page=1&size=20, ohne neuen Build. Eine andere Herkunft heißt für die API CORS (0302), und die Adresse der Produktionsseite muss bei kladde-spa als Weiterleitung stehen (0301).