Im Jahr 2012 wurde mir das Konzept der Trennung von Frontend- und Backend-Anliegen bewusst, als ich erkannte, wie schwer zu wartende serverseitig gerenderte Templates oft zu aufwendigen und kostspieligen Neuschreibungen von Software-Systemen führen. Wenn es Ihnen darum geht, wartbare Software zu schaffen, gibt es eine goldene Regel, die zu beachten ist: Halten Sie die Geschäftslogik hinter der API vom Präsentationslogik in der Benutzeroberfläche getrennt. Diese Trennung ist entscheidend, um die Flexibilität und Wartbarkeit eines Systems langfristig zu sichern.
Angular stellt in diesem Kontext eine elegante Lösung dar. Es folgt dem Pareto-Prinzip, auch als 80/20-Regel bekannt. Diese Plattform hat sich mittlerweile zu einem ausgereiften und sich ständig weiterentwickelnden Werkzeug entwickelt, das es ermöglicht, 80 % der Aufgaben mit nur 20 % des Aufwands zu erledigen. Jede größere Version von Angular wird 18 Monate lang unterstützt, was eine kontinuierliche Lernkurve ermöglicht, während gleichzeitig alte Funktionen nach und nach abgeschafft werden. Aus der Perspektive eines Full-Stack-Entwicklers ist diese kontinuierliche Entwicklung von unschätzbarem Wert, da Ihre Fähigkeiten und Schulungen über Jahre hinweg relevant und aktuell bleiben.
Die Philosophie hinter Angular setzt dabei auf Konfiguration statt auf Konvention. Obwohl konventionelle Frameworks von außen elegant wirken mögen, machen sie es Einsteigern schwer, sich schnell mit der Framework-Struktur zurechtzufinden. Konfigurationsbasierte Frameworks hingegen legen ihre inneren Mechanismen durch explizite Konfiguration und Haken offen, an die Entwickler ihre eigenen Verhaltensweisen anknüpfen können. Im Wesentlichen, wo AngularJS noch viel "Magie" in sich hatte, die verwirrend, unvorhersehbar und schwer zu debuggen war, strebt Angular nach einem klareren, nicht-magischen Ansatz. Die Philosophie „Konfiguration über Konvention“ führt zu einem umfangreicheren, aber wesentlich besser wartbaren Code. Verbose Code ist kein Fehler; im Gegenteil, er ist eine Stärke. Prägnanter Code mag anfangs schneller zu schreiben sein, doch er wird auf lange Sicht die Wartung und Erweiterung des Systems erschweren. Dies unterstreicht ein bekanntes Zitat von Andy Hunt und David Thomas im „Pragmatic Programmer“: "Denken Sie daran, dass Sie (und andere, die nach Ihnen kommen) den Code Hunderte von Malen lesen werden, aber nur wenige Male schreiben." Dabei erinnert uns Andy Hunts Gesetz des Designs: "Wenn Sie nicht jedes Stück Code leicht entfernen können, dann ist das Design fehlerhaft."
Angular ermöglicht eine konsequente Umsetzung dieser Prinzipien durch seine verschiedenen Mechanismen. Es vermeidet viele benutzerdefinierte Konventionen, die in AngularJS eingeführt wurden, wie etwa das ng-click-Attribut, und ersetzt es durch eine natürlichere Sprache, die auf bestehenden HTML-Elementen und -Eigenschaften aufbaut. So wird aus dem ng-click-Attribut einfach (click), was HTML erweitert, ohne es zu ersetzen.
Die Weiterentwicklung von Angular ist ein zentraler Bestandteil des Ökosystems. Sie müssen nicht eine spezifische Version von Angular lernen, sondern eine Plattform, die ständig weiterentwickelt wird. Dies ist ein wesentlicher Vorteil, da Sie sich keine Sorgen machen müssen, mit jeder neuen Version von Angular erneut bei Null anzufangen. Die Angular-Entwicklergemeinde legt großen Wert auf Rückwärtskompatibilität, sodass bei jeder neuen Version dafür gesorgt wird, dass Ihr Code auch in Zukunft lauffähig bleibt. Probleme, die durch neue Versionen entstehen, können mithilfe automatisierter Tools oder expliziter Anleitungen im Angular Update Guide gelöst werden.
Ein weiterer wichtiger Aspekt, der Angular langfristig zukunftssicher macht, ist TypeScript. TypeScript ermöglicht es, neue Funktionen effizient umzusetzen und gleichzeitig ältere Browser zu unterstützen, sodass Ihr Code ein größtmögliches Publikum erreicht. TypeScript, ursprünglich von Anders Hejlsberg, dem Schöpfer von Turbo Pascal und C#, entwickelt, bringt die Vorteile statisch typisierter Sprachen in die Welt von JavaScript. Statisch typisierte Sprachen bieten den Vorteil, dass Fehler bereits zur Compile-Zeit erkannt werden können, was die Entwicklungskosten erheblich senkt. TypeScript ist jedoch kein traditioneller Compiler, sondern ein Transpiler, der den Code von einer Sprache in eine andere übersetzt. Der resultierende Code ist reines JavaScript, das in jeder modernen JavaScript-Umgebung ausgeführt werden kann.
TypeScript trägt nicht nur dazu bei, Fehler frühzeitig zu erkennen, sondern auch dazu, den sogenannten JavaScript Feature Gap zu schließen. Während JavaScript zwischen 1999 und 2009 keine nennenswerten Neuerungen erlebte, gibt es heute jährliche Updates, die die Funktionalitäten der Sprache kontinuierlich erweitern. TypeScript ermöglicht es Entwicklern, von den neuesten JavaScript-Funktionen zu profitieren, während gleichzeitig die Kompatibilität mit älteren Browsern gewahrt bleibt.
Angulars Architektur basiert auf dem MV*-Muster, einer Mischung aus den bekannten MVC- und MVVM-Architekturen. Der zentrale Bestandteil dieser Architektur ist das Konzept des ViewModels, das als Bindeglied zwischen der Benutzeroberfläche und den zugrunde liegenden Geschäftslogik dient. In Angular wird dieses Bindeglied durch Binding umgesetzt, wodurch die View mit der Model- oder Service-Ebene verbunden wird. Diese Trennung von Logik und Darstellung ist entscheidend für eine saubere und wartbare Architektur, die es Entwicklern ermöglicht, die Benutzeroberfläche unabhängig von den zugrunde liegenden Geschäftsprozessen zu ändern.
Die Kombination aus einer klaren Trennung der Verantwortlichkeiten, einer starken Typisierung durch TypeScript und einem kontinuierlich wachsenden Ökosystem stellt sicher, dass Angular eine Plattform bleibt, die Entwicklern hilft, moderne, wartbare und zukunftssichere Anwendungen zu erstellen. Indem es auf Konfiguration statt Konvention setzt und eine klare Trennung der Logik ermöglicht, trägt Angular dazu bei, dass Softwareprojekte auch langfristig skalierbar und wartbar bleiben.
Wie man die Authentifizierung in modernen Webanwendungen mit JWT und Caching implementiert
In modernen Webanwendungen ist die Authentifizierung ein zentraler Bestandteil der Benutzerinteraktion. Ein häufig genutztes Verfahren zur Implementierung von Authentifizierung und Autorisierung ist die Verwendung von JSON Web Tokens (JWT). Dabei geht es nicht nur um den sicheren Austausch von Informationen zwischen Client und Server, sondern auch um die effiziente Verwaltung des Authentifizierungsstatus, insbesondere in Single-Page-Applications (SPAs). Ein besonders wichtiger Aspekt ist das Caching der Authentifizierungsinformationen, um wiederholte Login-Vorgänge zu vermeiden und die Benutzererfahrung zu verbessern.
Im Folgenden wird ein Überblick darüber gegeben, wie man die Authentifizierung mit JWT in einer Angular-Anwendung implementiert, wie man diese sicher speichert und wie man Caching effektiv nutzt, um die Authentifizierung zu optimieren.
Die login-Methode encapsuliert die korrekte Reihenfolge der Operationen, indem sie den Authentifizierungsprovider mit den E-Mail- und Passwortdaten aufruft, das empfangene JWT decodiert, es transformiert und anschließend den Authentifizierungsstatus (authStatus$) aktualisiert. Die Methode getCurrentUser() wird nur aufgerufen, wenn der Status als authentifiziert erkannt wird. Danach wird currentUser$ aktualisiert, und etwaige Fehler werden durch eine benutzerdefinierte Fehlerbehandlungsfunktion (transformError) abgefangen. Der Observablestream wird schließlich durch den Aufruf von subscribe aktiviert. Im Fehlerfall wird die Methode logout() aufgerufen, um den korrekten Status der Anwendung zu bewahren und Fehler weiterzugeben, indem sie erneut geworfen werden.
Ein zentraler Bestandteil des Authentifizierungsprozesses ist der Logout-Mechanismus. Der Logout wird ausgelöst, wenn der Benutzer den Logout-Button drückt oder wenn ein unbefugter Zugriffsversuch festgestellt wird. Um unbefugte Zugriffsversuche zu erkennen, kann ein Router-Auth-Guard verwendet werden, der es ermöglicht, die Authentifizierung zu überprüfen, während der Benutzer durch die Anwendung navigiert. Ein solches Konzept wird weiter im Kapitel behandelt.
Die logout-Methode selbst ist einfach: Sie setzt den authStatus$-Stream auf den Standardwert zurück, was den Benutzer effektiv ausloggt. Die Verwendung von setTimeout sorgt dafür, dass alle Statusänderungen korrekt behandelt werden, ohne dass Timing-Probleme entstehen, die durch das gleichzeitige Ändern von mehreren Kernkomponenten verursacht werden könnten.
In Bezug auf das Prinzip der Offenheit und Schließung (Open/Closed-Prinzip) ist die login-Methode erweiterbar, ohne dass Änderungen an ihrer grundlegenden Struktur erforderlich sind. Sie bleibt geschlossen für Modifikationen, weil sie über abstrakte Funktionen wie authProvider, transformJwtToken und getCurrentUser verfügt, die durch abgeleitete Klassen überschrieben werden können. Dadurch lässt sich der Authentifizierungsanbieter extern bereitstellen, ohne die login-Methode selbst zu verändern.
Die wahre Stärke der abstrakten Klassen zeigt sich in der Möglichkeit, gemeinsame Funktionalitäten auf eine erweiterbare Weise zu kapseln. Diese Flexibilität ist entscheidend, wenn man die Authentifizierung in größeren und komplexeren Anwendungen implementieren möchte.
Caching des Authentifizierungsstatus
Eines der häufigsten Probleme in Webanwendungen ist die Notwendigkeit, den Benutzer nach jedem Seiten-Reload erneut anzumelden. Um dies zu vermeiden, ist das Caching des Authentifizierungsstatus unerlässlich. Es gibt mehrere Möglichkeiten, Daten im Browser zu speichern, darunter Cookies, localStorage und sessionStorage.
Cookies sind aufgrund ihrer Sicherheitslücken, wie etwa der Gefahr, von Angreifern gestohlen zu werden, nicht ideal für die Speicherung sicherheitsrelevanter Daten. Zudem sind Cookies auf 4 KB begrenzt und können ein Ablaufdatum haben, was sie für das Caching von Authentifizierungsinformationen ungeeignet macht.
localStorage und sessionStorage bieten eine bessere Möglichkeit, größere Datenmengen zu speichern. Der Unterschied zwischen den beiden besteht darin, dass sessionStorage nur die Daten speichert, solange die aktuelle Browser-Tab geöffnet ist, während localStorage die Daten über mehrere Sitzungen hinweg speichert, auch wenn der Browser neu gestartet wird.
Da die Authentifizierungsdaten in der Regel mehrere Minuten bis hin zu mehreren Tagen oder sogar Monaten aufbewahrt werden müssen, eignet sich localStorage für diese Aufgabe besser als sessionStorage. Ein weiterer Vorteil von localStorage ist, dass es isoliert und vor Angriffen durch andere Seiten geschützt ist.
Implementierung des Caching mit localStorage
Die Speicherung von Authentifizierungsdaten im localStorage erfolgt über einen Cache-Service, der als zentralisierte Methode dient, um die Daten zu speichern und abzurufen. Der Cache-Service sollte Methoden enthalten, die Daten sicher speichern, abrufen und löschen können. Ein einfaches Beispiel eines solchen Cache-Services ist die folgende Implementierung:
Dieser Service stellt grundlegende Methoden zum Speichern, Abrufen und Löschen von Daten im localStorage bereit. Es ist wichtig zu betonen, dass dieser Cache-Service nicht zur Synchronisierung von Zuständen zwischen verschiedenen Komponenten verwendet werden sollte, sondern lediglich zum Speichern von Daten.
Die AuthService-Klasse wird dann so erweitert, dass sie diesen Cache-Service verwendet, um die Authentifizierungsinformationen, insbesondere das JWT, zu speichern. Dies gewährleistet, dass der Benutzer nach einem Reload der Seite weiterhin angemeldet bleibt, ohne den gesamten Login-Prozess erneut durchlaufen zu müssen.
In diesem Beispiel wird die authStatus$-Variable mithilfe eines BehaviorSubject initialisiert, das den gespeicherten Wert aus dem Cache abruft oder den Standardwert verwendet, falls keine Daten vorhanden sind. Die tap-Funktion wird verwendet, um den Cache zu aktualisieren, wenn sich der Status ändert.
Sicherheit des JWTs und mögliche Angriffe
Wichtig ist, dass der JWT selbst sicher gespeichert und verarbeitet wird. Während localStorage für das Caching des Tokens geeignet ist, muss darauf geachtet werden, dass der Token nicht unverschlüsselt oder leicht zugänglich bleibt. Eine geeignete Methode zur Absicherung der JWTs umfasst die Verschlüsselung des Tokens und die regelmäßige Überprüfung seiner Gültigkeit. In Kapitel 7 wird im Detail erklärt, wie man mit REST- und GraphQL-APIs arbeitet und wie man JWTs sicher verschlüsselt und überprüft.
Die Lebensdauer des JWT sollte begrenzt sein, und der Token sollte regelmäßig erneuert werden, um das Risiko eines missbräuchlichen Zugriffs zu minimieren. Ein zentraler Punkt dabei ist das Verständnis des JWT-Lebenszyklus, der eine kritische Rolle in der Sicherheit und dem Schutz der Anwendung spielt.
Warum Monorepos eine gute Wahl für große Projekte sein können
Monorepos bieten eine einzigartige Möglichkeit, Code aus verschiedenen Projekten in einem einzigen Repository zu verwalten. Diese Strategie ermöglicht ein einheitliches Versionsmanagement, eine vereinfachte Verwaltung von Abhängigkeiten und ein effizienteres Teilen von Code zwischen Projekten. Ein entscheidender Vorteil eines Monorepos ist die Möglichkeit, zwischen verschiedenen Projekten in derselben Entwicklungsumgebung zu wechseln, ohne die gewohnte Arbeitsweise zu unterbrechen. Entwickler können zum Beispiel TypeScript-Interfaces zwischen Frontend und Backend problemlos teilen und sicherstellen, dass Datenobjekte immer übereinstimmen.
Durch den Einsatz von Multi-Root-Arbeitsbereichen in Visual Studio Code können mehrere Projekte gleichzeitig geöffnet und in einem einzigen Fenster bearbeitet werden. Dies vereinfacht die Navigation und das Arbeiten an verschiedenen Komponenten erheblich. Das zentrale Management von Code in einem Monorepo bedeutet auch, dass Änderungen, die mehrere Projekte betreffen, in einem einzigen Commit zusammengefasst werden können. Diese "atomaren" Änderungen ermöglichen es, Code zu ändern und gleichzeitig die Synchronisation zwischen verschiedenen Komponenten zu gewährleisten, ohne auf die Koordination über mehrere Repositories hinweg angewiesen zu sein.
Neben der einfacheren Codeverwaltung bringt ein Monorepo auch Vereinfachungen in Bezug auf den Wartungsprozess mit sich. Es gibt nur einen Pull Request (PR), der überprüft werden muss, eine einzige Bereitstellung, die validiert werden muss, und einen Satz von Prüfungen, die durchgesetzt werden. Diese Struktur vereinfacht das Testen und das Sicherstellen der Codequalität erheblich. Besonders bei großen Teams kann dies den Entwicklungsprozess spürbar beschleunigen.
Jedoch hat die Verwendung von Monorepos auch ihre Schattenseiten. In großen Anwendungen kann die schiere Menge an Dateien und Code zu einem Problem werden. Dies erfordert nicht nur leistungsfähige Hardware für die Entwickler und die Continuous Integration (CI)-Server, sondern kann auch die Automatisierung des Deployments sehr komplex machen. Für kleinere Teams oder Projekte kann das Setup eines Monorepos sogar überwältigend wirken. Wenn ein neuer Entwickler dem Team beitritt, muss dieser zunächst in das gesamte System und die Arbeitsweise eingearbeitet werden, was eine gewisse Lernkurve mit sich bringt.
Monorepos waren in der Vergangenheit vor allem für große Tech-Unternehmen von Interesse, da sie spezielle Infrastruktur und Ressourcen erforderten. Doch mit der Veröffentlichung des Open-Source-Tools Bazel von Google im Jahr 2019 und der zunehmenden Popularität von Tools wie Nx, einem modernen Build-System, das von ehemaligen Google-Mitarbeitern entwickelt wurde, wird das Arbeiten mit Monorepos auch für kleinere Projekte zunehmend praktikabel. Nx bietet eine leistungsstarke Integration für Monorepos und unterstützt Teams dabei, große Codebasen effizient zu verwalten. Besonders hervorzuheben ist die Cloud-Option von Nx, die die Build-Prozesse durch verteiltes Caching und Parallelisierung optimiert, ohne dass das Team in komplexe Infrastruktur investieren muss.
Ein wesentlicher Vorteil von Nx ist die Möglichkeit, Code in separate Bibliotheken zu unterteilen, die über Module hinweg wiederverwendet werden können. Dies hilft, Merge-Konflikte zu reduzieren und ermöglicht es mehreren Entwicklern, gleichzeitig an verschiedenen Teilen des Projekts zu arbeiten. Die Architektur von Nx ist besonders für größere Teams und Unternehmen geeignet, die klare Strukturen und eine skalierbare Lösung benötigen. Jedoch kann der Einsatz von Nx auch mit einer gewissen Komplexität und einer steilen Lernkurve verbunden sein. Der Einstieg in ein solches Tool sollte gut überlegt sein, da es zusätzlich zur bereits vorhandenen Technologie wie JavaScript, TypeScript, Git und Node.js eine weitere Dimension hinzufügt, die Expertenwissen erfordert.
Trotz seiner Vorteile gibt es auch Kritik an der Nutzung von Monorepos, vor allem in Bezug auf die kognitive Belastung, die durch die Vielzahl an verwendeten Tools entsteht. Mit der Integration von Tools wie esbuild und Vite für schnellere Builds könnte dies jedoch langfristig verbessert werden. Nx bietet hier durch seine Funktionen wie verteiltes Caching und zentrales Abhängigkeitsmanagement eine Lösung, die bei den richtigen Projekten einen erheblichen Unterschied machen kann.
Die Entscheidung, ob man ein Monorepo verwendet oder nicht, hängt also stark von der Größe und den Anforderungen des Projekts ab. Während es für große Projekte und Teams eine unschätzbare Hilfe sein kann, ist es für kleinere Teams, die einen einfachen Entwicklungsprozess bevorzugen, möglicherweise zu komplex. Bevor man sich für die Einführung eines Monorepos entscheidet, sollte man daher genau abwägen, welche Anforderungen an das Projekt bestehen und ob die zusätzlichen Ressourcen gerechtfertigt sind.
Die Nutzung von Monorepos ist eine Entscheidung, die nicht nur technische Aspekte, sondern auch organisatorische und logistische Überlegungen mit sich bringt. In vielen Fällen können die Vorteile die Herausforderungen überwiegen, vor allem, wenn man bereit ist, in die notwendigen Tools und Schulungen zu investieren, um diese Architektur erfolgreich zu implementieren.
Wie man Docker mit Angular-Apps integriert und für die Produktion vorbereitet
Docker bietet eine Reihe von Vorteilen, wie etwa das Containerisieren von Anwendungen und das einfache Bereitstellen über verschiedene Umgebungen hinweg. Um diese Vorteile vollständig zu nutzen, ist es jedoch wichtig, die Grundlagen der Arbeit mit Docker zu verstehen. Insbesondere das Erstellen, Testen und Veröffentlichen von Docker-Containern für Angular-Apps erfordert eine präzise Konfiguration und ein tiefes Verständnis der zugrunde liegenden Prozesse.
Docker-Installation
Um mit Docker arbeiten zu können, müssen Sie zunächst das Docker-Umfeld auf Ihrem Computer installieren. Die neueste Version von Docker Desktop kann von Docker's offizieller Webseite heruntergeladen werden. Die Installation erfolgt durch einfaches Befolgen der Anweisungen des Installationsprogramms. Sobald Docker auf Ihrem Computer installiert ist, können Sie mit der Containerisierung Ihrer Anwendung beginnen.
Einrichten von npm-Skripten für Docker
Für die Automatisierung des Bauens, Testens und Veröffentlichens von Containern in Angular-Projekten gibt es spezielle npm-Skripte. Diese Skripte können sowohl unter Windows 10 als auch macOS verwendet werden. Die aktuellste Version dieser npm-Skripte kann über den Befehl npx mrm npm-docker in Ihrem Projekt installiert werden. Nach der Ausführung dieses Befehls sind die notwendigen Docker-Skripte in Ihrem Projekt vorhanden und bereit zur Nutzung.
Bauen und Veröffentlichen eines Docker-Images
Nachdem Sie die npm-Skripte eingerichtet haben, müssen Sie sicherstellen, dass Ihr Projekt korrekt konfiguriert ist, um es zu containerisieren, ein ausführbares Image zu bauen und dieses dann auf Docker Hub zu veröffentlichen. Docker Hub ist eine öffentliche Repository-Plattform, auf der Container-Images gespeichert und weltweit zugänglich gemacht werden können. Um mit Docker Hub zu arbeiten, sind die folgenden Schritte notwendig:
-
Erstellen Sie ein Docker Hub-Konto auf Docker Hub.
-
Erstellen Sie ein öffentliches (kostenloses) Repository für Ihre Anwendung.
-
Fügen Sie in der
package.json-Datei Ihres Projekts die folgenden Konfigurationsparameter hinzu:
Die Parameter werden wie folgt erklärt: Der namespace ist Ihr Benutzername auf Docker Hub, und das repository ist der Name des Repositories, das Sie erstellt haben. Der imageName ist ein Name für das Container-Image, der es Ihnen erleichtert, dieses später zu identifizieren. Der imagePort ist der Port, über den Ihre Anwendung von außen zugänglich gemacht wird, während der internalContainerPort den Port beschreibt, auf dem der Webserver innerhalb des Containers läuft.
Npm-Skripte für Docker im Detail
Die Skripte, die durch den npx mrm npm-docker-Befehl hinzugefügt werden, sind darauf ausgelegt, Ihnen die Arbeit mit Docker zu erleichtern. Diese Skripte sind in der package.json-Datei Ihres Projekts zu finden und ermöglichen eine saubere Trennung der verschiedenen Schritte im Docker-Prozess. Hier ist eine detaillierte Erklärung der wichtigsten Skripte:
-
Bauen eines Docker-Images:
Das Skriptdocker:buildbaut das Docker-Image, indem es die Angular-Anwendung in Produktionsmodus baut und dann das Docker-Image erstellt. Der Befehlcross-conf-env docker image build --platform linux/amd64,linux/arm64 . -t $npm_package_config_imageRepo:$npm_package_versionstellt sicher, dass der Build auf verschiedenen Plattformen funktioniert. -
Taggen des Images:
Nach dem Build-Prozess wird das Image mit einem Tag versehen, der die Version des Images und den neuesten Tag umfasst. Dies geschieht durch das Skriptdocker:tag. -
Starten und Stoppen von Docker-Containern:
Die Skriptedocker:stopunddocker:runermöglichen das Stoppen und Ausführen von Docker-Containern. Dasdocker:run-Skript sorgt dafür, dass das neu erstellte Image im Docker-Container gestartet wird. -
Veröffentlichen des Docker-Images:
Das Skriptdocker:publishveröffentlicht das erstellte Docker-Image auf Docker Hub. Zuerst wird das Image mit der Versionsnummer veröffentlicht, gefolgt von einem weiteren Push mit dem Taglatest, um immer die neueste Version der Anwendung bereitzustellen. -
Protokolle anzeigen und Debuggen:
Die Skriptedocker:taillogsunddocker:debugermöglichen das Überwachen der Docker-Container-Logs und das Debuggen der Anwendung, was bei der Fehlerbehebung und Überwachung der Anwendung in der Produktion hilfreich ist.
Optimierung für die Produktion
Bevor Sie das Docker-Image veröffentlichen, müssen Sie sicherstellen, dass Ihre Angular-Anwendung für den Produktionsmodus optimiert ist. Der Befehl ng build --prod sorgt dafür, dass Ihre Anwendung minimiert und optimiert wird, was zu einer geringeren Dateigröße und einer höheren Laufzeitperformance führt. Zudem sollten Sie sicherstellen, dass alle Unit-Tests vor dem Bauen des Docker-Images erfolgreich ausgeführt werden, um Fehler im Container zu vermeiden.
Das Beispiel für das Pre-Build-Skript könnte folgendermaßen aussehen:
Verwendung von Docker-Images in verschiedenen Umgebungen
Das Ziel von Docker ist es, eine konsistente Entwicklungsumgebung zu schaffen, die auf allen Maschinen und in allen Umgebungen identisch läuft. Dazu müssen Sie sicherstellen, dass Ihr Docker-Image richtig konfiguriert ist und in verschiedenen Umgebungen problemlos läuft. Es ist wichtig, die Unterschiede zwischen Entwicklungs-, Test- und Produktionsumgebungen zu berücksichtigen, insbesondere wenn Sie mit CI/CD-Pipelines arbeiten, um Ihre Anwendung automatisch zu bauen und zu veröffentlichen.
Fazit
Die Integration von Docker in den Entwicklungsworkflow für Angular-Anwendungen ermöglicht es, Container-Images effizient zu erstellen, zu testen und zu veröffentlichen. Dies stellt sicher, dass die Anwendung in jeder Umgebung gleich funktioniert und die Bereitstellung in Produktionsumgebungen schnell und einfach erfolgt. Es ist jedoch entscheidend, die richtigen npm-Skripte zu konfigurieren, den Produktionsmodus zu optimieren und sich mit den grundlegenden Docker-Befehlen vertraut zu machen, um ein reibungsloses Deployment zu gewährleisten.
Waren die Funde aus Sanauli ein Beweis für eine frühe Welle der indo-iranischen Migration und ihre Verbindung mit der späten Harappa-Kultur?
Wie man SSH und Git in der Konfigurationsverwaltung effizient einsetzt

Deutsch
Francais
Nederlands
Svenska
Norsk
Dansk
Suomi
Espanol
Italiano
Portugues
Magyar
Polski
Cestina
Русский