Dieser Artikel wurde von Vildan Softic überprüft. Vielen Dank an alle Peer -Rezensenten bei SitePoint für die perfekte Erstellung von SitePoint -Inhalten!
Ich bin die Art von Entwickler, die gerne von vorne anfängt und versteht, wie alles funktioniert. Ich weiß, dass dies mir eine (unnötige) Arbeitsbelastung bringt, aber es hilft mir, die Mechanismen hinter einem bestimmten Rahmen, einer Bibliothek oder einem bestimmten Modul zu schätzen und zu verstehen.
In letzter Zeit habe ich diesen Moment wieder erlebt und mit der Entwicklung einer Webanwendung mit Redux und reinem JavaScript begonnen. In diesem Artikel möchte ich meine Anwendungsstruktur skizzieren, meine frühen (schließlich fehlgeschlagenen) Iterationen untersuchen und dann die Lösung betrachten, die ich schließlich ausgewählt habe und was ich auf dem Weg gelernt habe.
Sie haben möglicherweise von der Kombination aus populärer React.js und Redux gehört, die die neueste Front-End-Technologie verwendet, um schnelle und leistungsstarke Webanwendungen zu erstellen.
React ist eine von Facebook erstellte Komponenten-basierte Open-Source-Bibliothek zum Erstellen von Benutzeroberflächen. Während React nur eine Ansichtsschicht ist ( ist kein vollständiges Gerüst wie Angular oder Ember ), verwaltet Redux den Zustand der Anwendung. Es fungiert als vorhersehbarer Zustandsbehälter , bei dem der gesamte Zustand in einem einzelnen Objektbaum gespeichert wird und nur durch Ausgabe einer sogenannten -Klüftung geändert werden kann. Wenn Sie dieses Thema überhaupt nicht verstehen, schlage ich vor, dass Sie diesen Artikel lesen.
Für den Rest dieses Artikels müssen Sie kein Redux -Experte sein, aber zumindest wird es hilfreich sein, sein Konzept ein gewisses Verständnis zu haben.
Die fragliche Anwendung ist ein mobiler Tetris-Klon mit verschiedenen Ansichten. Die tatsächliche Spiellogik erfolgt in Redux, während die Offline -Funktionalität von LocalStorage und Custom View -Verarbeitung bereitgestellt wird. Das Repository ist auf Github zu finden, obwohl die App noch unter aktiver Entwicklung steht und ich diesen Artikel während der Entwicklung geschrieben habe.
Ich habe beschlossen, die Ordnerstruktur zu übernehmen, die bei Redux- und React -Projekten üblich ist. Dies ist eine logische Struktur, die für viele verschiedene Einstellungen funktioniert. Es gibt viele Variationen dieses Themas, die meisten Projekte sind etwas unterschiedlich, aber die Gesamtstruktur ist gleich.
src/scripts/
<code>actions/ ├── game.js ├── score.js └── ... components/ ├── router.js ├── pageControls.js ├── canvas.js └── ... constants/ ├── game.js ├── score.js └── ... reducers/ ├── game.js ├── score.js └── ... store/ ├── configureStore.js ├── connect.js └── index.js utils/ ├── serviceWorker.js ├── localStorage.js ├── dom.js └── ... index.js worker.js</code>
Meine Tags werden in ein anderes Verzeichnis unterteilt und werden von einer einzelnen Index.html -Datei wiedergegeben. Diese Struktur ähnelt den Skripten/ um eine konsistente Architektur in der gesamten Codebasis aufrechtzuerhalten.
src/markup/
<code>layouts/ └── default.html partials/ ├── back-button.html └── meta.html pages/ ├── about.html ├── settings.html └── ... index.html</code>
Um auf den Speicher zuzugreifen, müssen Sie ihn einmal erstellen und an alle Instanzen der Anwendung übergeben. Die meisten Frameworks verwenden eine Art Abhängigkeitsinjektionsbehälter, sodass wir als Framework -Benutzer keine eigenen Lösungen finden müssen. Aber wenn ich meine eigene Lösung verwende, wie kann ich es für alle meine Komponenten zur Verfügung stellen?
Meine erste Iteration ist fehlgeschlagen. Ich weiß nicht, warum ich denke, dass dies eine gute Idee ist, aber ich stelle den Speicher in ein eigenes Modul (Skripte/Store/Index.js) ein, das dann vom Rest der Anwendung importiert werden kann. Am Ende habe ich es bereut und habe mich schnell mit kreisförmigen Abhängigkeiten befasst. Das Problem ist, dass der Speicher nicht korrekt initialisiert wird, wenn die Komponente versucht, auf den Speicher zuzugreifen. Ich habe ein Diagramm erstellt, um den Abhängigkeitsfluss zu demonstrieren, an dem ich arbeite:
Einstiegspunkt für die Anwendung ist die Initialisierung aller Komponenten und dann die Verwendung des Speichers entweder direkt oder über Helferfunktionen (hier als Connect bezeichnet). Da der Speicher jedoch nicht explizit erstellt wird, sondern nur ein Nebeneffekt in seinem eigenen Modul ist, verwendet die Komponente den Speicher, bevor er erstellt wird. Es gibt keine Kontrolle über die Zeit, in der eine Komponente oder eine Helferfunktion zum ersten Mal zum Speichern gerufen wird. Das ist sehr verwirrend.
Speichermodul lautet wie folgt:
scripts/store/index.js (☓ schlecht)
import { createStore } from 'redux' import reducers from '../reducers' const store = createStore(reducers) export default store export { getItemList } from './connect'
Wie oben erwähnt, wird der Speicher als Nebeneffekt erstellt und dann exportiert. Helferfunktionen müssen ebenfalls gespeichert werden.
Skripte/speichern/connect.js (☓ schlecht)
import store from './' export function getItemList () { return store.getState().items.all }
Dies ist genau der Moment, in dem sich meine Komponenten gegenseitig wiederholen. Helferfunktionen erfordern den Speicher ausgeführt und werden gleichzeitig aus der Speicherinitialisierungsdatei exportiert, damit sie auf andere Teile der Anwendung zugreifen können. Sehen Sie, wie chaotisch das klingt?
Es scheint jetzt offensichtlich, und ich habe eine Weile gebraucht, um zu verstehen. Ich habe dies gelöst, indem ich die Initialisierung auf meinen Anwendungseinstiegspunkt (Skripte/Index.js) verlagert und an alle erforderlichen Komponenten weitergegeben habe.
Dies ist erneut sehr ähnlich wie React tatsächlich zugänglich macht (siehe Quellcode). Es gibt einen Grund, warum sie so gut zusammenarbeiten. Warum nicht sein Konzept lernen?
Anwendungseinstiegspunkt erstellt zuerst den Speicher und übergibt ihn dann an alle Komponenten. Die Komponente kann dann eine Verbindung zum Speicher- und Zeitplanvorgang herstellen, Änderungen abonnieren oder bestimmte Daten abrufen. Schauen wir uns die Änderungen an:
scripts/store/configureStore.js
(✓ Gut)Ich habe das Modul aufbewahrt, sondern eine Funktion namens configurestore exportiert, die Speicher an anderer Stelle in der Codebasis erstellt.
<code>actions/ ├── game.js ├── score.js └── ... components/ ├── router.js ├── pageControls.js ├── canvas.js └── ... constants/ ├── game.js ├── score.js └── ... reducers/ ├── game.js ├── score.js └── ... store/ ├── configureStore.js ├── connect.js └── index.js utils/ ├── serviceWorker.js ├── localStorage.js ├── dom.js └── ... index.js worker.js</code>
scripts/speichern/connect.js
(✓ Gut)Helferfunktion verbinden sich im Wesentlichen nicht, aber jetzt muss der Speicher als Parameter übergeben werden. Zuerst zögerte ich, diese Lösung zu verwenden, weil ich dachte,
<code>layouts/ └── default.html partials/ ├── back-button.html └── meta.html pages/ ├── about.html ├── settings.html └── ... index.html</code>
scripts/index.js
Dies ist der Anwendungseinstiegspunkt. Das Geschäft wird erstellt und an alle Komponenten übergeben. PageControls fügt einen globalen Ereignis -Listener für bestimmte Aktionsschaltflächen hinzu, und Tetrisgame ist die tatsächliche Spielkomponente. Es sieht grundsätzlich gleich aus, bevor der Speicher hier verschoben wird, gibt den Speicher jedoch nicht getrennt auf alle Module weiter. Wie bereits erwähnt, kann die Komponente über meine fehlgeschlagene Verbindungsmethode auf den Speicher zugreifen.
import { createStore } from 'redux' import reducers from '../reducers' const store = createStore(reducers) export default store export { getItemList } from './connect'
Komponenten
Containerkomponente . Präsentationskomponenten tun nur reine DOM -Verarbeitung. Andererseits kann die Containerkomponente Aktionen planen oder Änderungen abonnieren. Dan Abramov hat einen großartigen Artikel für React -Komponenten geschrieben, diese Methoden können jedoch auch auf jede andere Komponentenarchitektur angewendet werden.
Es gibt jedoch Ausnahmen für mich. Manchmal sind die Komponenten sehr klein und machen nur eine Sache. Ich möchte sie nicht in eines der oben genannten Muster aufteilen, also habe ich beschlossen, sie zu mischen. Wenn die Komponente wächst und mehr Logik bekommt, werde ich sie trennen.scripts/components/pageControls.js
Das obige Beispiel ist eine der Komponenten. Es enthält eine Liste von Elementen (in diesem Fall alle Elemente mit Daten-Handlungsattributen) und plant Aktionen, wenn sie basierend auf dem Attributinhalt klicken. Das ist alles. Andere Module können dann auf Änderungen im Speicher hören und sich entsprechend aktualisieren. Wie bereits erwähnt, werde ich es trennen, wenn die Komponente auch ein DOM -Update hat.
import store from './' export function getItemList () { return store.getState().items.all }
Aktualisieren Sie DOM
Ich denke tatsächlich darüber nach, dasselbe zu tun. Wenn meine Anwendung größer wird und DOM mühsamer ist, könnte ich zu einem virtuellen DOM wechseln, aber im Moment mache ich klassische DOM -Operation, welche Funktioniert gut mit Redux.
Der Grundprozess ist wie folgt:
Hinweis: Ich bin ein Fan des $ -Symbol -Präfixes für alles, was mit DOM in JavaScript zu tun hat. Wie Sie vielleicht erraten haben, stammt es von JQuery's $. Daher wird der Name der reinen Präsentationskomponenten mit dem Dollar -Zeichen vorangestellt.
scripts/index.js
<code>actions/ ├── game.js ├── score.js └── ... components/ ├── router.js ├── pageControls.js ├── canvas.js └── ... constants/ ├── game.js ├── score.js └── ... reducers/ ├── game.js ├── score.js └── ... store/ ├── configureStore.js ├── connect.js └── index.js utils/ ├── serviceWorker.js ├── localStorage.js ├── dom.js └── ... index.js worker.js</code>
Nichts Besonderes hier. Importieren, erstellen und initialisieren Sie Containerkomponenten ScoreObserver. Was genau macht es? Es aktualisiert alle Ansichtselemente im Zusammenhang mit Bewertungen: High -Score -Liste und aktuelle Score -Informationen während des Spiels.
Skripte/Komponenten/ScoreObserver/index.js
<code>layouts/ └── default.html partials/ ├── back-button.html └── meta.html pages/ ├── about.html ├── settings.html └── ... index.html</code>
Denken Sie daran, dass dies eine einfache Komponente ist. Was ist hier los? Die ScoreObServer-Komponente spart interne Verweise auf den Speicher und erstellt zwei Komponenten auf Präsentationsebene der neuen Instanz für die spätere Verwendung. Die Init -Methode zeichnet sich bei den Speicheraktualisierungen an und aktualisiert die $ -T -Label -Komponente jedes Mal, wenn sich der Speicher ändert - jedoch nur, wenn das Spiel tatsächlich ausgeführt wird.
updatesCoreboard -Methode wird an anderer Stelle verwendet. Es ist nicht sinnvoll, die Liste jedes Mal zu aktualisieren, wenn eine Änderung eintritt, da die Ansicht ohnehin inaktiv ist. Es gibt auch eine Routing -Komponente, die jedes Mal, wenn sich die Ansicht ändert, eine andere Komponente aktualisiert oder deaktiviert. Seine API ist ungefähr wie folgt:
import { createStore } from 'redux' import reducers from '../reducers' const store = createStore(reducers) export default store export { getItemList } from './connect'
Hinweis: $ (und $$) ist keine JQuery -Referenz, sondern eine bequeme Versorgungsverknüpfung zu document.querySelector.
Skripte/Komponenten/ScoreObserver/$ board.js
import store from './' export function getItemList () { return store.getState().items.all }
Dies ist wieder ein grundlegendes Beispiel und eine grundlegende Komponente. Die updateboard () -Methode nimmt ein Array an, iteriert es und fügt den Inhalt in die Punktzahlliste ein.
Skripte/Komponenten/ScoreObserver/$ label.js
import { createStore } from 'redux' import reducers from '../reducers' export default function configureStore () { return createStore(reducers) }
Diese Komponente entspricht fast genau der obigen Anzeigetafel, aber nur ein einzelnes Element aktualisiert.
Ein weiterer wichtiger Punkt ist die Implementierung von Anwendungsfall-gesteuerten Speicher. Ich denke, es ist wichtig, nur Inhalte zu speichern, die für die Anwendung unerlässlich sind. Zu Beginn habe ich fast alles gespeichert: die aktuelle aktive Ansicht, Spieleinstellungen, Punktzahlen, Schwebebereicheffekte, Benutzernatmodus usw.
Während dies mit einer Anwendung zusammenhängt, hat es nichts mit einer anderen zu tun. Es mag schön sein, die aktuelle Ansicht zu speichern und beim Nachladen genau an dem gleichen Ort fortzufahren, aber in meinem Fall fühlte es sich wie eine schlechte Benutzererfahrung an und ärgerlicher als nützlich. Sie möchten keine Menüs oder modalen Schalter speichern, oder? Warum müssen Benutzer in diesen bestimmten Zustand zurückkehren? In größeren Webanwendungen kann dies sinnvoll sein. Aber in meinem kleinen Handy -Focus -Spiel kehren Sie zum Bildschirm der Einstellungen zurück, nur weil ich von dort aus gegangen bin, was ziemlich ärgerlich ist.
Ich habe mein Redux -Projekt mit und ohne Reaktion durchgeführt, und mein Hauptaufbau ist, dass der enorme Unterschied im Anwendungsdesign nicht erforderlich ist. Die meisten in React verwendeten Methoden können tatsächlich alle anderen Ansichtsverarbeitungseinstellungen anpassen. Es dauerte eine Weile, bis ich das erkannte, weil ich dachte, ich musste etwas anderes machen, aber am Ende stellte ich fest, dass es nicht notwendig war. , was unterschiedlich ist, ist, wie Sie Module initialisieren, wie Sie sie speichern und wie gut die Komponenten den gesamten Anwendungszustand verstehen. Das Konzept bleibt gleich, aber die Implementierung und das Codevolumen sind perfekt für Ihre Anforderungen.
Redux ist ein großartiges Tool, mit dem Ihre Anwendung auf nachdenklicher erstellt wird. Wenn Sie es allein ohne Ansichtsgalerie verwenden, kann es zunächst sehr schwierig sein, aber wenn Sie die anfängliche Verwirrung überwunden haben, kann Sie nichts aufhalten. Was denkst du über meine Methode? Verwenden Sie Redux und verschiedene Ansichten, um Einstellungen allein zu behandeln? Ich würde gerne von Ihnen hören und es in den Kommentaren besprechen.
Wenn Sie mehr über Redux erfahren möchten, lesen Sie den Mini -Kurs "Umschreiben und Testen von Redux, um Designprobleme zu lösen". In diesem Kurs erstellen Sie eine Redux -Anwendung, die Tweets erhält, die nach Thema über eine WebSocket -Verbindung organisiert werden. Um Ihnen eine Vorstellung davon zu geben, was passieren wird, sehen Sie sich den kostenlosen Kurs unten an.
Laden des Players… FAQ auf reag-freier Redux (FAQ) Was ist der Hauptunterschied zwischen der Verwendung von Redux und React und nicht der Verwendung von React?
asynchrone Operationen in Redux werden normalerweise mit Middleware wie Redux Thunk oder Redux -Saga behandelt. Mit dieser Middleware können Sie Funktionen (Thunks) oder komplexere asynchrone Operationen (SAGAS) und nicht gewöhnliche Objekte planen. Auch ohne Reaktion können Sie diese Middleware im Redux -Speicher verwenden. Sie müssen nur Middleware anwenden, wenn Sie den Speicher über die Funktion von Reduxs anwenden.
Ja, Redux Devtools hängt nicht von React ab und kann mit jeder UI -Schicht verwendet werden, die Redux verwendet. Sie können Redux Devtools in Ihre Anwendung integrieren, indem Sie sie beim Erstellen von Redux -Speicher als Middleware hinzufügen. Auf diese Weise können Sie den Status und die Aktionen Ihrer Anwendung in Echtzeit prüfen, auch ohne Reagieren.
Ohne React und seine Verbindungsfunktion müssen Sie den Redux -Speicher manuell abonnieren und die UI -Komponenten aktualisieren, wenn sich der Status ändert. Sie können den Store mit der Store.Subscribe -Methode abonnieren, die eine Hörerfunktion annimmt, die jedes Mal aufgerufen wird, wenn der Vorgang geplant ist. In dieser Listener -Funktion können Sie Store.getState verwenden, um den aktuellen Status des Speichers zu erhalten und die UI -Komponenten entsprechend zu aktualisieren.
Ja, Redux stützt sich nicht auf React und kann mit jeder UI -Schicht verwendet werden. Für andere Bibliotheken und Frameworks wie Vue und Angular werden Bindungen bereitgestellt, die eine ähnliche Funktionalität wie die Connect -Funktion von React bieten. Mit diesen Bindungen können Sie UI -Komponenten einfach mit Redux -Speicher anschließen und Aktualisierungen von Komponenten verarbeiten, wenn sich der Status ändert.
Testen von Redux -Code ohne React ähnelt dem Testen mit React. Sie können Unit -Tests für Ihre Aktionsersteller und Reduzierer erstellen, indem Sie ein JavaScript -Test -Framework wie Scherz oder Mokka verwenden. Zum Testen asynchroner Vorgänge können Sie einen Mock -Speicher verwenden, um Redux -Speicher zu simulieren.
Nebenwirkungen in Redux werden normalerweise mit Middleware wie Redux Thunk oder Redux -Saga behandelt. Mit dieser Middleware können Sie Funktionen mit Nebenwirkungen oder komplexeren asynchronen Operationen planen, z. B. API -Anrufe. Auch ohne Reaktion können Sie diese Middleware im Redux -Speicher verwenden.
Ja, Redux kann mit reinem JavaScript verwendet werden. Sie können einen Redux -Store erstellen, Aktionen planen und Änderungen im Zustand mit nur reinem JavaScript abonnieren. Wenn es jedoch keine Bibliothek oder Framework gibt, wie Reaktionen, um Updates für die Benutzeroberfläche zu verarbeiten, müssen Sie die UI -Komponenten manuell aktualisieren, wenn sich der Status ändert.
Die Struktur des Redux -Codes hängt nicht davon ab, ob Sie React verwenden. Sie können weiterhin die gleichen Best Practices zum Erstellen von Redux -Code befolgen, z.
Ja, Redux Middleware stützt sich nicht auf React und kann mit jeder UI -Ebene verwendet werden, die Redux verwendet. Middleware in Redux wird verwendet, um Nebenwirkungen und asynchrone Vorgänge zu verarbeiten, usw. Sie können die Middleware von Redux anwenden, die über die Anwendung von Reduxs anwenden, unabhängig davon, ob Sie React verwenden oder nicht.
Das obige ist der detaillierte Inhalt vonRedux ohne Reaktion. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!