Bringen Sie Ihnen bei, wie Sie eine Reaktion aus HTML implementieren

Bringen Sie Ihnen bei, wie Sie eine Reaktion aus HTML implementieren

Was ist React

React 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,快速響應. Die neue Version von React 16.8 bringt eine neue fiber mit, um die Probleme zu lösen, die mit der schnellen Reaktion von Webseiten verbunden sind, nämlich den Engpass der CPU. Das herkömmliche Surfen im Internet wird durch Faktoren wie die Aktualisierungsrate des Browsers und die lange Ausführungszeit von JS eingeschränkt, was zu Seitenrahmenverlusten und sogar zum Einfrieren führen kann.

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 <div></div> hinzu und fügen Sie dann ein Skript-Tag hinzu. Da wir für den modularen Aufbau import verwenden müssen, müssen wir dem Skript das Attribut vom Typ module hinzufügen.

<!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 Live Server Plugin zu installieren, das uns beim Debuggen des Codes hilft und auch bei den folgenden Vorgängen verwendet wird.

JavaScript

Wir werden die folgende Reaktion imitieren, um eine grundlegende Operation zu implementieren, das Ereignis an <input/> zu binden und den Eingabewert in den Tag <h2/> einzufügen:

...
Funktion App() {
  zurückkehren (
    <div>
      <input onInput={updateValue} Wert={Wert} />
      <h2>Hallo {value}</h2>
      <hr />
    </div>
  );
}
... 

Wenn React mit Babel kompiliert wird, wird JSX Syntax in React.createElement() umgewandelt. Der obige Retuen-Code wird umgewandelt in

...
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:

  • Typ, Knotentyp
  • config, Attribute auf dem Knoten, wie ID und href
  • Kinder, untergeordnete Elemente. Es kann mehrere untergeordnete Elemente geben. Der Typ kann einfacher Text oder React.createElement sein. Wenn es React.createElement ist, ist es tatsächlich ein untergeordneter Knoten, und unter dem untergeordneten Knoten können untergeordnete Knoten vorhanden sein. Auf diese Weise wird die verschachtelte Beziehung von React.createElement verwendet, um die Baumstruktur von HTML-Knoten zu implementieren.

Wir können ein createElement schreiben, das dieselbe Funktion in Form von React.createElement erreichen kann, um jsx über eine einfache Datenstruktur anzuzeigen, nämlich虛擬DOM Auf diese Weise kann beim Aktualisieren der Vergleich zwischen neuen und alten Knoten auch in einen Vergleich des virtuellen DOM umgewandelt werden.

{
  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

  • Das Prinzip besteht darin, alle Parameter an ein Objekt zurückzugeben
  • Kinder sollten auch in Requisiten eingefügt werden, damit wir die untergeordneten Elemente über props.children in der Komponente erhalten können
  • Wenn die untergeordnete Komponente ein Textknoten ist, wird sie durch die Konstruktion eines Knotentyps vom Typ TEXT_ELEMENT dargestellt.
/**
 * 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 createElement können wir das virtuelle DOM erhalten, aber wir benötigen noch render , um den Code auf der Seite darzustellen. Zu diesem Zeitpunkt müssen wir index.js verarbeiten, Eingabeereignisse hinzufügen, createElement und render durch Import einführen, beim Rendern das kompilierte virtuelle DOM und root Stammelement der Seite übergeben und schließlich executeRender aufrufen. Die Seite wird gerendert. Wenn die Seite aktualisiert ist, rufen Sie executeRender erneut auf, um zu aktualisieren und zu rendern.

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

render Renderfunktion hilft uns, dem realen Knoten ein Element hinzuzufügen. Zunächst akzeptiert sie zwei Parameter:

Die Stammkomponente ist tatsächlich eine JSX-Komponente, ein virtueller DOM, der von createElement zurückgegeben wird.

Der übergeordnete Knoten ist der Ort, an dem wir diesen virtuellen DOM rendern möchten

Vor React 16.8 erfolgte das Rendering in den folgenden Schritten:

  • Erstellen Sie einen DOM-Knoten vom Typ „element.type“ und fügen Sie ihn dem Stammelement hinzu (Sonderbehandlung für Textknoten).
  • Fügen Sie die Eigenschaften des Elements zum entsprechenden DOM hinzu, behandeln Sie das Ereignis speziell und hängen Sie es in das Dokument ein (react17 passt es so an, dass es in den Container eingehängt wird).
  • Fügen Sie dem DOM-Knoten eine element.children-Schleife hinzu;

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.

  • Ermöglicht die Unterbrechung von Rendering-Arbeiten. Wenn ein Job mit höherer Priorität eingefügt wird, wird das Browser-Rendering vorübergehend unterbrochen. Nachdem der Job abgeschlossen ist, wird das Browser-Rendering fortgesetzt.
  • Teilen Sie die Rendering-Arbeit in kleine Einheiten auf;

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:

  • child bezieht sich auf die untergeordnete Komponente
  • Geschwister verweist auf Geschwisterkomponenten
  • return zeigt auf die übergeordnete Komponente

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 A ein an der Wurzel hängender Knoten ist, ist die Rendering-Reihenfolge der Faser ebenfalls wie folgt:

  • Suchen Sie ausgehend von der Wurzel den ersten untergeordneten Knoten A.
  • Suchen Sie den ersten untergeordneten Knoten B von A
  • Suchen Sie den ersten untergeordneten Knoten E von B
  • Suchen Sie den ersten untergeordneten Knoten von E. Wenn kein untergeordneter Knoten vorhanden ist, suchen Sie den nächsten Geschwisterknoten. Suchen Sie den Geschwisterknoten F von E.
  • Suchen Sie den ersten untergeordneten Knoten von F. Wenn kein untergeordneter Knoten und kein Geschwisterknoten vorhanden ist, suchen Sie den nächsten Geschwisterknoten seines übergeordneten Knotens und suchen Sie den Geschwisterknoten C des übergeordneten Knotens von F.
  • Suchen Sie den ersten untergeordneten Knoten von C. Er kann nicht gefunden werden. Suchen Sie den Geschwisterknoten D.
  • Suchen Sie den ersten untergeordneten Knoten von D, G
  • Wir suchen nach dem ersten Kindknoten von G, können ihn aber nicht finden. Wir suchen nach seinem Geschwisterknoten, können ihn aber nicht finden. Wir suchen nach dem Geschwisterknoten seines Elternknotens D, können ihn aber auch nicht finden. Wir suchen weiter nach dem Geschwisterknoten des Elternknotens von D und finden schließlich die Wurzel.
  • Der Stammknoten wurde im vorherigen Schritt gefunden und das Rendering ist abgeschlossen.

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 auf, um den gesamten Faserbaum von oben nach unten zu konstruieren

/**
 * 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.

  • Löschen Sie unnötige Knoten
  • Geänderte Knoten aktualisieren
  • Einen neuen Knoten hinzufügen

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
}
  • Wenn die neuen und alten Knotentypen identisch sind, verwenden Sie das alte Knoten-DOM erneut und aktualisieren Sie die Eigenschaften.
  • Wenn die Typen unterschiedlich sind und der neue Knoten vorhanden ist, erstellen Sie einen neuen Knoten, um den alten Knoten zu ersetzen
  • Wenn die Typen unterschiedlich sind, gibt es keinen neuen Knoten, aber einen alten Knoten. Löschen Sie den alten Knoten.

versöhnenKinder

  • Extrahieren Sie die Logik zum Erstellen neuer Fasern in performUnitOfWork in die Funktion reconcileChildren.
  • Vergleichen Sie die neuen und alten Fasern in reconcileChildren;

Beim Vergleich von Faserbäumen

  • Wenn die neuen und alten Fasertypen identisch sind, behalten Sie das DOM bei,僅更新props,設置effectTag 為UPDATE .
  • Wenn die neuen und alten Fasertypen unterschiedlich sind und neue Elemente vorhanden sind創建一個新的dom 節點,設置effectTag 為PLACEMENT .
  • Wenn die neuen und alten Fasertypen unterschiedlich sind und die alte Faser vorhanden ist刪除舊fiber,設置effectTag 為DELETION
/**
 * 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 effectTag auf dem Faserknoten ausgeführt.

nach Version (Commit)

In commitWork wird beurteilt, ob der EffectTag der Faser den eigentlichen DOM-Vorgang handhabt.

  • Wenn der EffectTag einer Faser PLACEMENT ist, bedeutet dies, dass eine neue Faser hinzugefügt und der Knoten dem übergeordneten Knoten hinzugefügt wird.
  • Wenn der EffectTag einer Faser DELETION ist, bedeutet dies, dass die Faser und der Knoten des übergeordneten Knotens gelöscht werden.
  • Wenn der EffectTag einer Faser UPDATE ist, bedeutet dies, dass die Faser und die Requisiteneigenschaften aktualisiert werden.
/**
 * @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 updateDom passiert. Wir holen die neuen und alten Attribute, die im DOM geändert wurden, und führen Operationen aus

/*
    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.
Alternativ verweist es auf den vorherigen Glasfaserknoten zur Wiederverwendung und führt Aktualisierungsvorgänge schneller aus, wie in der Abbildung dargestellt:

Mission erfüllt!

Den vollständigen Code finden Sie auf meinem GitHub.

Fazit und Zusammenfassung 💢

abschließend

  1. Der von uns geschriebene JSX-Code wurde von Babel in React.createElement konvertiert.
  2. React.createElement gibt tatsächlich die virtuelle DOM-Struktur zurück.
  3. Die Abstimmung und Darstellung des virtuellen DOM kann einfach und grob rekursiv erfolgen, dieser Prozess ist jedoch synchron. Wenn zu viele Knoten verarbeitet werden müssen, können Benutzereingaben und die Wiedergabe von Animationen blockiert werden, was zu Verzögerungen führt.
  4. Fiber ist eine neue Funktion, die in 16.x eingeführt wurde und dazu dient, synchrone Koordination in asynchrone umzuwandeln.
  5. Fiber transformiert die Struktur des virtuellen DOM mit Zeigern wie „Eltern->erstes Kind“, „Kind->Bruder“ und „Kind->Eltern“. Mit diesen Zeigern können von jedem Fiber-Knoten aus andere Knoten gefunden werden.
  6. Fiber teilt die synchronen Aufgaben des gesamten Baums in asynchrone Ausführungsstrukturen auf, die jeder Knoten unabhängig ausführen kann.
  7. Die Durchquerung von Fiber kann von jedem beliebigen Knoten aus beginnen. Die Durchquerung erfolgt in der Reihenfolge Eltern->Kind->Bruder->Eltern, also von oben nach unten und von links nach rechts.
  8. Die Abstimmungsphase einer Faser kann eine kleine asynchrone Aufgabe sein, die Commit-Phase muss jedoch synchron sein. Denn bei asynchronen Commits kann es sein, dass Benutzer sehen, wie Knoten nacheinander erscheinen, was eine schlechte Erfahrung ist.

Zusammenfassen

  • Implementierung von React-Hooks ✖
  • Synthetische Ereignisse reagieren ✖
  • Es gibt noch viele Dinge, die nicht realisiert wurden 😤...

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騰訊云TRTC + websocket basiert. Ich werde es in einen Artikel schreiben, um es zu teilen, wenn ich Zeit habe. Natürlich wird die Implementierung von React fortgesetzt.

👋: 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:
  • Zwei Möglichkeiten zur Verwendung von React in React HTML
  • So entfernen Sie HTML-Tags in Rich Text und Filtern in Vue-, React- und WeChat-Applets
  • Wissen Sie, wie Sie React in HTML-Seiten verwenden?

<<:  Eine kurze Erläuterung der vier häufig verwendeten Speicher-Engines in MySQL

>>:  Lösung für den internen Serverfehler Nginx 500

Artikel empfehlen

So legen Sie mit CSS eine Hintergrundunschärfe fest

Beim Erstellen einiger Seiten müssen wir häufig H...

Wie melde ich mich per Remote-Zugriff bei der MySql-Datenbank an?

Einführung: Manchmal müssen wir zur Entwicklung e...

Analyse des Ereignisschleifenmechanismus von JavaScript

Inhaltsverzeichnis Vorwort: 1. Gründe für die Ere...

WeChat-Applet implementiert Formularüberprüfung

Validierung des WeChat-Applets-Formulars. Zu Ihre...

4 Lösungen für CSS-Browserkompatibilitätsprobleme

Frontend ist ein harter Job, nicht nur weil sich ...

Grundlegendes Tutorial zum WeChat-Miniprogramm: Verwendung von Echart

Vorwort Schauen wir uns zunächst den Endeffekt an...

Der Unterschied zwischen Löschen, Abschneiden und Löschen und wie man wählt

Vorwort Letzte Woche fragte mich ein Kollege: „Br...

Beispielcode für das MySQL-Indexprinzip ganz links

Vorwort Ich habe kürzlich etwas über MySQL-Indize...

Beispiel zum Erstellen eines öffentlichen Harbor-Repository mit Docker

Im vorherigen Blogbeitrag ging es um das private ...

Detaillierte Erklärung der neuen Funktionen von ES9: Asynchrone Iteration

Inhaltsverzeichnis Asynchrones Durchlaufen Asynch...