On paper come zhongjue shallow, we will achieve a simple server rendering process, intended to realize the dividend brought by SSR

React state management and isomorphism

A couple of important concepts

The implementation of SSR relies on the ReactDomServer object provided by React

It mainly provides renderToString() and renderToStaticMarkup() methods that can only be used on the server side

renderToString()/renderToStaticMarkup()

Method of use: ReactDomServer. RenderTostring (element)/ReactDomServer renderToStaticMarkup (element)

Thing in common:

  • Both receive a React Element and convert it to an HTML string, which is returned by the browser. This enables the server to insert the page concatenation string into the HTML document and return it to the browser for preliminary server rendering

The difference between

  • RenderToString is present in each Dom node of the HTML string generated by React 15data-react-idProperty, the root node will have onedata-react-checkSumattribute
  • RenderToStaticMarkup withoutdata-react-checkSumThe property browser must rerender the component when rendering

About the data – the react – checkSum:

If two components have the same props and Dom structure, this value will be the same. We know that when the server rendering the page is sad, the browser will render the component to perform interaction, etc. The browser will generate the component's data-react-checksum value and compare it to the server rendering component's value. If equal, the rendering is not repeatedCopy the code

And here’s a sketch that gives you an idea of what happens here.

ReactDom.hydrate()

After React 16, renderToString components no longer have the data-react-* attribute. Therefore, the browser rendering method cannot be determined by data-react-checksum to determine whether to re-render

ReactDom provides a new API against this background ReactDom.hydrate() is used with render() to render components on the browser side

React is, of course, backward-compatible, and it’s still fine to use Render () on the browser side when rendering components, but it should be a better mode for both future and performance reasons

renderToNodeStream()/renderToStaticNodeStream()

React 16 provides these two methods to optimize initial page loading and shorten TTFB time

concept

This method continues to generate child throttles that return the HTML string that will eventually be returned as a stream from the Readable Stream so that the server is transmitting the data to the browser in real time while processing the content rather than returning the results only once

RenderToStaticNodeStream also does not generate a data-react-* property for renderToNodeStream, which can be used for static pages.

Possible problems in actual development

  1. There is no browser environment on the server that supports component mounting, so the React component onlycomponentDidMountThe previous lifecycle methods are valid, so browser features cannot be used in the previous lifecycle methods, such asWindow, localStorage.
  2. Double side may have pull data requirements, so in order to achieve the reuse of the code, a kind of typical approach is to put the logic of the request data to React the component of a static method Request method and double side sharing, double end inconsistent problems can be through the server and the browser judgment to encapsulate it Such as according to window is browser specific objects

React 16 surprises in server-side rendering

There are also mixed said in front, in this summary

  • The browser rendering component needs to work with the serverhydratemethods
  • providesstreamMode interface
  • Similar to the new browser feature, except that it can handleReact ElementIt can also handle other types, for examplestring number
  • Because it’s done away with in the return Domdata-react-checksum, so the server generates HTML more efficiently
  • Allows non-standard Dom attributes to be added to the render Dom

To test this, implement a small chestnut based on Node.js

Express4.15.3 carries out server-side processing

  • Browser: browser-side rendering
  • Server: server logic
  • Share: isomorphic part

Operation effect:

share/app.js

import React, { Component } from "react";
import logo from "./logo.svg";
import "./App.css";

class App extends Component {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
  }
  handleClick() {
    alert('I'm triggered hot')}render() {
    return (
      <div className="App">
        <div className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <h2>Welcome to React in the Server</h2>
        </div>
        <p className="App-intro"</p> <button onClick={e => this.handleclick ()}> I am a button </button> </div>); }}export default App;
Copy the code

browser/index.js

import React from "react";
import { hydrate } from "react-dom";
import App from ".. /shared/App";

hydrate(<App />, document.getElementById("root"));

Copy the code

server/index.js

import express from "express";
import React from "react";
import { renderToString } from "react-dom/server";
import App from ".. /shared/App";

const app = express();

app.use(express.static("public"));

app.get("*", (req, res) => { const htmlMarkup = renderToString(<App />); res.send(` <! DOCTYPE html> <head> <title>Universal Reacl</title> <link rel="stylesheet" href="/css/main.css">
        <script src="/bundle.js" defer></script>
      </head>

      <body>
        <div id="root">${htmlMarkup}</div>
      </body>
    </html>
  `);
});

app.listen(process.env.PORT || 3000, () => {
  console.log("Server is listening");
});
Copy the code

Server side:

The string generated using renderToString is sent to the browser using res.send

The client side:

The Dom node with id root is the result returned from the server, using react.hydrate to handle the logic on the browser side

Suppose a client render still uses render()

test

import React from "react";
import {render } from "react-dom";
import App from ".. /shared/App";

render(<App />, document.getElementById("root"));

Copy the code

The result is that it is ok because of backward compatibility, but with the following warning ⚠️

Conclusion Use new features as much as possible

What happens if hypothesis two relies entirely on server-side rendering

After the browser/index.js code is annotated out, the page is displayed normally, but clicking the button does not pop up. The conclusion requires both ends to complete the display and interaction of the page

React 16 renderToNodeStream

Test changes to server/index.js


import express from "express";
import React from "react";
import { renderToNodeStream } from "react-dom/server";
import App from ".. /shared/App";

const app = express();

app.use(express.static("public"));

app.get("*", (req, res) => { res.write(` <! DOCTYPE html> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8">
        <title>Universal Reacl</title>
        <link rel="stylesheet" href="/css/main.css">
        <script src="/bundle.js" defer></script>
      </head>`
  );
  res.write("<div id='root'>"); 
  const stream = renderToNodeStream(<App/>);
  stream.pipe(res, { end: false });
  stream.on('end', () => {
    res.write("</div></body></html>");
    res.end();
  });
});
Copy the code

Note: In order to return a stream, use the res.write method instead of res.end

The advantage of using renderToString page TTFB time

Use renderToNodeStream page TTFB time

Conclusion Progressive stream rendering can shorten the server response time to the maximum extent, so that the browser can receive information faster

Assume a triisomorphic application versus browser rendering advantage

Browser rendering:

Isomorphic application:

Assume that triple Render 16 is more efficient than Render 15

React 15

legacy

  1. In view of therenderToNodeStream()/renderToStaticNodeStream()withrenderToString()/renderToStaticMarkup()React 16 didn’t existdata-react-*What’s the difference between the two sides?
  2. React 16 How to do dual end comparison after react 16? Official says it’s based onReactDom.hydrate()withrenderToString()Combined with judgment.. A face of meng