Reaktivitätsmodelle erklärt
Vorwort
Es ist (schon) 10 Jahre her, seit ich mit der Entwicklung von Anwendungen und Websites begonnen habe, aber das JavaScript-Ökosystem war noch nie so spannend wie heute!
Im Jahr 2022 war die Community vom Konzept „Signal“ so fasziniert, dass die meisten JavaScript-Frameworks sie in ihre eigene Engine integrierten. Ich denke an Preact, das seit September 2022 vom Komponentenlebenszyklus entkoppelte reaktive Variablen anbietet; oder in jüngerer Zeit Angular, das Signale im Mai 2023 experimentell implementierte, dann offiziell ab Version 18. Auch andere JavaScript-Bibliotheken haben beschlossen, ihren Ansatz zu überdenken...
Von 2023 bis heute habe ich Signale konsequent in verschiedenen Projekten verwendet. Ihre einfache Implementierung und Nutzung hat mich voll und ganz überzeugt, sodass ich ihre Vorteile in technischen Workshops, Schulungen und Konferenzen mit meinem beruflichen Netzwerk geteilt habe.
Aber in letzter Zeit begann ich mich zu fragen, ob dieses Konzept wirklich „revolutionär“ war/ob es Alternativen zu Signals gibt? Also habe ich mich eingehender mit dieser Überlegung befasst und verschiedene Ansätze für reaktive Systeme entdeckt.
Dieser Beitrag gibt einen Überblick über verschiedene Reaktivitätsmodelle und mein Verständnis ihrer Funktionsweise.
Hinweis: An dieser Stelle, Sie haben es wahrscheinlich erraten, werde ich nicht über Javas „Reaktive Streams“ sprechen; andernfalls hätte ich diesen Beitrag mit „WTF ist Gegendruck!?“ betitelt. ?
Theorie
Wenn wir über Reaktivitätsmodelle sprechen, sprechen wir (in erster Linie) von „reaktiver Programmierung“, insbesondere aber von „Reaktivität“.
Die reaktive Programmierung ist ein Entwicklungsparadigma, das es ermöglicht, die Änderung einer Datenquelle automatisch an Verbraucher weiterzugeben.
Also können wir die Reaktivität als die Fähigkeit definieren, Abhängigkeiten in Echtzeit abhängig von der Datenänderung zu aktualisieren.
Hinweis: Kurz gesagt, wenn ein Benutzer ein Formular ausfüllt und/oder absendet, müssen wir auf diese Änderungen reagieren, eine Ladekomponente oder irgendetwas anderes anzeigen, das angibt, dass etwas passiert. .. Ein weiteres Beispiel: Beim asynchronen Empfang von Daten müssen wir reagieren, indem wir alle oder einen Teil dieser Daten anzeigen, eine neue Aktion ausführen usw.
In diesem Zusammenhang stellen reaktive Bibliotheken Variablen bereit, die automatisch aktualisiert und effizient weitergegeben werden, wodurch es einfacher wird, einfachen und optimierten Code zu schreiben.
Um effizient zu sein, müssen diese Systeme diese Variablen genau dann neu berechnen/auswerten, wenn sich ihre Werte geändert haben! Um sicherzustellen, dass die übertragenen Daten konsistent und aktuell bleiben, muss das System die Anzeige von Zwischenzuständen vermeiden (insbesondere während der Berechnung von Zustandsänderungen).
Hinweis: Der Status bezieht sich auf die Daten/Werte, die während der gesamten Lebensdauer eines Programms/einer Anwendung verwendet werden.
Okay, aber dann... Was genau sind diese „Reaktivitätsmodelle“?
PUSH, auch bekannt als „Eager“ Reaktivität
Das erste Reaktivitätsmodell heißt „PUSH“ (oder „eifrige“ Reaktivität). Dieses System basiert auf den folgenden Prinzipien:
- Initialisierung von Datenquellen (bekannt als „Observables“)
- Komponenten/Funktionen abonnieren diese Datenquellen (das sind die Verbraucher)
- Wenn sich ein Wert ändert, werden die Daten sofort an die Verbraucher (sogenannte „Beobachter“) weitergegeben
Wie Sie vielleicht schon erraten haben, basiert das „PUSH“-Modell auf dem Designmuster „Observable/Observer“.
1. Anwendungsfall: Anfangszustand und Zustandsänderung
Betrachten wir den folgenden Ausgangszustand
let a = { firstName: "John", lastName: "Doe" }; const b = a.firstName; const c = a.lastName; const d = `${b} ${c}`;
Bei Verwendung einer reaktiven Bibliothek (wie RxJS) würde dieser Anfangszustand eher so aussehen:
let a = observable.of({ firstName: "John", lastName: "Doe" }); const b = a.pipe(map((a) => a.firstName)); const c = a.pipe(map((a) => a.lastName)); const d = merge(b, c).pipe(reduce((b, c) => `${b} ${c}`));
Hinweis: Für diesen Beitrag sollten alle Codeschnipsel als „Pseudocode“ betrachtet werden.
Nehmen wir nun an, dass ein Verbraucher (zum Beispiel eine Komponente) den Wert von Zustand D protokollieren möchte, wann immer diese Datenquelle aktualisiert wird,
d.subscribe((value) => console.log(value));
Unsere Komponente würde den Datenstrom abonnieren; es muss noch eine Veränderung auslösen,
a.next({ firstName: "Jane", lastName: "Doe" });
Von dort aus erkennt das „PUSH“-System die Änderung und sendet sie automatisch an die Verbraucher. Basierend auf dem oben genannten Ausgangszustand finden Sie hier eine Beschreibung der Vorgänge, die auftreten können:
- Zustandsänderung tritt in Datenquelle A auf!
- Der Wert von A wird an B weitergegeben (Berechnung der Datenquelle B);
- Dann wird der Wert von B an D weitergegeben (Berechnung der Datenquelle D);
- Der Wert von A wird an C weitergegeben (Berechnung der Datenquelle C);
- Schließlich wird der Wert von C an D weitergegeben (Neuberechnung der Datenquelle D);
Eine der Herausforderungen dieses Systems liegt in der Reihenfolge der Berechnungen. Basierend auf unserem Anwendungsfall werden Sie tatsächlich feststellen, dass D möglicherweise zweimal ausgewertet wird: ein erstes Mal mit dem Wert von C in seinem vorherigen Zustand; und ein zweites Mal mit dem aktuellen C-Wert! In dieser Art von Reaktivitätsmodell wird diese Herausforderung als „Diamantproblem“ ♦️ bezeichnet.
2. Anwendungsfall: Nächste Iteration
Nehmen wir nun an, dass sich der Staat auf zwei Hauptdatenquellen verlässt,
let a = { firstName: "John", lastName: "Doe" }; const b = a.firstName; const c = a.lastName; const d = `${b} ${c}`;
Beim Aktualisieren von E berechnet das System den gesamten Status neu, wodurch eine einzige Quelle der Wahrheit erhalten bleibt, indem der vorherige Status überschrieben wird.
- Zustandsänderung tritt in Datenquelle E auf!
- Der Wert von A wird an B weitergegeben (Berechnung der Datenquelle B);
- Dann wird der Wert von B an D weitergegeben (Berechnung der Datenquelle D);
- Der Wert von A wird an C weitergegeben (Berechnung der Datenquelle C);
- Der Wert von E wird an C weitergegeben (Neuberechnung der Datenquelle C);.
- Schließlich wird der Wert von C an D weitergegeben (Neuberechnung der Datenquelle D);
Wieder einmal tritt das „Diamantproblem“ auf... Dieses Mal auf der Datenquelle C, die möglicherweise zweimal ausgewertet wird, und zwar immer auf D.
Diamantproblem
Das „Diamantenproblem“ ist keine neue Herausforderung im „eifrigen“ Reaktivitätsmodell. Einige Berechnungsalgorithmen (insbesondere die von MobX verwendeten) können die „Knoten des reaktiven Abhängigkeitsbaums“ markieren, um die Zustandsberechnung auszugleichen. Bei diesem Ansatz würde das System zuerst die „Root“-Datenquellen (A und E in unserem Beispiel), dann B und C und schließlich D auswerten. Eine Änderung der Reihenfolge der Zustandsberechnungen hilft, diese Art von Problem zu beheben.
PULL, auch bekannt als „faule“ Reaktivität
Das zweite Reaktivitätsmodell heißt "PULL". Im Gegensatz zum „PUSH“-Modell basiert es auf den folgenden Prinzipien:
- Deklaration reaktiver Variablen
- Das System verschiebt die Zustandsberechnung
- Der abgeleitete Status wird basierend auf seinen Abhängigkeiten berechnet
- Das System vermeidet übermäßige Aktualisierungen
Diese letzte Regel ist am wichtigsten, die Sie sich merken sollten: Im Gegensatz zum vorherigen System verschiebt diese letzte Regel die Zustandsberechnung, um mehrere Auswertungen derselben Datenquelle zu vermeiden.
1. Anwendungsfall: Anfangszustand und Zustandsänderung
Behalten wir den vorherigen Ausgangszustand bei...
In einem solchen System hätte die Anfangszustandssyntax die folgende Form:
let a = observable.of({ firstName: "John", lastName: "Doe" }); const b = a.pipe(map((a) => a.firstName)); const c = a.pipe(map((a) => a.lastName)); const d = merge(b, c).pipe(reduce((b, c) => `${b} ${c}`));
Hinweis:React-Enthusiasten werden diese Syntax wahrscheinlich erkennen ?
Durch die Deklaration einer reaktiven Variablen entsteht ein Tupel: unveränderliche Variable auf der einen Seite; Aktualisierungsfunktion dieser Variablen andererseits. Die übrigen Anweisungen (in unserem Fall B, C und D) werden als abgeleitete Zustände betrachtet, da sie auf ihre jeweiligen Abhängigkeiten „lauschen“.
d.subscribe((value) => console.log(value));
Das entscheidende Merkmal eines „faulen“ Systems ist, dass es Änderungen nicht sofort weitergibt, sondern nur, wenn dies ausdrücklich angefordert wird.
let a = { firstName: "John", lastName: "Doe" }; const b = a.firstName; const c = a.lastName; const d = `${b} ${c}`;
In einem „PULL“-Modell löst die Verwendung eines effect() (von einer Komponente) zum Protokollieren des Werts einer reaktiven Variablen (angegeben als Abhängigkeit) die Berechnung der Zustandsänderung aus:
- D prüft, ob seine Abhängigkeiten (B und C) aktualisiert wurden;
- B prüft, ob seine Abhängigkeit (A) aktualisiert wurde;
- A gibt seinen Wert an B weiter (wobei der Wert von B berechnet wird);
- C prüft, ob seine Abhängigkeit (A) aktualisiert wurde;
- A gibt seinen Wert an C weiter (wobei der Wert von C berechnet wird)
- B und C geben ihren jeweiligen Wert an D weiter (wobei der Wert von D berechnet wird);
Eine Optimierung dieses Systems ist bei der Abfrage von Abhängigkeiten möglich. Tatsächlich wird A im obigen Szenario zweimal abgefragt, um festzustellen, ob es aktualisiert wurde. Die erste Abfrage könnte jedoch ausreichen, um festzustellen, ob sich der Status geändert hat. C müsste diese Aktion nicht ausführen... Stattdessen könnte A nur seinen Wert senden.
2. Anwendungsfall: Nächste Iteration
Komplizieren wir den Zustand etwas, indem wir eine zweite reaktive Variable „root“ hinzufügen.
let a = observable.of({ firstName: "John", lastName: "Doe" }); const b = a.pipe(map((a) => a.firstName)); const c = a.pipe(map((a) => a.lastName)); const d = merge(b, c).pipe(reduce((b, c) => `${b} ${c}`));
Noch einmal verschiebt das System die Zustandsberechnung, bis sie explizit angefordert wird. Mit dem gleichen Effekt wie zuvor löst das Aktualisieren einer neuen reaktiven Variablen die folgenden Schritte aus:
- D prüft, ob seine Abhängigkeiten (B und C) aktualisiert wurden ;
- B prüft, ob seine Abhängigkeit (A) aktualisiert wurde ;
- C prüft, ob seine Abhängigkeiten (A und E) aktualisiert wurden ;
- E gibt seinen Wert an C weiter und C ruft den Wert von A durch Memoisierung ab (Berechnung des Werts von C) ;
- C gibt seinen Wert an D weiter und D ruft den Wert von B durch Memoisierung ab (Berechnung des Werts von D) ;
Da sich der Wert von A nicht geändert hat, ist eine Neuberechnung dieser Variablen nicht erforderlich (dasselbe gilt für den Wert von B). In solchen Fällen verbessert die Verwendung von Memoisierungsalgorithmen die Leistung während der Zustandsberechnung.
PUSH-PULL, auch bekannt als „feinkörnige“ Reaktivität
Das letzte Reaktivitätsmodell ist das „PUSH-PULL“-System. Der Begriff „PUSH“ spiegelt die sofortige Weitergabe von Änderungsbenachrichtigungen wider, während „PULL“ sich auf das Abrufen der Statuswerte bei Bedarf bezieht. Dieser Ansatz steht in engem Zusammenhang mit der sogenannten „feinkörnigen“ Reaktivität, die den folgenden Prinzipien folgt:
- Deklaration reaktiver Variablen (wir sprechen von reaktiven Grundelementen)
- Abhängigkeiten werden auf atomarer Ebene verfolgt
- Die Verbreitung von Veränderungen erfolgt sehr zielgerichtet
Beachten Sie, dass diese Art der Reaktivität nicht nur beim „PUSH-PULL“-Modell auftritt. Unter feinkörniger Reaktivität versteht man die präzise Verfolgung von Systemabhängigkeiten. Es gibt also PUSH und PULL Reaktivitätsmodelle, die ebenfalls auf diese Weise funktionieren (ich denke an Jotai oder Recoil.
).1. Anwendungsfall: Anfangszustand und Zustandsänderung
Immer noch basierend auf dem vorherigen Anfangszustand... Die Deklaration eines Anfangszustandes in einem „feinkörnigen“ Reaktivitätssystem würde wie folgt aussehen:
let a = { firstName: "John", lastName: "Doe" }; const b = a.firstName; const c = a.lastName; const d = `${b} ${c}`;
Hinweis: Die Verwendung des Signalschlüsselworts ist hier nicht nur anekdotisch ?
In Bezug auf die Syntax ist es dem „PUSH“-Modell sehr ähnlich, es gibt jedoch einen bemerkenswerten und wichtigen Unterschied: Abhängigkeiten! In einem „feinkörnigen“ Reaktivitätssystem ist es nicht notwendig, die zur Berechnung eines abgeleiteten Zustands erforderlichen Abhängigkeiten explizit zu deklarieren, da diese Zustände implizit die von ihnen verwendeten Variablen verfolgen. In unserem Fall verfolgen B und C automatisch Änderungen am Wert von A und D verfolgt Änderungen sowohl an B als auch an C.
let a = observable.of({ firstName: "John", lastName: "Doe" }); const b = a.pipe(map((a) => a.firstName)); const c = a.pipe(map((a) => a.lastName)); const d = merge(b, c).pipe(reduce((b, c) => `${b} ${c}`));
In einem solchen System ist die Aktualisierung einer reaktiven Variablen effizienter als in einem einfachen „PUSH“-Modell, da die Änderung automatisch an die davon abhängigen abgeleiteten Variablen weitergegeben wird (nur als Benachrichtigung, nicht). der Wert selbst).
d.subscribe((value) => console.log(value));
Dann werden bei Bedarf (nehmen wir das Beispiel Logger) durch die Verwendung von D innerhalb des Systems die Werte der zugehörigen Wurzelzustände (in unserem Fall A) abgerufen und die Werte berechnet der abgeleiteten Zustände (B und C) und schließlich D auswerten. Ist das nicht eine intuitive Funktionsweise?
2. Anwendungsfall: Nächste Iteration
Betrachten wir den folgenden Zustand:
a.next({ firstName: "Jane", lastName: "Doe" });
Auch hier ermöglicht der „feinkörnige“ Aspekt des PUSH-PULL-Systems die automatische Verfolgung jedes Zustands. Der abgeleitete Zustand C verfolgt nun die Wurzelzustände A und E. Durch das Aktualisieren der Variablen E werden die folgenden Aktionen ausgelöst:
- Zustandsänderung des reaktiven Primitivs E!
- Gezielte Änderungsbenachrichtigung (E nach D über C);
- E gibt seinen Wert an C weiter und C ruft den Wert von A durch Memoisierung ab (Berechnung des Werts von C);
- C gibt seinen Wert an D weiter und D ruft den Wert von B durch Memoisierung ab (Berechnung des Werts von D);
Dies ist die vorherige Verknüpfung reaktiver Abhängigkeiten miteinander, die dieses Modell so effizient macht!
Tatsächlich wird das Framework in einem klassischen „PULL“-System (wie z. B. dem Virtual DOM von React) beim Aktualisieren eines reaktiven Status von einer Komponente über die Änderung benachrichtigt (wodurch ein „ diffing"-Phase). Dann berechnet das Framework bei Bedarf (und verzögert) die Änderungen, indem es den reaktiven Abhängigkeitsbaum durchläuft. jedes Mal, wenn eine Variable aktualisiert wird! Diese „Entdeckung“ des Zustands der Abhängigkeiten ist mit erheblichen Kosten verbunden...
Bei einem „feinkörnigen“ Reaktivitätssystem (wie Signalen) benachrichtigt die Aktualisierung reaktiver Variablen/Primitive automatisch jeden damit verknüpften abgeleiteten Zustand über die Änderung. Daher besteht keine Notwendigkeit, die damit verbundenen Abhängigkeiten (wieder) zu entdecken; Die Staatsverbreitung ist gezielt!
Fazit(.value)
Im Jahr 2024 haben sich die meisten Web-Frameworks entschieden, ihre Funktionsweise zu überdenken, insbesondere im Hinblick auf ihr Reaktivitätsmodell. Dieser Wandel hat sie im Allgemeinen effizienter und wettbewerbsfähiger gemacht. Andere entscheiden sich dafür, (noch) hybrid zu sein (ich denke hier an Vue), was sie in vielen Situationen flexibler macht.
Abschließend, unabhängig vom gewählten Modell, basiert meiner Meinung nach ein (gutes) reaktives System auf ein paar Hauptregeln:
- Das System verhindert inkonsistente abgeleitete Zustände;
- Die Verwendung eines Zustands innerhalb des Systems führt zu einem reaktiv abgeleiteten Zustand;
- Das System minimiert übermäßige Arbeit ;
- Und: „Für einen gegebenen anfänglichen Zustand wird das Endergebnis des Systems immer dasselbe sein, ganz gleich, welchem Weg der Zustand folgt! „
Dieser letzte Punkt, der als grundlegendes Prinzip der deklarativen Programmierung interpretiert werden kann, ist, dass ein (gutes) reaktives System meiner Meinung nach deterministisch sein muss! Dies ist der „Determinismus“, der ein reaktives Modell unabhängig von der Komplexität des Algorithmus zuverlässig, vorhersehbar und einfach in technischen Projekten im großen Maßstab einsetzbar macht.
Das obige ist der detaillierte Inhalt vonWTF ist Reaktivität!?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Zu den Hauptanwendungen von JavaScript in der Webentwicklung gehören die Interaktion der Clients, die Formüberprüfung und die asynchrone Kommunikation. 1) Dynamisches Inhaltsaktualisierung und Benutzerinteraktion durch DOM -Operationen; 2) Die Kundenüberprüfung erfolgt vor dem Einreichung von Daten, um die Benutzererfahrung zu verbessern. 3) Die Aktualisierung der Kommunikation mit dem Server wird durch AJAX -Technologie erreicht.

Es ist für Entwickler wichtig, zu verstehen, wie die JavaScript -Engine intern funktioniert, da sie effizientere Code schreibt und Leistungs Engpässe und Optimierungsstrategien verstehen kann. 1) Der Workflow der Engine umfasst drei Phasen: Parsen, Kompilieren und Ausführung; 2) Während des Ausführungsprozesses führt die Engine dynamische Optimierung durch, wie z. B. Inline -Cache und versteckte Klassen. 3) Zu Best Practices gehören die Vermeidung globaler Variablen, die Optimierung von Schleifen, die Verwendung von const und lass und die Vermeidung übermäßiger Verwendung von Schließungen.

Python eignet sich besser für Anfänger mit einer reibungslosen Lernkurve und einer kurzen Syntax. JavaScript ist für die Front-End-Entwicklung mit einer steilen Lernkurve und einer flexiblen Syntax geeignet. 1. Python-Syntax ist intuitiv und für die Entwicklung von Datenwissenschaften und Back-End-Entwicklung geeignet. 2. JavaScript ist flexibel und in Front-End- und serverseitiger Programmierung weit verbreitet.

Python und JavaScript haben ihre eigenen Vor- und Nachteile in Bezug auf Gemeinschaft, Bibliotheken und Ressourcen. 1) Die Python-Community ist freundlich und für Anfänger geeignet, aber die Front-End-Entwicklungsressourcen sind nicht so reich wie JavaScript. 2) Python ist leistungsstark in Bibliotheken für Datenwissenschaft und maschinelles Lernen, während JavaScript in Bibliotheken und Front-End-Entwicklungsbibliotheken und Frameworks besser ist. 3) Beide haben reichhaltige Lernressourcen, aber Python eignet sich zum Beginn der offiziellen Dokumente, während JavaScript mit Mdnwebdocs besser ist. Die Wahl sollte auf Projektbedürfnissen und persönlichen Interessen beruhen.

Die Verschiebung von C/C zu JavaScript erfordert die Anpassung an dynamische Typisierung, Müllsammlung und asynchrone Programmierung. 1) C/C ist eine statisch typisierte Sprache, die eine manuelle Speicherverwaltung erfordert, während JavaScript dynamisch eingegeben und die Müllsammlung automatisch verarbeitet wird. 2) C/C muss in den Maschinencode kompiliert werden, während JavaScript eine interpretierte Sprache ist. 3) JavaScript führt Konzepte wie Verschlüsse, Prototypketten und Versprechen ein, die die Flexibilität und asynchrone Programmierfunktionen verbessern.

Unterschiedliche JavaScript -Motoren haben unterschiedliche Auswirkungen beim Analysieren und Ausführen von JavaScript -Code, da sich die Implementierungsprinzipien und Optimierungsstrategien jeder Engine unterscheiden. 1. Lexikalanalyse: Quellcode in die lexikalische Einheit umwandeln. 2. Grammatikanalyse: Erzeugen Sie einen abstrakten Syntaxbaum. 3. Optimierung und Kompilierung: Generieren Sie den Maschinencode über den JIT -Compiler. 4. Führen Sie aus: Führen Sie den Maschinencode aus. V8 Engine optimiert durch sofortige Kompilierung und versteckte Klasse.

Zu den Anwendungen von JavaScript in der realen Welt gehören die serverseitige Programmierung, die Entwicklung mobiler Anwendungen und das Internet der Dinge. Die serverseitige Programmierung wird über node.js realisiert, die für die hohe gleichzeitige Anfrageverarbeitung geeignet sind. 2. Die Entwicklung der mobilen Anwendungen erfolgt durch reaktnative und unterstützt die plattformübergreifende Bereitstellung. 3.. Wird für die Steuerung von IoT-Geräten über die Johnny-Five-Bibliothek verwendet, geeignet für Hardware-Interaktion.

Ich habe eine funktionale SaaS-Anwendung mit mehreren Mandanten (eine EdTech-App) mit Ihrem täglichen Tech-Tool erstellt und Sie können dasselbe tun. Was ist eine SaaS-Anwendung mit mehreren Mietern? Mit Multi-Tenant-SaaS-Anwendungen können Sie mehrere Kunden aus einem Sing bedienen


Heiße KI -Werkzeuge

Undresser.AI Undress
KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover
Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Undress AI Tool
Ausziehbilder kostenlos

Clothoff.io
KI-Kleiderentferner

AI Hentai Generator
Erstellen Sie kostenlos Ai Hentai.

Heißer Artikel

Heiße Werkzeuge

Dreamweaver CS6
Visuelle Webentwicklungstools

Herunterladen der Mac-Version des Atom-Editors
Der beliebteste Open-Source-Editor

Senden Sie Studio 13.0.1
Leistungsstarke integrierte PHP-Entwicklungsumgebung

SublimeText3 Mac-Version
Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

DVWA
Damn Vulnerable Web App (DVWA) ist eine PHP/MySQL-Webanwendung, die sehr anfällig ist. Seine Hauptziele bestehen darin, Sicherheitsexperten dabei zu helfen, ihre Fähigkeiten und Tools in einem rechtlichen Umfeld zu testen, Webentwicklern dabei zu helfen, den Prozess der Sicherung von Webanwendungen besser zu verstehen, und Lehrern/Schülern dabei zu helfen, in einer Unterrichtsumgebung Webanwendungen zu lehren/lernen Sicherheit. Das Ziel von DVWA besteht darin, einige der häufigsten Web-Schwachstellen über eine einfache und unkomplizierte Benutzeroberfläche mit unterschiedlichen Schwierigkeitsgraden zu üben. Bitte beachten Sie, dass diese Software