VorwortKontext wird mit Kontext übersetzt. Im Bereich der Programmierung ist dies ein häufig anzutreffendes Konzept, das auch in React existiert. In der offiziellen Dokumentation von React wird Context als „Advanced“ klassifiziert und gehört zur erweiterten API von React, die offizielle Dokumentation empfiehlt jedoch nicht, Context in der stabilen Version der App zu verwenden.
Dies bedeutet jedoch nicht, dass wir dem Kontext keine Aufmerksamkeit schenken müssen. Tatsächlich verwenden viele hervorragende React-Komponenten Context, um ihre Funktionen zu vervollständigen. Beispielsweise stellt <Provider /> von react-redux einen globalen Speicher über Context bereit. Die Drag-Komponente react-dnd verteilt DOM-Drag-and-Drop-Ereignisse in Komponenten über Context. Die Routing-Komponente react-router verwaltet den Routing-Status über Context und so weiter. Wenn Sie Context bei der Entwicklung von React-Komponenten gut einsetzen, können Ihre Komponenten leistungsstark und flexibel werden. Heute möchte ich mit Ihnen darüber sprechen, was ich während der Entwicklung über Kontext gelernt habe und wie ich es zur Entwicklung von Komponenten verwende. Hinweis: Alle in diesem Artikel erwähnten Apps beziehen sich auf Web-Apps. Erster Blick auf React Context Offizielle Definition von Kontext Auf der React-Dokumentationswebsite finden Sie keine Definition von „Was ist Context“, sondern es werden vielmehr die Szenarien beschrieben, in denen Context verwendet wird und wie Context verwendet wird. Die offizielle Website beschreibt das Szenario der Verwendung von Context wie folgt:
Einfach ausgedrückt: Wenn Sie Daten nicht durch die schichtweise Übergabe von Eigenschaften oder Zuständen im Komponentenbaum übergeben möchten, können Sie Context verwenden, um eine ebenenübergreifende Komponentendatenübertragung zu implementieren. Verwenden Sie Requisiten oder Status, um Daten zu übergeben, und die Daten fließen von oben nach unten. Mithilfe von Context können Daten zwischen Komponenten weitergegeben werden. So verwenden Sie ContextDamit Context funktioniert, werden zwei Komponenten benötigt: ein Context-Produzent (Provider), der normalerweise ein übergeordneter Knoten ist, und ein Context-Consumer (Consumer), der normalerweise aus einem oder mehreren untergeordneten Knoten besteht. Daher basiert die Verwendung von Context auf dem Produzenten-Konsumenten-Modell. Für die übergeordnete Komponente, d. h. den Kontextproduzenten, müssen die Eigenschaften des Kontextobjekts deklariert werden, das der untergeordneten Komponente über eine statische Eigenschaft childContextTypes bereitgestellt wird, und eine Instanzmethode getChildContext implementiert werden, um ein einfaches Objekt zurückzugeben, das den Kontext darstellt. React von „react“ importieren Importiere PropTypes aus „Prop-Types“ Klasse MiddleComponent erweitert React.Component { rendern () { return <Unterkomponente /> } } Klasse ParentComponent erweitert React.Component { //Eigenschaften des Kontextobjekts deklarieren static childContextTypes = { propA: PropTypes.string, MethodeA: PropTypes.func } // Gib das Context-Objekt zurück. Der Methodenname ist der vereinbarte getChildContext () { zurückkehren { propA: "propA", MethodeA: () => 'MethodeA' } } rendern () { return <Mittelkomponente /> } } Greifen Sie als Kontextkonsument auf den von der übergeordneten Komponente bereitgestellten Kontext folgendermaßen zu. React von „react“ importieren Importiere PropTypes aus „Prop-Types“ Klasse ChildComponent erweitert React.Component { //Deklarieren Sie die Kontexteigenschaften, die Sie verwenden müssen static contextTypes = { propA: PropTypes.string } rendern () { Konstante { propA, MethodeA } = dieser.Kontext console.log(`context.propA = ${propA}`) // context.propA = propA console.log(`context.methodA = ${methodA}`) // context.methodA = undefiniert zurückkehren ... } } Die untergeordnete Komponente muss über eine statische Eigenschaft contextTypes deklariert werden, bevor sie auf die Eigenschaften des Context-Objekts der übergeordneten Komponente zugreifen kann. Andernfalls ist das erhaltene Objekt undefiniert, auch wenn der Eigenschaftsname nicht falsch geschrieben ist. Bei zustandslosen Unterkomponenten können Sie auf den Kontext der übergeordneten Komponente auf folgende Weise zugreifen: React von „react“ importieren Importiere PropTypes aus „Prop-Types“ const ChildComponent = (Eigenschaften, Kontext) => { Konstante { propA } = Kontext console.log(`context.propA = ${propA}`) // context.propA = propA zurückkehren ... } ChildComponent.contextProps = { propA: PropTypes.string } In der nächsten Version hat React die Context API angepasst, um die Verwendung des Producer-Consumer-Modells klarer zu gestalten. importiere React von „react“; importiere ReactDOM von „react-dom“; const ThemeContext = React.createContext({ Hintergrund: 'rot', Farbe: 'weiß' }); Erstellen Sie ein Context-Objekt mit der statischen Methode React.createContext(). Dieses Context-Objekt enthält zwei Komponenten: <Provider /> und <Consumer />. Klasse App erweitert React.Component { rendern () { zurückkehren ( <ThemeContext.Provider Wert={{Hintergrund: 'grün', Farbe: 'weiß'}}> <Kopfzeile /> </ThemeContext.Provider> ); } } Der Wert von <Provider /> entspricht dem aktuellen getChildContext(). Klasse Header erweitert React.Component { rendern () { zurückkehren ( <Title>Hallo React Context API</Title> ); } } Klasse Title erweitert React.Component { rendern () { zurückkehren ( <ThemeContext.Consumer> {Kontext => ( <h1 Stil={{Hintergrund: Kontext.Hintergrund, Farbe: Kontext.Farbe}}> {diese.props.children} </h1> )} </ThemeContext.Consumer> ); } } Die untergeordneten Elemente von <Consumer /> müssen eine Funktion sein, und der von <Provider /> bereitgestellte Kontext wird über die Funktionsparameter abgerufen. Es ist ersichtlich, dass die neue API von Context dem Stil von React näher kommt. Mehrere Orte, an denen Sie direkt Kontext erhalten könnenTatsächlich verfügen React-Komponenten zusätzlich zur Kontexteigenschaft der Instanz (this.context) über viele weitere Stellen, an denen sie direkt auf den von der übergeordneten Komponente bereitgestellten Kontext zugreifen können. Beispielsweise die Bauweise:
Zum Beispiel der Lebenszyklus:
Bei funktionsorientierten zustandslosen Komponenten kann auf den Kontext der Komponente direkt über die Parameter der Funktion zugegriffen werden. const StatelessComponent = (Eigenschaften, Kontext) => ( ...... ) Das Obige sind die Grundlagen von Context. Ausführlichere Anleitungen finden Sie hier Mein Verständnis von KontextOK, nachdem ich über die Grundlagen gesprochen habe, möchte ich jetzt über mein Verständnis des Kontexts von React sprechen. Kontext als Komponentenbereich behandelnEntwickler, die React verwenden, wissen, dass eine React-App im Wesentlichen ein React-Komponentenbaum ist. Jede React-Komponente entspricht einem Knoten in diesem Baum. Mit Ausnahme des Stammknotens der App hat jeder andere Knoten eine übergeordnete Komponentenkette. Beispielsweise ist in der obigen Abbildung die übergeordnete Komponentenkette von <Child /> <SubNode /> – <Node /> – <App />, die übergeordnete Komponentenkette von <SubNode /> ist <Node /> – <App /> und die übergeordnete Komponentenkette von <Node /> hat nur einen Komponentenknoten, nämlich <App />. Diese baumartig verbundenen Komponentenknoten bilden tatsächlich einen Kontextbaum. Der Kontext jedes Knotens ist ein Objekt, das aus den Kontextobjekten besteht, die von allen Komponentenknoten in der übergeordneten Komponentenkette über getChildContext() bereitgestellt werden. Entwickler, die mit dem Konzept der JS-Bereichskette vertraut sind, sollten alle wissen, dass während der Ausführung eines JS-Codeblocks eine entsprechende Bereichskette erstellt wird. Diese Bereichskette zeichnet die aktiven Objekte auf, auf die während der Ausführung des JS-Codeblocks zur Laufzeit zugegriffen werden kann, einschließlich Variablen und Funktionen. Das JS-Programm greift über die Bereichskette auf Variablen und Funktionen innerhalb oder außerhalb des Codeblocks zu. Wenn wir die JS-Bereichskette als Analogie verwenden, ist das von der React-Komponente bereitgestellte Context-Objekt tatsächlich wie ein Bereich, der untergeordneten Komponenten für den Zugriff bereitgestellt wird, und die Eigenschaften des Context-Objekts können als aktive Objekte im Bereich betrachtet werden. Da der Kontext einer Komponente aus den Kontextobjekten besteht, die von allen Komponenten in ihrer übergeordneten Knotenkette über getChildContext() zurückgegeben werden, kann die Komponente über Context auf die Eigenschaften des Kontexts zugreifen, die von allen Knotenkomponenten in ihrer übergeordneten Komponentenkette bereitgestellt werden. Daher habe ich die Idee der JS-Bereichskette übernommen und Context als Bereich der Komponente verwendet. Fokus auf die Kontrollierbarkeit und Auswirkung des KontextsAllerdings unterscheidet sich Kontext als Komponentenbereich vom üblichen Konzept des Bereichs (soweit es die Programmiersprachen betrifft, mit denen ich in Kontakt gekommen bin). Wir müssen uns auf die Steuerbarkeit und Auswirkung des Kontexts konzentrieren. In unserer täglichen Entwicklung ist die Verwendung von Scope oder Context sehr üblich, natürlich und sogar unbewusst. Die Verwendung von Context in React ist jedoch nicht so einfach. Die übergeordnete Komponente muss den von ihr bereitgestellten Kontext über childContextTypes „deklarieren“, und die untergeordnete Komponente muss die Context-Eigenschaft der übergeordneten Komponente über contextTypes „beantragen“. Daher denke ich, dass der Kontext von React ein „ berechtigungspflichtiger“ Komponentenbereich ist. Welche Vorteile bietet dieser „autorisierte“ Ansatz? So wie ich es verstehe, besteht das Erste darin, die Konsistenz der Framework-API aufrechtzuerhalten und einen deklarativen Codierungsstil zu verwenden, genau wie bei propTypes. Darüber hinaus können die Steuerbarkeit und Einflussmöglichkeiten des durch die Komponente bereitgestellten Kontextes bis zu einem gewissen Grad sichergestellt werden. Die Komponenten der React App sind baumartige Strukturen, die sich Schicht für Schicht erweitern, und über- und untergeordnete Komponenten sind lineare Eins-zu-viele-Abhängigkeiten. Die zufällige Verwendung von Kontext zerstört tatsächlich diese Abhängigkeitsbeziehung, was zu einigen unnötigen zusätzlichen Abhängigkeiten zwischen Komponenten führt, die Wiederverwendbarkeit von Komponenten verringert und die Wartbarkeit der App beeinträchtigen kann. Aus der obigen Abbildung können wir erkennen, dass der ursprüngliche linear abhängige Komponentenbaum, da die untergeordnete Komponente den Kontext der übergeordneten Komponente verwendet, dazu führt, dass die <Child />-Komponente sowohl von <Node /> als auch von <App /> abhängig ist. Nach der Trennung von diesen beiden Komponenten kann die Verfügbarkeit von <Child /> nicht mehr garantiert werden, was die Wiederverwendbarkeit von <Child /> einschränkt. Meiner Meinung nach ist das Offenlegen von Daten oder API über den Kontext keine elegante Vorgehensweise, auch wenn react-redux dies auf diese Weise macht. Daher ist ein Mechanismus oder eine Einschränkung erforderlich, um unnötige Auswirkungen zu reduzieren. Durch die Einschränkung der beiden statischen Eigenschaften von childContextTypes und contextTypes kann bis zu einem gewissen Grad garantiert werden, dass nur die Komponente selbst oder andere mit der Komponente verknüpfte untergeordnete Komponenten nach Belieben auf die Eigenschaften des Kontexts zugreifen können, unabhängig davon, ob es sich um Daten oder Funktionen handelt. Denn nur die Komponente selbst oder verwandte untergeordnete Komponenten können eindeutig wissen, auf welche Kontexteigenschaften sie zugreifen können, und für andere Komponenten, die nicht mit der Komponente verwandt sind (ob intern oder extern), ist unklar, welche Kontexteigenschaften von den childContextTypes jeder übergeordneten Komponente in der übergeordneten Komponentenkette „deklariert“ werden. Daher ist es unmöglich, verwandte Eigenschaften über ContextTypes „anzuwenden“. Daher verstehe ich es so, dass die Steuerbarkeit und der Einflussbereich des Kontexts bis zu einem gewissen Grad sichergestellt werden können, wenn man dem Kontextbereich der Komponente „Autorität“ verleiht. Bei der Entwicklung von Komponenten sollten wir immer darauf achten und Context nicht leichtfertig verwenden. Sie müssen Context nicht zuerst verwendenDa es sich bei React um eine High-Level-API handelt, empfiehlt React nicht, der Verwendung von Context Priorität einzuräumen. Mein Verständnis ist:
Kurz gesagt, solange Sie sicherstellen können, dass Context steuerbar ist, gibt es kein Problem bei der Verwendung von Context. Wenn es sinnvoll angewendet werden kann, kann Context tatsächlich eine sehr leistungsstarke Erfahrung in die Entwicklung von React-Komponenten einbringen. Verwenden Sie Context als Medium zum Teilen von DatenDer offizielle Kontext kann für die komponentenübergreifende Datenkommunikation verwendet werden. Ich verstehe es als eine Brücke, ein Medium zum Teilen von Daten. Die Datenfreigabe kann in zwei Kategorien unterteilt werden: App-Ebene und Komponentenebene.
Das von der App-Stammknotenkomponente bereitgestellte Kontextobjekt kann als globaler Bereich der App-Ebene betrachtet werden. Daher verwenden wir das von der App-Stammknotenkomponente bereitgestellte Kontextobjekt, um einige globale Daten auf App-Ebene zu erstellen. Fertige Beispiele finden Sie unter react-redux. Nachfolgend sehen Sie die Kernimplementierung des Quellcodes der Komponente <Provider />: Exportfunktion createProvider(storeKey = 'store', Unterschlüssel) { const subscriptionKey = unterSchlüssel || `${storeKey}Abonnement` Klasse Provider erweitert Komponente { getChildContext() { return { [Store-Schlüssel]: dieser [Store-Schlüssel], [Abonnementschlüssel]: null } } Konstruktor(Requisiten, Kontext) { super(Requisiten, Kontext) dies[storeKey] = props.store; } rendern() { gibt Children.only(diese.props.children) zurück } } // ...... Anbieter.propTypes = { speichern: storeShape.isRequired, untergeordnete Elemente: PropTypes.element.isRequired, } Anbieter.childContextTypes = { [storeKey]: storeShape.isterforderlich, [Abonnementschlüssel]: Abonnementform, } return Anbieter } Standardmäßig exportieren createProvider() Wenn die Stammkomponente der App mit der Komponente <Provider /> umschlossen wird, stellt sie im Wesentlichen einen globalen Eigenschaftenspeicher für die App bereit, was dem Teilen der Speichereigenschaften in der gesamten App entspricht. Natürlich kann die Komponente <Provider /> auch in andere Komponenten gepackt werden, um den Store global auf Komponentenebene freizugeben.
Wenn die Funktion einer Komponente nicht von der Komponente selbst ausgeführt werden kann und auf zusätzliche Unterkomponenten angewiesen ist, können Sie mit Context eine Komponente erstellen, die aus mehreren Unterkomponenten besteht. Beispielsweise react-router. Der <Router /> von react-router kann Routing-Operationen und -Verwaltung nicht unabhängig durchführen, da Navigationslinks und umgeleitete Inhalte normalerweise getrennt sind. Daher ist es auch erforderlich, sich auf Unterkomponenten wie <Link /> und <Route /> zu verlassen, um Routing-bezogene Arbeiten gemeinsam durchzuführen. Um die Zusammenarbeit verwandter Unterkomponenten zu ermöglichen, besteht die Implementierung des React-Routers darin, Context zu verwenden, um einen Router zwischen verwandten Komponenten wie <Router />, <Link /> und <Route /> gemeinsam zu nutzen, wodurch die einheitliche Bedienung und Verwaltung des Routings vervollständigt wird. Zum besseren Verständnis des oben Gesagten finden Sie nachfolgend einen Teil des Quellcodes der relevanten Komponenten <Router />, <Link /> und <Route />. // Router.js /** * Die öffentliche API, um die Geschichte in einen Kontext zu setzen. */ Klasse Router erweitert React.Component { statische Eigenschaftentypen = { Verlauf: PropTypes.object.isRequired, untergeordnete Elemente: PropTypes.node }; statische Kontexttypen = { Router: PropTypes.Objekt }; statische untergeordnete Kontexttypen = { Router: PropTypes.object.isRequired }; getChildContext() { zurückkehren { Router: { ...diesen.Kontext.Router, Geschichte: this.props.history, Route: { Standort: this.props.history.location, Übereinstimmung: dieser.Zustand.Übereinstimmung } } }; } // ...... KomponenteWillMount() { const { Kinder, Verlauf } = this.props; // ...... dies.abhören = history.listen(() => { dies.setState({ Übereinstimmung: this.computeMatch(history.location.pathname) }); }); } // ...... } Obwohl der Quellcode andere Logik enthält, besteht der Kern von <Router /> darin, einen Kontext mit einem Router-Attribut für die untergeordnete Komponente bereitzustellen, gleichzeitig den Verlauf zu überwachen und ein erneutes Rendern der Komponente über setState() auszulösen, sobald sich der Verlauf ändert. // Link.js /** * Die öffentliche API zum Rendern eines verlaufsbewussten <a>. */ Klasse Link erweitert React.Component { // ...... statische Kontexttypen = { Router: PropTypes.shape({ Verlauf: PropTypes.shape({ push: PropTypes.func.isterforderlich, ersetzen: PropTypes.func.isRequired, createHref: PropTypes.func.isterforderlich }).ist erforderlich }).ist erforderlich }; handleClick = Ereignis => { wenn (this.props.onClick) this.props.onClick(Ereignis); Wenn ( !event.defaultPrevented && Ereignisschaltfläche === 0 && !diese.Eigenschaften.Ziel && !isModifiedEvent(Ereignis) ) { event.preventDefault(); // Verwenden Sie die von der Komponente <Router /> bereitgestellte Routerinstanz const { history } = this.context.router; const { ersetzen, durch } = this.props; wenn (ersetzen) { Verlauf.Ersetzen(durch); } anders { Verlauf.push(zu); } } }; rendern() { const { ersetzen, durch, innerRef, ...props } = this.props; // ... const { Verlauf } = dieser.Kontext.Router; Konstantstandort = typeof zu === "Zeichenfolge" ? createLocation(nach, null, null, Verlauf.Standort) : Zu; const href = Verlauf.createHref(Standort); zurückkehren ( <a {...props} beiKlick={this.handleClick} href={href} ref={innerRef} /> ); } } Der Kern von <Link /> besteht darin, das <a>-Tag zu rendern, das Klickereignis des <a>-Tags abzufangen und dann Routing-Vorgänge für den Verlauf über den von <Router /> gemeinsam genutzten Router auszuführen und <Router /> anschließend zu benachrichtigen, dass ein erneutes Rendern durchgeführt werden soll. // Route.js /** * Die öffentliche API zum Abgleichen eines einzelnen Pfads und zum Rendern. */ Klasse Route erweitert React.Component { // ...... Zustand = { Übereinstimmung: this.computeMatch(this.props, this.context.router) }; // Berechnen Sie den passenden Pfad. Wenn er übereinstimmt, wird ein passendes Objekt zurückgegeben, andernfalls wird null zurückgegeben. berechneMatch( { computedMatch, Standort, Pfad, streng, genau, sensibel }, Router ) { wenn (berechnetesMatch) returniere berechnetesMatch; // ...... const { route } = Router; const Pfadname = (Standort || Route.Standort).Pfadname; returniere matchPath(Pfadname, { Pfad, streng, genau, sensibel }, route.match); } // ...... rendern() { const { match } = dieser.Zustand; const { untergeordnete Elemente, Komponente, Renderer} = this.props; const { Verlauf, Route, statischer Kontext } = dieser.Kontext.Router; const Standort = this.props.location || Route.Standort; const props = { Übereinstimmung, Standort, Verlauf, statischer Kontext }; wenn (Komponente) Übereinstimmung zurückgibt? React.createElement(Komponente, Requisiten): null; wenn (Rendern) Übereinstimmung zurückgeben? Render(Props): null; wenn (Typ der untergeordneten Elemente === "Funktion"), returniere untergeordnete Elemente (Eigenschaften); wenn (Kinder && !isEmptyChildren(Kinder)) gibt React.Children.only(Kinder) zurück; gibt null zurück; } } <Route /> verfügt über einen ähnlichen Quellcode wie <Router />, der verschachtelte Routen realisieren kann. Sein Kern besteht jedoch darin, zu bestimmen, ob der Pfad der aktuellen Route über den von Context gemeinsam genutzten Router übereinstimmt, und dann die Komponente zu rendern. Durch die obige Analyse können wir sehen, dass der gesamte React-Router tatsächlich um den Kontext von <Router /> herum aufgebaut ist. Entwickeln von Komponenten mit ContextZuvor wurde über Context eine einfache Komponente, die Slot-Verteilungskomponente, entwickelt. In diesem Kapitel wird anhand der Entwicklungserfahrungen mit dieser Slot-Verteilungskomponente erläutert, wie Context zur Entwicklung von Komponenten eingesetzt werden kann. SteckplatzverteilungskomponentenLassen Sie uns zunächst darüber sprechen, was Slot-Verteilungskomponenten sind. Dieses Konzept wurde erstmals in Vuejs eingeführt. Slot-Verteilung ist eine Technologie, die den Inhalt einer übergeordneten Komponente durch die Kombination von Komponenten in die Vorlage einer untergeordneten Komponente einfügt. In Vuejs wird sie Slot genannt. Damit dieses Konzept für jeden intuitiver verständlich ist, habe ich eine Demo zur Slot-Verteilung von Vuejs verschoben. Für die Komponente <my-component /> mit dem bereitgestellten Slot sieht die Vorlage wie folgt aus: <div> <h2>Ich bin der Titel der Unterkomponente</h2> <Steckplatz> Wird nur angezeigt, wenn kein Inhalt zum Verteilen vorhanden ist</slot> </div> Für die übergeordnete Komponente sieht die Vorlage wie folgt aus: <div> <h1>Ich bin der Titel der übergeordneten Komponente</h1> <meine-Komponente> <p>Dies ist ein erster Inhalt</p> <p>Dies ist weiterer anfänglicher Inhalt</p> </meine-Komponente> </div> Das endgültige gerenderte Ergebnis: <div> <h1>Ich bin der Titel der übergeordneten Komponente</h1> <div> <h2>Ich bin der Titel der Unterkomponente</h2> <p>Dies ist ein erster Inhalt</p> <p>Dies ist weiterer anfänglicher Inhalt</p> </div> </div> Sie können sehen, dass der Knoten <slot /> der Komponente <my-component /> schließlich durch den Inhalt unter dem Knoten <my-component /> in der übergeordneten Komponente ersetzt wird. Vuejs unterstützt auch benannte Slots. Beispielsweise eine Layoutkomponente <app-layout />: <div Klasse="Container"> <Kopfzeile> <slot name="header"></slot> </header> <Haupt> <Steckplatz></Steckplatz> </main> <Fußzeile> <slot name="Fußzeile"></slot> </Fußzeile> </div> Und in der Vorlage der übergeordneten Komponente: <App-Layout> <h1 slot="header">Dies kann ein Seitentitel sein</h1> <p>Ein Absatz mit Hauptinhalt. </p> <p>Ein weiterer Absatz. </p> <p slot="footer">Hier sind einige Kontaktinformationen</p> </app-layout> Das endgültige gerenderte Ergebnis: <div Klasse="Container"> <Kopfzeile> <h1>Dies kann ein Seitentitel sein</h1> </header> <Haupt> <p>Ein Absatz mit Hauptinhalt. </p> <p>Ein weiterer Absatz. </p> </main> <Fußzeile> <p>Hier sind einige Kontaktinformationen</p> </Fußzeile> </div> Der Vorteil der Slot-Verteilung besteht darin, dass Komponenten in Vorlagen abstrahiert werden können. Die Komponente selbst kümmert sich nur um die Vorlagenstruktur und überlässt die Verarbeitung des spezifischen Inhalts der übergeordneten Komponente. Gleichzeitig wird der grammatikalische Ausdruck von HTML, der die DOM-Struktur beschreibt, nicht verletzt. Ich denke, dass dies eine sehr sinnvolle Technologie ist, aber leider ist die Unterstützung von React für diese Technologie nicht sehr freundlich. Daher habe ich mich an der Slot-Verteilungskomponente von Vuejs orientiert und einen Satz von Slot-Verteilungskomponenten auf Basis von React entwickelt, mit denen React-Komponenten auch über Vorlagenfunktionen verfügen können. Für die Komponente <AppLayout /> würde ich es gerne so schreiben: Klasse AppLayout erweitert React.Component { statischer Anzeigename = "AppLayout" rendern () { zurückkehren ( <div Klasse="Container"> <Kopfzeile> <Slotname="Header"></Slot> </header> <Haupt> <Steckplatz></Steckplatz> </main> <Fußzeile> <Slot name="footer"></Slot> </Fußzeile> </div> ) } } Bei Verwendung in der äußeren Schicht kann es folgendermaßen geschrieben werden: <Anwendungslayout> <AddOn slot="header"> <h1>Dies kann ein Seitentitel sein</h1> </AddOn> <AddOn> <p>Ein Absatz mit Hauptinhalt. </p> <p>Ein weiterer Absatz. </p> </AddOn> <AddOn slot="Fußzeile"> <p>Hier sind einige Kontaktinformationen</p> </AddOn> </AppLayout> Ideen zur KomponentenimplementierungLassen Sie uns auf der Grundlage meiner bisherigen Überlegungen zunächst die Ideen zur Umsetzung klären. Es ist nicht schwer zu erkennen, dass die Slot-Verteilungskomponente auf zwei Unterkomponenten angewiesen ist – der Slot-Komponente <Slot /> und der Verteilungskomponente <AddOn />. Die Slot-Komponente ist für das Stapeln und Bereitstellen von Slots zur Verteilung von Inhalten verantwortlich. Die Verteilungskomponente ist dafür verantwortlich, den Verteilungsinhalt zu sammeln und ihn der Slot-Komponente zur Darstellung des Verteilungsinhalts bereitzustellen. Sie entspricht dem Verbraucher des Slots. Offensichtlich gibt es hier ein Problem. Die Komponente <Slot /> ist unabhängig von der Komponente <AddOn />. Wie füllt man den Inhalt von <AddOn /> in <Slot />? Dieses Problem lässt sich nicht schwer lösen. Wenn zwei unabhängige Module verbunden werden müssen, bauen Sie einfach eine Brücke zwischen ihnen. Also, wie bauen wir diese Brücke? Schauen wir uns noch einmal den Code an, den wir uns zuvor vorgestellt haben. Für die Komponente <AppLayout /> möchten Sie es wie folgt schreiben: Klasse AppLayout erweitert React.Component { statischer Anzeigename = "AppLayout" rendern () { zurückkehren ( <div Klasse="Container"> <Kopfzeile> <Slotname="Header"></Slot> </header> <Haupt> <Steckplatz></Steckplatz> </main> <Fußzeile> <Slot name="footer"></Slot> </Fußzeile> </div> ) } } Bei Verwendung in der äußeren Schicht wird es folgendermaßen geschrieben: <Anwendungslayout> <AddOn slot="header"> <h1>Dies kann ein Seitentitel sein</h1> </AddOn> <AddOn> <p>Ein Absatz mit Hauptinhalt. </p> <p>Ein weiterer Absatz. </p> </AddOn> <AddOn slot="Fußzeile"> <p>Hier sind einige Kontaktinformationen</p> </AddOn> </AppLayout> Unabhängig davon, ob es sich um <Slot /> oder <AddOn /> handelt, liegen sie tatsächlich im Rahmen von <AppLayout />. <Slot /> ist der Komponentenknoten, der von der render()-Methode der <AppLayout />-Komponente zurückgegeben wird, und <AddOn /> ist der untergeordnete Knoten von <AppLayout />. Daher kann <AppLayout /> als Brücke zwischen <Slot /> und <AddOn /> betrachtet werden. Wie also stellt <AppLayout /> eine Verbindung zwischen <Slot /> und <AddOn /> her? Hier verwenden wir den Protagonisten dieses Artikels – den Kontext. Die nächste Frage ist, wie Context verwendet wird, um eine Verbindung zwischen <Slot /> und <AddOn /> herzustellen? Die Brücke <AppLayout /> wurde bereits zuvor erwähnt. In der äußeren Komponente ist <AppLayout /> für das Sammeln des Inhalts verantwortlich, um den Slot über <AddOn /> zu füllen. <AppLayout /> definiert eine Schnittstelle zum Abrufen von Füllinhalten mit Hilfe von Context. Da <Slot /> beim Rendern der von <AppLayout /> gerenderte Knoten ist, kann <Slot /> über Context die Schnittstelle zum Abrufen des von <AppLayout /> definierten Füllinhalts abrufen und dann zum Rendern den Füllinhalt über diese Schnittstelle abrufen. Slot-Verteilungskomponenten entsprechend der Idee umsetzenDa <AddOn /> ein untergeordneter Knoten von <AppLayout /> und <AddOn /> eine bestimmte Komponente ist, können wir es anhand des Namens oder des Anzeigenamens identifizieren. Daher durchläuft <AppLayout /> vor dem Rendern, d. h. bevor render() zurückkehrt, die untergeordneten Elemente und speichert jedes untergeordnete Element von <AddOn /> zwischen, wobei der Wert von Slot als Schlüssel verwendet wird. Wenn <AddOn /> keinen Slot festlegt, wird davon ausgegangen, dass der unbenannte <Slot /> gefüllt wird. Wir können diesen unbenannten Slots einen Schlüssel zuweisen, z. B. $$default. Für <AppLayout /> lautet der Code wie folgt: Klasse AppLayout erweitert React.Component { statische untergeordnete Kontexttypen = { requestAddOnRenderer: PropTypes.func } // Wird verwendet, um den Inhalt jedes <AddOn /> zwischenzuspeichern. addOnRenderers = {} // Schnittstelle getChildContext () für untergeordnete Knoten über Context { bereitstellen const requestAddOnRenderer = (Name) => { wenn (!this.addOnRenderers[name]) { Rückgabe undefiniert } return () => ( this.addOnRenderers[Name] ) } zurückkehren { requestAddOnRenderer } } rendern () { Konstante { Kinder, …restProps } = diese.Eigenschaften wenn (Kinder) { // Cache den Inhalt von <AddOn /> im kv-Modus const arr = React.Children.toArray(children) const nameChecked = [] this.addOnRenderers = {} arr.fürJeden(Element => { const itemType = item.typ wenn (item.type.displayName === 'AddOn') { const slotName = item.props.slot || '$$default' // Eindeutigkeit des Inhalts sicherstellen if (nameChecked.findIndex(item => item === stubName) !== -1) { throw new Error(`Slot(${slotName}) wurde belegt`) } this.addOnRenderers[stubName] = item.props.children nameChecked.push(StubName) } }) } zurückkehren ( <div Klasse="Container"> <Kopfzeile> <Slotname="Header"></Slot> </header> <Haupt> <Steckplatz></Steckplatz> </main> <Fußzeile> <Slot name="footer"></Slot> </Fußzeile> </div> ) } } <AppLayout /> definiert eine Kontextschnittstelle requestAddOnRenderer(). Die Schnittstelle requestAddOnRenderer() gibt eine Funktion basierend auf dem Namen zurück. Die zurückgegebene Funktion greift basierend auf dem Namen auf die Eigenschaften von addOnRenderers zu. addOnRenderers ist das Inhaltscacheobjekt von <AddOn />. Die Implementierung von <Slot /> ist sehr einfach, der Code lautet wie folgt: // Requisiten, Kontext const Slot = ({ Name, untergeordnete Elemente }, { requestAddOnRenderer }) => { const addOnRenderer = requestAddOnRenderer(Name). return (addOnRenderer && addOnRenderer()) || Kinder || Null } Slot.displayName = "Steckplatz" Slot.contextTypes = { requestAddOnRenderer: PropTypes.func } Slot.propTypes = { Name: PropTypes.string } Slot.defaultProps = { Name: '$$default' } Es ist ersichtlich, dass <Slot /> die von <AppLayout /> bereitgestellte Schnittstelle requestAddOnRenderer() über den Kontext erhält und dass das Hauptobjekt des endgültigen Renderings der im <AppLayout /> zwischengespeicherte Inhalt von <AddOn /> ist. Wenn der Inhalt des angegebenen <AddOn /> nicht abgerufen wird, werden die untergeordneten Elemente von <Slot /> selbst gerendert. <AddOn /> ist noch einfacher: const AddOn = () => null AddOn.propTypes = { slot: PropTypes.string } AddOn.defaultTypes = { Steckplatz: '$$default' } AddOn.displayName = "AddOn" <AddOn /> tut nichts, gibt aber null zurück. Seine Funktion besteht darin, dass <AppLayout /> den an den Slot verteilten Inhalt zwischenspeichern kann. Kann <AppLayout /> vielseitiger machenDurch den obigen Code wird <AppLayout /> grundsätzlich in eine Komponente mit Slot-Verteilungsfunktionen umgewandelt, es ist jedoch offensichtlich, dass <AppLayout /> nicht universell ist. Wir können es zu einer unabhängigen und universellen Komponente machen. Ich habe diese Komponente SlotProvider genannt Funktion getDisplayName (Komponente) { returniere Komponente.Anzeigename || Komponente.Name || 'Komponente' } const slotProviderHoC = (WrappedComponent) => { return-Klasse erweitert React.Component { statischer Anzeigename = `SlotProvider(${getDisplayName(WrappedComponent)})` statische untergeordnete Kontexttypen = { requestAddOnRenderer: PropTypes.func } // Wird verwendet, um den Inhalt jedes <AddOn /> zwischenzuspeichern. addOnRenderers = {} // Schnittstelle getChildContext () für untergeordnete Knoten über Context { bereitstellen const requestAddOnRenderer = (Name) => { wenn (!this.addOnRenderers[name]) { Rückgabe undefiniert } return () => ( this.addOnRenderers[Name] ) } zurückkehren { requestAddOnRenderer } } rendern () { Konstante { Kinder, …restProps } = diese.Eigenschaften wenn (Kinder) { // Cache den Inhalt von <AddOn /> im kv-Modus const arr = React.Children.toArray(children) const nameChecked = [] this.addOnRenderers = {} arr.fürJeden(Element => { const itemType = item.typ wenn (item.type.displayName === 'AddOn') { const slotName = item.props.slot || '$$default' // Eindeutigkeit des Inhalts sicherstellen if (nameChecked.findIndex(item => item === stubName) !== -1) { throw new Error(`Slot(${slotName}) wurde belegt`) } this.addOnRenderers[stubName] = item.props.children nameChecked.push(StubName) } }) } Rückgabewert (<WrappedComponent {...restProps} />) } } } exportiere const SlotProvider = slotProviderHoC Verwenden Sie die High-Order-Komponenten von React, um das ursprüngliche <AppLayout /> in eine unabhängige und allgemeine Komponente umzuwandeln. Für das ursprüngliche <AppLayout /> können Sie diese SlotProvider-High-Order-Komponente verwenden, um es in eine Komponente mit Slot-Verteilungsfunktionen zu konvertieren. importiere { SlotProvider } aus './SlotProvider.js' Klasse AppLayout erweitert React.Component { statischer Anzeigename = "AppLayout" rendern () { zurückkehren ( <div Klasse="Container"> <Kopfzeile> <Slotname="Header"></Slot> </header> <Haupt> <Steckplatz></Steckplatz> </main> <Fußzeile> <Slot name="footer"></Slot> </Fußzeile> </div> ) } } Standard-SlotProvider exportieren (AppLayout) Durch die oben genannten Erfahrungen können wir sehen, dass beim Entwurf und der Entwicklung einer Komponente,
Zu diesem Zeitpunkt müssen wir einen Vermittler als Medium zum Teilen von Daten verwenden. Im Vergleich zur Einführung von Drittanbietermodulen wie Redux kann die direkte Verwendung von Context eleganter sein. Probieren Sie die neue Context API ausVerwenden Sie die neue Version der Context-API, um die vorherige Slot-Verteilungskomponente zu transformieren. // SlotProvider.js Funktion getDisplayName (Komponente) { returniere Komponente.Anzeigename || Komponente.Name || 'Komponente' } exportiere const SlotContext = React.createContext({ requestAddOnRenderer: () => {} }) const slotProviderHoC = (WrappedComponent) => { return-Klasse erweitert React.Component { statischer Anzeigename = `SlotProvider(${getDisplayName(WrappedComponent)})` // Wird verwendet, um den Inhalt jedes <AddOn /> zwischenzuspeichern. addOnRenderers = {} requestAddOnRenderer = (Name) => { wenn (!this.addOnRenderers[name]) { Rückgabe undefiniert } return () => ( this.addOnRenderers[Name] ) } rendern () { Konstante { Kinder, …restProps } = diese.Eigenschaften wenn (Kinder) { // Cache den Inhalt von <AddOn /> im kv-Modus const arr = React.Children.toArray(children) const nameChecked = [] this.addOnRenderers = {} arr.fürJeden(Element => { const itemType = item.typ wenn (item.type.displayName === 'AddOn') { const slotName = item.props.slot || '$$default' // Eindeutigkeit des Inhalts sicherstellen if (nameChecked.findIndex(item => item === stubName) !== -1) { throw new Error(`Slot(${slotName}) wurde belegt`) } this.addOnRenderers[stubName] = item.props.children nameChecked.push(StubName) } }) } zurückkehren ( <SlotContext.Provider-Wert={ requestAddOnRenderer: this.requestAddOnRenderer }> <WrappedComponent {...restProps} /> </Slotcontext.provider> ) } } } Export const slotProvider = slotProviderhoc Die vorherigen ChildcontextTypes und GetChildContext () wurden entfernt. // Slot.js importieren {slotcontext} aus './slotprovider.js' ' const Slot = ({Name, Kinder}) => { zurückkehren ( <SlotContext.Consumer> {(context) => { const addonrenderer = requestAddonrenderer (Name) return (addonrenderer && addonrenderer ()) || Kinder || Null }} </Slotcontext.consumer> ) } Slot.Displayname = 'Slot' ' SLOT.PROPTYPES = {NAME: ProTTypes.String} Slot.DefaultProps = {Name: '$$ Standard'} Da der Kontext zuvor im Hersteller-Verbraucher-Modus verwendet wurde und die Komponente selbst relativ einfach war, gab es nach der Transformation mit der neuen API keinen großen Unterschied. Zusammenfassen
Das obige ist mein Teilen von Inhalten. Verweise Kontext - https://reactjs.org/docs/context.html Dies ist das Ende dieses Artikels über mein Verständnis und die Anwendung des React -Kontextes. Das könnte Sie auch interessieren:
|
<<: Analyse und Lösung der Gründe, warum geplante Crontab-Aufgaben nicht ausgeführt werden
>>: Detaillierte Einführung in den MySql-Index und korrekte Verwendungsmethode
Vorwort Bevor wir mit diesem Artikel beginnen, be...
Gespeicherte Datenbankprozeduren DROP-VERFAHREN, ...
Inhaltsverzeichnis 1. Umgebungskonfiguration 1.NT...
Inhaltsverzeichnis 1. Was ist Dockerfile? 2. Anal...
1. Herunterladen Zunächst möchte ich einen inländ...
Anfänger können HTML lernen, indem sie einige HTM...
Beim Laden von Netzwerkdaten wird zur Verbesserun...
Die folgende Grafik zeigt, wie zeitaufwändig es is...
einführen In diesem Kapitel wird hauptsächlich de...
Inhaltsverzeichnis Einführung Schritt 1 Schritt 2...
1. Bei der Verwendung von mysqldump wird ein Fehl...
MySQL-Datenbank erstellen Nachdem wir uns beim My...
1. Migrationsmethode für virtuelle KVM-Maschinen ...
Einführung: Im Vergleich zu herkömmlichen Bildver...
Korrespondenz zwischen Flutter und CSS im Shadow-...