Was ist ReactReact ist eine einfache JavaScript-UI-Bibliothek zum Erstellen effizienter und schneller Benutzeroberflächen. Es handelt sich um eine leichtgewichtige Bibliothek und daher ist sie auch so beliebt. Es folgt Komponentenentwurfsmustern, deklarativen Programmierparadigmen und funktionalen Programmierkonzepten, um Front-End-Anwendungen effizienter zu gestalten. Es verwendet virtuelles DOM, um DOM effizient zu manipulieren. Es folgt ein unidirektionaler Datenfluss von Komponenten höherer Ordnung zu Komponenten niedrigerer Ordnung. Vorwort👉 Wir glauben, dass React die bevorzugte Methode ist, um schnelle, reaktionsschnelle und groß angelegte Webanwendungen in JavaScript zu erstellen. Es funktioniert gut auf Facebook und Instagram. Offizielle Website-Adresse Das Konzept von React besteht darin, React vermeidet dieses Problem aufgrund seines eigenen zugrunde liegenden Designs, sodass die Veröffentlichung von react16.8 nur drei Dinge für das Front-End-Feld tut: schnelle Reaktion, schnelle Reaktion oder TMD-schnelle Reaktion! Dieser Artikel beginnt mit einem HTML, folgt dem Faserkonzept von react und imitiert ein sehr grundlegendes react Vorbereitungsarbeiten am Anfang🤖html Wir benötigen HTML, um die gesamte Seite und die Funktionsweise von React zu unterstützen. Fügen Sie der Seite <!DOCTYPE html> <html lang="de"> <Kopf> <meta charset="UTF-8"> <meta name="viewport" content="width=Gerätebreite, Anfangsmaßstab=1.0"> <title>Dokument</title> </Kopf> <Text> <div id="Wurzel"></div> <script Typ="Modul" src="./index.js" ></script> </body> </html> Es wird empfohlen, ein JavaScript Wir werden die folgende Reaktion imitieren, um eine grundlegende Operation zu implementieren, das Ereignis an ... Funktion App() { zurückkehren ( <div> <input onInput={updateValue} Wert={Wert} /> <h2>Hallo {value}</h2> <hr /> </div> ); } ... Wenn React mit Babel kompiliert wird, wird ... Reagieren.createElement( "div", null, React.createElement("Eingabe", { beiEingabe: Aktualisierungswert, Wert: Wert, }), React.createElement("h2", null, "Hallo", Wert), React.createElement("hr", null) ); ... Online-Adresse Aus dem konvertierten Code können wir erkennen, dass React.createElement mehrere Parameter unterstützt:
Wir können ein { Typ: „Knotenbezeichnung“, Requisiten:{ props: „Eigenschaften des Knotens, einschließlich Ereignisse, Klassen …“, children: „Untergeordnete Knoten des Knotens“ } } Hier können wir eine Funktion schreiben, um die folgenden Anforderungen zu erreichen
/** * Erstellen Sie eine virtuelle DOM-Struktur * @param {type} Tag-Name * @param {props} Eigenschaftsobjekt * @param {children} untergeordnete Knoten * @return {element} virtuelles DOM */ const createElement = (Typ, Eigenschaften, ...untergeordnete Elemente) => ({ Typ, Requisiten: { ...Requisiten, Kinder: Kinder.map(Kind => Typ des untergeordneten Objekts === "Objekt" ? Kind : { Typ: "TEXT_ELEMENT", Requisiten: { Knotenwert: Kind, Kinder: [], }, } ), }, }); React-Quellcodeimplementierung von createElement Nach der Implementierung importiere {createElement,render} aus "./mini/index.js"; const updateValue = e => executeRender(e.target.value); const executeRender = (Wert = "Welt") => { const element = erstelleElement( "div", null, createElement("Eingabe", { beiEingabe: Aktualisierungswert, Wert: Wert, }), createElement("h2", null, "Hallo", Wert), createElement("hr", null) ); rendern(Element, Dokument.getElementById("root")); }; führeRender(); Was wird beim Rendern gemacht?vor der Version
Vor React 16.8 erfolgte das Rendering in den folgenden Schritten:
Holen Sie sich den virtuellen DOM und führen Sie die oben genannten drei Schritte rekursiv aus. Die gerenderte Seite ähnelt dem folgenden Prozess /** * Virtuelles DOM zum realen DOM hinzufügen * @param {element} virtuelles DOM * @param {container} echtes DOM */ const render = (Element, Container) => { lass dom; /* Verarbeitungsknoten (einschließlich Textknoten) */ wenn (Typ des Elements !== "Objekt") { dom = document.createTextNode(element); } anders { dom = Dokument.createElement(Element.Typ); } /* Verarbeitungseigenschaften (einschließlich Ereigniseigenschaften) */ wenn (element.props) { Objekt.Schlüssel(Element.Eigenschaften) .filter((Schlüssel) => Schlüssel != "Kinder") .fürEach((Element) => { dom[Element] = Element.Requisiten[Element]; }); Objekt.Schlüssel(Element.Eigenschaften) .filter((Schlüssel) => Schlüssel.startsWith("on")) .fürEach((Name) => { const eventType = name.toLowerCase().substring(2); dom.addEventListener(eventType, nextProps[name]); }); } Wenn ( element.props && element.props.children && element.Requisiten.Kinder.Länge ) { /* Schleife zum Hinzufügen zum DOM */ element.props.children.forEach((Kind) => render(Kind, dom)); } container.anhängenKind(dom); }; nach Version (Faser) Wenn wir mit dem Schreiben des obigen Codes fertig sind, werden wir feststellen, dass dieser rekursive Aufruf problematisch ist. Der obige Teil der Arbeit wird von React offiziell als Renderer bezeichnet. Renderer ist ein Modul, das Dritte selbst implementieren können. Es gibt auch ein Kernmodul namens Reconsiler. Eine der Hauptfunktionen von Reconsiler ist der Diff-Algorithmus. Er berechnet, welche Seitenknoten aktualisiert werden sollen, und übergibt dann den virtuellen DOM der zu aktualisierenden Knoten an den Renderer. Der Renderer ist für das Rendern dieser Knoten auf der Seite verantwortlich, dies erfolgt jedoch synchron. Sobald das Rendern beginnt, werden alle Knoten und ihre untergeordneten Knoten gerendert und der Vorgang endet erst, wenn er abgeschlossen ist. In der offiziellen Präsentation von React gibt es ein Beispiel, das die Verzögerung, die durch diese synchrone Berechnung entsteht, deutlich zeigt: Wenn der DOM-Baum groß ist, kann die Ausführungszeit des JS-Threads relativ lang sein. Während dieser Zeit reagiert der Browser nicht auf andere Ereignisse, da sich der JS-Thread und der GUI-Thread gegenseitig ausschließen. Die Seite reagiert nicht, wenn das JS ausgeführt wird. Wenn diese Zeit zu lang ist, kann es zu Verzögerungen kommen. Wir können dieses Problem in zwei Schritten lösen.
Lösung I führt eine neue API ein requestIdleCallback empfängt einen Rückruf, der aufgerufen wird, wenn der Browser im Leerlauf ist. Jeder Aufruf übergibt eine IdleDeadline, um die aktuelle Leerlaufzeit zu ermitteln. Optionen können die maximale Wartezeit übergeben. Wenn der Browser zu diesem Zeitpunkt nicht im Leerlauf ist, wird die Ausführung erzwungen. window.requestIdleCallback stellt eine Funktion in die Warteschlange, die während der Leerlaufzeit des Browsers aufgerufen werden soll. Dadurch können Entwickler Hintergrundarbeiten und Arbeiten mit niedriger Priorität an der Hauptereignisschleife durchführen, ohne dass verzögerte Schlüsselereignisse beeinträchtigt werden. Da diese API jedoch noch experimentell ist und eine schlechte Kompatibilität aufweist, hat React offiziell einen eigenen Satz implementiert. Dieser Artikel wird weiterhin requestIdleCallback für die Aufgabenplanung verwenden // Nächste Arbeitseinheit let nextUnitOfWork = null /** * workLoop Arbeitsschleifenfunktion * @param {deadline} Frist * / Funktion Arbeitsschleife(Frist) { // Soll die Arbeitsschleifenfunktion gestoppt werden? let shouldYield = false // Wenn eine nächste Arbeitseinheit vorhanden ist und keine andere Arbeit mit höherer Priorität, führen Sie eine Schleife aus, während (nextUnitOfWork && !shouldYield) { nächsteArbeitseinheit = Arbeitseinheit ausführen( nächsteArbeitseinheit ) // Wenn die Frist näher rückt, stoppen Sie die Arbeit Loop-Funktion shouldYield = deadline.timeRemaining() < 1 } // Benachrichtigen Sie den Browser, dass workLoop während der Leerlaufzeit ausgeführt werden soll AnfrageIdleCallback(workLoop) } // Benachrichtigen Sie den Browser, dass workLoop während der Leerlaufzeit ausgeführt werden soll AnfrageIdleCallback(workLoop) // Unit-Ereignisse ausführen und das nächste Unit-Ereignis zurückgeben Funktion performUnitOfWork(nextUnitOfWork) { //ZU TUN } Lösung II: Erstellen der Faserdatenstruktur Die vorherige Datenstruktur von Fiber ist ein Baum, bei dem die untergeordneten Knoten des übergeordneten Knotens auf die untergeordneten Knoten zeigen, aber dieser Zeiger allein kann keine Unterbrechung und Fortsetzung erreichen. Beispielsweise habe ich jetzt einen übergeordneten Knoten A, A hat drei untergeordnete Knoten B, C und D. Wenn ich zu C gehe, werde ich unterbrochen. Wenn ich erneut anfange, weiß ich eigentlich nicht, welchen ich unter C ausführen soll, da ich nur C kenne und es weder einen Zeiger auf seinen übergeordneten Knoten noch einen Zeiger auf seine Brüder gibt. Fiber transformiert eine solche Struktur und fügt Zeiger zum übergeordneten Knoten und den Geschwisterknoten hinzu:
Jede Faser hat eine Verbindung zu ihrem ersten Kind, ihrem nächsten Geschwister und ihrem Elternteil. Mit dieser Datenstruktur können wir die nächste Arbeitseinheit leichter finden. Unter der Annahme, dass
Wir verwenden diese Datenstruktur zur Implementierung einer Faser //Erstellen Sie die anfängliche Wurzelfaser wipRoot = { dom: Behälter, Requisiten: { untergeordnete Elemente: [Element] }, }; führe eine Arbeitseinheit aus (wipRoot); Rufen Sie dann /** * performUnitOfWork wird zum Ausführen von Aufgaben verwendet * @param {fiber} unsere aktuelle Fiber-Aufgabe * @return {fiber} die nächste Fiber-Aufgabe */ const performUnitOfWork = Faser => { if (!fiber.dom) fiber.dom = createDom(fiber); // Erstelle ein DOM, um es einzuhängen const elements = fiber.props.children; // Alle Geschwisterknoten unter dem aktuellen Element // Wenn ein übergeordneter Knoten vorhanden ist, hänge den aktuellen Knoten an den übergeordneten Knoten an if (fiber.return) { faser.return.dom.appendChild(faser.dom); } let vorherigesGeschwister = null; /* Später im Code extrahieren wir hier die Logik*/ wenn (Elemente und Elemente.Länge) { Elemente.fürJedes((Element, Index) => { const neueFiber = { Typ: Element.Typ, Requisiten: Element.Requisiten, Rückkehr: Faser, dom: null, }; // Das untergeordnete Element des übergeordneten Elements zeigt auf das erste untergeordnete Element if (index === 0) { faser.kind = neueFaser; } anders { // Jedes untergeordnete Element hat einen Zeiger auf das nächste untergeordnete Element prevSibling.sibling = newFiber; } vorherigesGeschwister = Faser; }); } // Finde zuerst das untergeordnete Element. Wenn es kein untergeordnetes Element gibt, suche dann das Geschwisterelement. // Wenn es kein Geschwisterelement gibt, kehre zum übergeordneten Element zurück. // Beende es schließlich beim Stammknoten. // Die Durchlaufreihenfolge ist von oben nach unten und von links nach rechts. if (fiber.child) { gibt Faser.Kind zurück; } anders { sei nextFiber = Faser; während (nächste Faser) { wenn (nächsteFiber.geschwister) { gib nextFiber.sibling zurück; } nächsteFiber = nächsteFiber.return; } } } nach Version (abgleichen)aktuelleRoot Bei der Abstimmung handelt es sich eigentlich um eine Diff-Operation des virtuellen DOM-Baums. Dabei wird der Faserbaum vor und nach der Aktualisierung verglichen. Nach Erhalt des Vergleichsergebnisses werden nur die DOM-Knoten aktualisiert, die den geänderten Fasern entsprechen.
Die Variable currentRoot wurde hinzugefügt, um den Faserbaum zu speichern, bevor der Stammknoten aktualisiert wurde, und das alternative Attribut wurde zur Faser hinzugefügt, um den Faserbaum zu speichern, bevor die Faser aktualisiert wurde. let currentRoot = null Funktion rendern (Element, Container) { wipRoot = { // Alternative weglassen: currentRoot } } Funktion commitRoot () { commitWork(wipRoot.child) /* Ändern Sie die Richtung des Faserbaums und ersetzen Sie den Faserbaum im Cache durch den Faserbaum auf der Seite */ aktuelleRoot = wipRoot wipRoot = null }
versöhnenKinder
Beim Vergleich von Faserbäumen
/** * Koordinations-Unterknoten * @param {fiber} fiber * @param {elements} untergeordnete Knoten der Faser */ Funktion reconcileChildren(wipFiber, Elemente) { let index = 0; // Wird verwendet, um den Indexwert von untergeordneten Knoten zu zählen let oldFiber = wipFiber.alternate && wipFiber.alternate.child; // Wird nur beim Aktualisieren generiert let prevSibling; // Vorheriger Geschwisterknoten while (index < elements.length || oldFiber) { /** * Durchlaufen Sie die untergeordneten Knoten* oldFiber bestimmt, ob es sich um einen Update-Trigger oder den ersten Trigger handelt. Wenn das Update ausgelöst wird, sind es alle Knoten unter dem Element*/ lass neueFiber; const element = Elemente[Index]; const sameType = oldFiber && element && element.type == oldFiber.type; // Ob der Fasertyp derselbe ist/** * Beim Aktualisieren* des gleichen Tags mit unterschiedlichen Attributen, aktualisiere die Attribute*/ wenn (gleicherTyp) { neueFaser = { Typ: oldFiber.type, props: element.props, //Nur Eigenschaften aktualisieren dom: oldFiber.dom, Eltern: wipFiber, alternativ: oldFiber, effectTag: "UPDATE", }; } /** * Andere Tags, d.h. Tag ersetzen oder neues Tag erstellen*/ wenn (Element und !gleicherTyp) { neueFaser = { Typ: Element.Typ, Requisiten: Element.Requisiten, dom: null, Eltern: wipFiber, alternativ: null, effectTag: "PLATZIERUNG", }; } /** * Der Knoten wurde gelöscht */ wenn (alteFaser && !gleicherTyp) { oldFiber.effectTag = "LÖSCHEN"; Löschungen.push(alteFiber); } wenn (alte Faser) alte Faser = alte Faser.Geschwister; // Das untergeordnete Element des übergeordneten Elements zeigt auf das erste untergeordnete Element if (index === 0) { // Das erste untergeordnete Element einer Faser ist ihr untergeordnetes Element wipFiber.child = newFiber; } anders { // Die anderen untergeordneten Knoten von Fiber sind Geschwisterknoten seines ersten untergeordneten Knotens prevSibling.sibling = newFiber; } // Weisen Sie die neu erstellte newFiber prevSibling zu, damit das Hinzufügen von Geschwisterknoten für newFiber bequem ist. prevSibling = newFiber; // Indexwert + 1 Index++; } } Beim Commit werden verschiedene Rendering-Vorgänge entsprechend den Eigenschaften des nach Version (Commit) In commitWork wird beurteilt, ob der EffectTag der Faser den eigentlichen DOM-Vorgang handhabt.
/** * @param {fiber} virtuelles Dom der Faserstruktur */ Funktion commitWork(Faser) { wenn (!Faser) zurückgeben; const domParent = fiber.parent.dom; wenn (fiber.effectTag === "PLACEMENT" && fiber.dom != null) { domParent.appendChild(fiber.dom); } sonst wenn (fiber.effectTag === "UPDATE" && fiber.dom != null) { updateDom(fiber.dom, fiber.alternate.props, fiber.props); } sonst wenn (fiber.effectTag === "LÖSCHEN") { domParent.removeChild(fiber.dom); } // Kind- und Geschwisterelemente rekursiv bearbeiten commitWork(fiber.child); commitWork(fiber.sibling); } Konzentrieren wir uns an dieser Stelle auf das, was /* isEvent: Ereignisattribute abrufen isProperty: Nicht-Knoten- und Nicht-Ereignisattribute abrufen isNew: Die Attribute abrufen, die sich vor und nach der Änderung geändert haben*/ const isEvent = key => key.startsWith("on"); const isProperty = Schlüssel => Schlüssel !== "Kinder" und !isEvent(Schlüssel); const isNew = (vorherige, nächste) => Schlüssel => vorherige[Schlüssel] !== nächste[Schlüssel]; /** * DOM-Eigenschaften aktualisieren * @param {dom} fiber dom * @param {prevProps} die alten Eigenschaften auf Fiber Dom * @param {nextProps} die neuen Eigenschaften auf Fiber Dom */ Funktion updateDom(dom, vorherigeProps, nächsteProps) { /** * Praktische alte Attribute* 1. Holen Sie sich die Ereignisattribute, die mit „on“ beginnen* 2. Holen Sie sich die gelöschten Ereignisse* 3. Brechen Sie die Überwachung der gelöschten Ereignisse ab*/ Objekt.Schlüssel(prevProps) .filter(isEvent) .filter(Schlüssel => !(Schlüssel in nextProps)) .fürJeden(Name => { const eventType = name.toLowerCase().substring(2); dom.removeEventListener(eventType, prevProps[name]); }); /** * Praktische alte Attribute* 1. Nicht-Ereignisattribute und Nicht-Knotenattribute abrufen* 2. Gelöschte Attribute abrufen* 3. Attribute löschen*/ Objekt.keys(prevProps) .filter(istEigenschaft) .filter(Schlüssel => !(Schlüssel in nextProps)) .forEach(Schlüssel => dom[Schlüssel] löschen); /** * Praktische neue Attribute * 1. Nicht-Ereignisattribute und Nicht-Knotenattribute abrufen * 2. Vorher und nachher geänderte Attribute abrufen * 3. Attribute hinzufügen */ Objekt.Schlüssel(nextProps) .filter(istEigenschaft) .filter(istNeu(vorherigeProps, nächsteProps)) .fürJeden(Name => { dom[Name] = nextProps[Name]; }); /** * Praktische neue Attribute * 1. Holen Sie sich die Ereignisattribute, die mit „am“ beginnen. * 2. Holen Sie sich die Ereignisattribute, die sich vor und nach „am“ geändert haben. * 3. Fügen Sie einen Listener für die neu hinzugefügten Ereignisattribute hinzu. */ Objekt.Schlüssel(nextProps) .filter(isEvent) .filter(istNeu(vorherigeProps, nächsteProps)) .fürJeden(Name => { const eventType = name.toLowerCase().substring(2); dom.addEventListener(eventType, nextProps[name]); }); } Nachdem wir eine Reihe von Vorgängen am DOM abgeschlossen haben, rendern wir das neu geänderte DOM auf der Seite. Wenn das Eingabeereignis ausgeführt wird, wird die Seite erneut gerendert, aber zu diesem Zeitpunkt wird die Logik zum Aktualisieren des Faserbaums eingegeben. Mission erfüllt! Den vollständigen Code finden Sie auf meinem GitHub. Fazit und Zusammenfassung 💢 abschließend
Zusammenfassen
An dieser Stelle danke ich dir, dass du dir die Zeit genommen hast, diesen Artikel zu lesen. Ich hoffe, er wird dir weiterhelfen. Wenn du Fragen hast, kannst du mich gerne korrigieren. Aus beruflichen Gründen schreibe ich diesen Artikel seit etwa einem Monat mit Unterbrechungen. Ich bin mit einer kleinen Programmtelefonfunktion beschäftigt, die auf 👋: Springe zu GitHub. Gerne kannst du einen Stern vergeben. Vielen Dank an alle. Verweise 🍑: Handschriftserie - Realisieren Sie ein React auf Platin-Niveau 🍑: Bauen Sie Ihr eigenes React (dringend empfohlen) 🍑: Schreiben Sie die Fiber-Architektur von React von Hand und verstehen Sie ihre Prinzipien gründlich 🍑: Schreiben Sie eine einfache React- 🍑: Lehrer Dasheng vom Miaowei-Klassenzimmer hat die Faser- und Hook-Architektur von React geschrieben 🍑: React Fiber-Architektur 🍑: Schreiben Sie eine einfache React- Dies ist das Ende dieses Artikels, in dem wir Ihnen beigebracht haben, wie Sie eine Reaktion aus HTML implementieren. Weitere relevante Inhalte zur Implementierung von Reaktionen in HTML finden Sie in den vorherigen Artikeln von 123WORDPRESS.COM oder in den folgenden verwandten Artikeln. Ich hoffe, dass jeder 123WORDPRESS.COM in Zukunft unterstützen wird! Das könnte Sie auch interessieren:
|
<<: Eine kurze Erläuterung der vier häufig verwendeten Speicher-Engines in MySQL
>>: Lösung für den internen Serverfehler Nginx 500
Beim Erstellen einiger Seiten müssen wir häufig H...
Einführung: Manchmal müssen wir zur Entwicklung e...
Inhaltsverzeichnis Vorwort: 1. Gründe für die Ere...
Der Linux-Befehl „seq“ kann blitzschnell Zahlenli...
Validierung des WeChat-Applets-Formulars. Zu Ihre...
Frontend ist ein harter Job, nicht nur weil sich ...
Vorwort Schauen wir uns zunächst den Endeffekt an...
Vorwort Letzte Woche fragte mich ein Kollege: „Br...
Vorwort Ich habe kürzlich etwas über MySQL-Indize...
Im vorherigen Blogbeitrag ging es um das private ...
ElementUI implementiert das Tutorial zum Paginier...
Inhaltsverzeichnis Asynchrones Durchlaufen Asynch...
Inhaltsverzeichnis 1. Tool-Einführung 2. Arbeitsa...
Installations-Tutorial zur dekomprimierten Versio...
Unter den Anforderungen des heutigen responsiven ...