Was ist der Unterschied zwischen Hydrat () und Render () in Reaktion 16?

79

Ich habe die Dokumentation gelesen, aber den Unterschied zwischen hydrate()und render()in React 16 nicht wirklich verstanden .

Ich weiß, hydrate()wird verwendet, um SSR und clientseitiges Rendering zu kombinieren.

Kann jemand erklären, was feuchtigkeitsspendend ist und was dann der Unterschied in ReactDOM ist?

Shabenda
quelle
wenn Sie unten durch @tophar gegebene Antwort richtig ist, es erkunden wollen mehr können Sie dieses lesen reactjs.org/docs/react-dom.html
Gorakh Nath

Antworten:

75

Aus den ReactDOMServer- Dokumenten (Schwerpunkt Mine):

Wenn Sie ReactDOM.hydrate()einen Knoten aufrufen, auf dem dieses vom Server gerenderte Markup bereits vorhanden ist, behält React es bei und hängt nur Ereignishandler an , sodass Sie beim ersten Laden eine sehr performante Erfahrung machen können.

Der fett gedruckte Text ist der Hauptunterschied. renderkann Ihren Knoten ändern, wenn zwischen dem anfänglichen DOM und dem aktuellen DOM ein Unterschied besteht.hydratehängt nur Ereignishandler an.

Aus dem Github-Problem, das hydrateals separate API eingeführt wurde :

Wenn dies Ihr erstes DOM ist:

<div id="container">
    <div class="spinner">Loading...</div>
</div>

und dann anrufen:

ReactDOM.render(
   <div class="myapp">
      <span>App</span>
   </div>,
   document.getElementById('container')
)

beabsichtigt, nur clientseitig zu rendern (keine Flüssigkeitszufuhr). Dann endest du mit

<div id="container">
   <div class="spinner">
       <span>App</span>
   </div>
</div>

Weil wir die Attribute nicht patchen.

Nur zu Ihrer Information, der Grund, warum sie die Attribute nicht gepatcht haben, ist

... Dies würde im normalen Hydratationsmodus sehr langsam hydratisieren und das anfängliche Rendern in einen Nicht-SSR-Baum verlangsamen.

topher
quelle
7
Ich verstehe nicht, warum das gerenderte Div kein Div mit dem Klassennamen myapp hat und warum es dort eine Spinner-Klasse im endgültig gerenderten Element gibt
Pravin Poudel
@pravinpoudel Ich denke, das liegt daran, dass die Attribute beim clientseitigen Rendern nicht gepatcht werden. Deshalb class="spinner"bleibt das Attribut so, wie es im <div>Element ist.
Glenn Mohammad
25

Hydrat wird grundsätzlich bei SSR (Server Side Rendering) verwendet. SSR gibt Ihnen das Skelett- oder HTML-Markup, das von einem Server gesendet wird, damit Ihre Seite beim Laden zum ersten Mal nicht leer ist und Suchmaschinen-Bots sie für SEO indizieren können (ein Anwendungsfall von SSR). Hydrat fügt also das JS Ihrer Seite oder einem Knoten hinzu, auf den SSR angewendet wird. Damit Ihre Seite auf die vom Benutzer durchgeführten Ereignisse reagiert.

Render wird zum Rendern der Komponente im clientseitigen Browser verwendet. Wenn Sie versuchen, das Hydrat durch Render zu ersetzen, wird eine Warnung angezeigt, dass das Rendern veraltet ist und bei SSR nicht verwendet werden kann. es wurde entfernt, weil es im Vergleich zu Hydrat langsam war.

Sumit Kapoor
quelle
21

Zusätzlich zu oben ...

ReactDOM.hydrate()ist dasselbe wie render(), wird jedoch verwendet, um einen Container zu hydratisieren (Ereignis-Listener anzuhängen), dessen HTML-Inhalt von ReactDOMServer gerendert wurde. React versucht, Ereignis-Listener an das vorhandene Markup anzuhängen .

Die Verwendung von ReactDOM.render () zur Hydratisierung eines vom Server gerenderten Containers ist aufgrund der Langsamkeit veraltet und wird in React 17 entfernt. Verwenden Sie hydrate()stattdessen.

Devinder Suthwal
quelle
19

Ich habe nichts Spezielles zu dem hinzuzufügen, was oben über die Verwendung von gesagt wurde hydrate , aber beim Versuch, etwas darüber zu lernen, habe ich ein kleines Beispiel zusammengestellt. Hier ist die Arbeit für alle, die es hilfreich finden.

Tor

Servieren Sie zwei Seiten, eine, die verwendet, ReactDOM.hydrateund eine, die verwendet ReactDOM.render. Sie hängen von einigen in JSX geschriebenen Reaktionskomponenten ab, die durch <script>Tags geladen werden und eine künstliche Verzögerung (vom Server) aufweisen, um den Unterschied zwischen hydrateund zu veranschaulichen render.

Grundstruktur

  1. Eine Datei mit dem HTML-Skelett
  2. Eine Datei mit den in JSX geschriebenen benutzerdefinierten React-Komponenten
  3. Ein Skript, das alle Seiten generiert, die der Server verwenden kann
  4. Ein Skript zum Ausführen des Servers

Ergebnisse

Nachdem ich die Seiten generiert und den Server ausgeführt habe, gehe ich zu 127.0.0.1und werde mit dem Header- Hydrat , einer Schaltfläche und zwei Links angezeigt. Ich kann auf die Schaltfläche klicken, aber es passiert nichts. Nach einigen Augenblicken ist das Laden des Dokuments abgeschlossen und die Schaltfläche beginnt, meine Klicks zu zählen. Dann klicke ich auf den Link "Rendern". Jetzt hat die Seite, die mir präsentiert wird, den Header- Render und zwei Links, aber keine Schaltfläche. Nach wenigen Augenblicken erscheint die Schaltfläche und reagiert sofort.

Erläuterung

Auf der Seite "Hydrat" wird das gesamte Markup sofort gerendert, da alle erforderlichen HTML-Dateien mit der Seite bereitgestellt werden. Die Schaltfläche reagiert nicht, da noch keine Rückrufe verbunden sind. Sobald der components.jsLadevorgang abgeschlossen ist, wird das loadEreignis ausgelöst windowund die Rückrufe werden mit verbunden hydrate.

Auf der Seite "Rendern" wird das Schaltflächen-Markup nicht mit der Seite bereitgestellt, sondern nur von eingefügt ReactDOM.render, sodass es nicht sofort sichtbar ist. Beachten Sie, wie sich das Erscheinungsbild der Seite durch das endgültige Laden des Skripts grundlegend ändert.

Quelle

Hier ist die benutzerdefinierte Reaktionskomponente, die ich verwende. Es wird vom Server im Knoten verwendet, um auf statisch gerenderte Komponenten zu reagieren, und es wird auch dynamisch vom Server zur Verwendung in Seiten geladen (dies ist der Zweck der Suche nach exportsund ReactObjekten am Anfang der Datei).

// components.jsx

var exports = typeof(exports) == 'object' ? exports : {};
var React = typeof(React) == 'object' ? React : require('react');

function MyButton(props) {
  [click, setClick] = React.useState(0);
  function handleClick() { setClick(click + 1); }
  return (
    <button onClick={handleClick}>Clicked: {click}</button>
  );
}

exports.MyButton = MyButton;

Dies ist das Skript, mit dem alle für den Server erforderlichen Seiten generiert werden. Zuerst wird babel verwendet, um components.jsx in Javascript zu transpilieren, dann werden diese Komponenten zusammen mit React und ReactDOMServer verwendet, um die eigentlichen Seiten zu erstellen. Diese Seiten werden mit der Funktion erstellt, getPagedie aus der als pageTemplate.jsnächstes gezeigten Datei exportiert wird .

// genScript.js

let babel          = require('@babel/core');
let fs             = require('fs');
let ReactDOMServer = require('react-dom/server');
let React          = require('react');
let pageTemplate   = require('./pageTemplate.js');

script = babel.transformFileSync(
  'components.jsx', 
  {presets : [['@babel/react']]}
);

fs.writeFileSync('components.js',script.code);
let components = require('./components.js');

hydrateHTML = pageTemplate.getPage(
  'MyButton',
  ReactDOMServer.renderToString(React.createElement(components.MyButton)),
  'hydrate'
);

renderHTML = pageTemplate.getPage(
  'MyButton',
  '',
  'render'
);

fs.writeFileSync('hydrate.html',hydrateHTML);
fs.writeFileSync('render.html',renderHTML);

Diese Datei exportiert nur die getPagezuvor erwähnte Funktion.

// pageTemplate.js

exports.getPage = function(
  reactElementTag,
  reactElementString,
  reactDOMMethod
  ) {
  return `
  <!DOCTYPE html>
  <html>
    <head>
      <meta charset="utf-8" />
      <script crossorigin src="https://unpkg.com/react@16/umd/react.development.js" defer></script>
      <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" defer></script>
      <script src="./components.js" defer></script>
    </head>
    <body> 
      <h1>${ reactDOMMethod }</h1>
      <div id="react-root">${ reactElementString }</div> 
      <a href="hydrate.html">hydrate</a>
      <a href="render.html">render</a>
    </body>
    <script>
      window.addEventListener('load', (e) => {
        ReactDOM.${ reactDOMMethod }(
          React.createElement(${ reactElementTag }),
          document.getElementById('react-root')
        );
      });
    </script>
  </html>
  `;
}

Endlich der eigentliche Server

// server.js

let http = require('http');
let fs   = require('fs');

let renderPage       = fs.readFileSync('render.html');
let hydratePage      = fs.readFileSync('hydrate.html');
let componentsSource = fs.readFileSync('components.js');

http.createServer((req, res) => {
  if (req.url == '/components.js') {
    // artificial delay
    setTimeout(() => {
    res.setHeader('Content-Type','text/javascript');
    res.end(componentsSource);
    }, 2000);
  } else if (req.url == '/render.html') {
    res.end(renderPage);
  } else {
    res.end(hydratePage);
  }
}).listen(80,'127.0.0.1');
Nathan Chappell
quelle
2
Beeindruckend. Sie haben im Grunde die Frage beantwortet + erklärt, wie man einen minimalen statischen Generator wie Gatsby baut. Tolle. Vielen Dank!
Sergey Lukin
17

Der gesamte Prozess des Zurücksetzens von Funktionen in den HTML-Code, der bereits in serverseitigem React gerendert wurde, wird als Hydratation bezeichnet.

Der Vorgang des erneuten Renderns über den einmal gerenderten HTML-Code wird daher als Hydratation bezeichnet.

Wenn wir also versuchen, unsere Anwendung durch Aufrufen zu hydratisieren ReactDOM.render(), soll dies durch Aufrufen geschehen ReactDOM.hydrate().

Daniel
quelle
0

Beim Rendern wird alles im angegebenen Element (in den meisten Fällen als "root" bezeichnet) gelöscht und neu erstellt, während beim Hydrat alles, was sich bereits im angegebenen Element befindet, beibehalten und daraus erstellt wird, wodurch das Laden der ersten Seite beschleunigt wird.

alvin lal
quelle