Server-side rendering revealed

preface

Still worry about my brother’s study, I heard a: SSR! Sister I SSR!

Me:??

Elementary school students can SSR? Let me wake up.

Oh no, that’s it

You must have heard of SSG and SSR as you are busy with coding. Let’s reveal what it is.

So what is SSR? Super Super Rare?

SSGThe full nameStatic-Site GeneratorStatic site generation, listen to the name is static, generated in the construction of the time, then want to update the site content to rebuild, this is suitable for the enterprise official website or personal blog, no frequent update of the appeal. The advantages of thinking about the obvious, speed (evenapiNot sure soon), easy to deploy (just throw static files to work), safe (pure static is notsqlInjection)..SSRThe full nameServer-side rendering (SSR), server rendering,

The advantages of CSR are obvious, and the disadvantages are obvious (this is no nonsense). Even if you do dynamic import, split chunk, you are bound to bundle. It is not uncommon for a large application that has been doing this for 3 years to have a bundle size of 1m. The browser builds the HTML for the first time and gets nothing. It has to wait for the JS to execute before dynamically rendering and changing the DOM tree. During this time, it also requests the API to retrieve the data and render it to the page and finish displaying it. Because the initial HTML provided by the server (for any page) does not contain any application-specific HTML, the search engine treats the site as blank with no content. So even though your site has a lot of traffic or related content, it may not show up at the top of the search results.

I know in my mind that SSR is fast, but what is fast? If you execute js on the server, the HTML can be assembled on the server side, returned to the browser to render, and the page can have a preliminary display, but there is no Window on the server side to bind, so you have to execute the script again on the client side, execute the lifecycle method, and bind the event. Diff the DOM. Until this event is complete, the server and client will execute a set of code called isomorphism. In the react case, the HTML will not be rerendered.

Problems to be solved

Isomorphic rendering is a set of code for the server and client, with the server rendering and the client interaction.

Route isomorphism and data prefetch

The client uses BrowserRouter and the server uses staticRouter. There is no history object on the node side, it just returns a matching React. CreateElement based on the requested route. The matchRoutes method implements the prefetch of route matching data. When a route is declared, the data request method is associated with the route. For example, a loadData method is assigned.

// routes.ts
const routes = [
  {
    path: "/".component: loadable(() = > import("./Com")),
    loadData: () = > getData(),
  },
];
const loadData = () = > {
  const promises: Promise<unknown>[] = [];
  routes.some((route) = > {
    const match = matchPath(ctx.request.path, route);
    // Call the method defined to get the data
    if (match && route.loadData) promises.push(route.loadData());
    return match;
  });
  return Promise.all(promises).then(() = > {
    return Promise.resolve(
      <StaticRouter>
        <App />
      </StaticRouter>
    );
  });
};

// Write prefetched data to HTML (EJS)
Copy the code

Dynamic loading and resource acquisition

The main reason to use the loadable library is to get the resource mapping, when the route matches to the key to get the value of the resource and plug it into the HTML return string to the client. In addition, dynamic import can be used with React. Lazy on the client, but not before 18. If the React. Lazy main JS is loaded and executed, the bundle of the corresponding page can be loaded. @loadable/webpack-plugin can print out the map of the resource map and give it to ChunkExtractor. The idea is to match the route first. According to the matched routes, the corresponding mapping resources are obtained and the resources are loaded

/ / webpack configuration
const LoadablePlugin = require('@loadable/webpack-plugin');

module.exports = {
  module: {
    rules: []},plugins: [
    newLoadablePlugin(), ... ] };// Resource mapping
const statsFile = path.resolve(__dirname, '.. /dist/asset/loadable-stats.json'); // The above loadaer types this name by default.const extractor = new ChunkExtractor({
  statsFile,
  publicPath: '/'});// extractor.getLinkTags(), extractor.getStyleTags(), ...
Copy the code

Render isomorphism

React. Hydrate: The client executes the lifecycle method, and the HTML is not rerendered. Diff the client and server HTML nodes, and the attributes on the HTML are not replaced when the results are inconsistent. The child node will be replaced with a different one, and a warning will be thrown pointing to the error node, which needs to be handled manually.

Data isomorphism

From the data level, putting data into the Window is called water injection, and taking data out of the Window is called dehydration, which is difficult to understand. The simple understanding is that the server writes the data into the EJS template as a global variable. The server takes this variable from the Window and implements the isomorphism of the data.

/ / / rendux data
/ / ejs template
<body>
  <div id="root"><%- html %></div>
  <script type="text/javascript">
    window.REDUX_PRELOAD_DATA = <%- preloadState %>
  </script>
  <%- reload -%>
  <%- scriptTags %>
</body>

// server/app.ts
ejs.renderFile(
    template,
    {
      ...
      // Write the preloadState variable to the EJS template
      preloadState: JSON.stringify(ctx.store.getState()),
      ...
    },
    {},
    (err, str) = >{... }); });Copy the code

Get the path, key (routing path) value (resources required by the page)

Loadable How to know the page path?

const jsx = extractor.collectChunks(reactApp); Loadable (() => import(“)) is equivalent to consumer.

Wipe the napkin, let’s go, go, go, go wu, go Tiao.

Performance monitoring

Monitor nodeJS V8 heap memory, degrade services when memory exceeds 80%, and deploy unhealthy containers to other instances within a time frame (maybe half an hour, depending on the business). Process.memoryusage () returns an object describing the memoryUsage, in bytes, of the node.js process.

import { memoryUsage } from "process";

console.log(memoryUsage());
/ / print:
/ / {
// rss: 4935680,
// heapTotal: 1826816,
// heapUsed: 650472,
// external: 49879,
// arrayBuffers: 9386
// }
Copy the code
  • heapTotalheapUsedRefers to the memory usage of V8.
  • externalRefers to those that are bound to V8 managementJavaScriptThe object’sC++Object memory usage.
  • rss.Resident Set SizeIs the amount of space occupied by the process on the main memory device (that is, a subset of the total allocated memory), including allC++JavaScriptObject and code.
  • arrayBuffersRefers to asArrayBufferSharedArrayBufferAllocated memory, including all Node.js buffers. This is also included inexternalIn the value. When Node.js is used as an embedded library, this value may be 0, as there may be no trace in this caseArrayBufferThe distribution of.

Throw it all away and let’s use next-.js

Despite a bunch of principles mentioned above, the general-grade SSR solution is still not enough as an enterprise-level solution. There is a large amount of code in nex.js to deal with various compatibility problems. As a react project with a large volume, it is more recommended to choose a general-grade solution.

Hybrid rendering

In our actual business, we often need SSR for some pages and CSR for the rest. For our own simple SSR, we can configure the whitelist, do the matching request, in the whitelist of the empty HTML string (only CSS, JS resources); Or do interception in nGINx layer, whitelist forward to the CSR address. Next. Js provides a much simpler way for this hybrid rendering by providing a statically generated API with getStaticProps, where requests are requested at build time and written to the Window. GetServerSideProps is an API that can only be executed on the server, so getServerSideProps will not be exported during static generation. Here is the statically generated code.

export async function getStaticProps() {
  // Call an external API endpoint to get posts.
  // You can use any data fetching library
  const res = await fetch("https://... /posts");
  const posts = await res.json();

  // By returning { props: { posts } }, the Blog component
  // will receive `posts` as a prop at build time
  return {
    props: {
      posts,
    },
  };
}
Copy the code

Service degradation

innodeWhen the server is unhealthy, it reaches the millisecond levelCSRThat is, there is no need to rely on our own built framework, can takeserverclientPack them separately. Here you goclientPackage output plusHTMLFile, can be hosted separately. Below isnginxConfiguration sample and elegant degradation schematic, explained below is the user requestnginxIf the server is normal, it will be forwarded tonodeRender server, if the exception returns the exception status code, intercept the exception status code and rewrite200And forwarded to theHTMLStatic file server.

Natural elegant service degradation

Since SSG comes with it, then our service degradation can take its static generation, the official provide next export command, can directly generate SSG output, each route out of an HTML file, which will introduce the required CSS and JS. Note that if you want a set of code, you need to plant the environment variables, because the SSG requirement is that you must not expose getServerSideProps. The compatible handling code is as follows

// .sh
export NEXT_SSG = SSG
// .tsx
let getServerSideProps =
  process.env.NEXT_SSG === "SSG"
    ? undefined
    : async() = > {const res = await fetch(`url`);
        const post = await res.json();
        return { props: { name: post? .data? .token } }; };export { getServerSideProps };
Copy the code

Throw the printed output to nginx as follows

server { root /www/data; location / { try_files $uri ; }}Copy the code

If you get the HTML from the return path, the client gets the HTML string and then hydrate, which is actually a CSR, achieving an elegant degradation without going to the Node server.

Built-in performance analysis

For web application performance analysis, it is always difficult to get around the metrics of Web Vitals. Next. Js Analytics provides us with a very convenient API to obtain these metrics data. For projects deployed on Vercel hosting, visual metrics data can be seen in their Analytics TAB. Web performance analysis is available and possible for self-managed projects. Simply create an _app.js that exposes a method called reportWebVitals.

Next. Js calls this function when any metric is evaluated.

//_app.js
export function reportWebVitals(metric) {
  console.log(metric);
}
/ / print
/ / {
// id: "1628518848412-9295257969280",
// label: "web-vital",
// name: "TTFB",
// startTime: 0,
/ / value: 815.5,
// }
Copy the code
  • id: Unique identifier for the indicator;
  • label: Indicates the indicator typeweb-vitalsandcustom;
  • name: Index name;
  • startTime: Indicates the time stamp of the indicator in milliseconds.
  • value: Indicates the value or duration of an indicator in milliseconds.

web-Vitals

Google is a unified measure of the user experience and quality of web pages. Next. Js provides us with the following five metrics:

  • First byte timeTTFB
  • First content drawingFCP
  • Measuring loading performanceLCP
  • Measure interactivityFID
  • Measuring visual stabilityCLS

custom

This is a unique indicator provided by Nex.js to measure the hydrate and Render time

  • Next.js-hydration: Page start and finishhydrateTime required in milliseconds
  • Next.js-route-change-to-render: The time (in milliseconds) between the time the page started rendering after the routing change
  • Next.js-render: The time (in milliseconds) before the page is rendered after the routing change

Using this function, you can create your own performance report.

other

Dynamic loading, such as dynamic routing matching next10 have support, need to document oh ~ www.nextjs.cn/docs/gettin…

In this case, select Serverless

Serverless is basically a simple sequence repeat (SSR) application in a function, serverless has triggers, you use HTTP triggers, A route can be added to the trigger, and the received route is passed to nex.js, which then returns HTML to the client.

ServerlessWhat problem does it solve?

Serverless Allows applications to be o&M free on the server. When there is no flow, the volume is reduced to 0 to save the flow. We can save a lot of money. It is a cost-effective solution. The use of Serverless+SSR is perfect for landing pages that may increase traffic in certain situations, as well as edge services. What is o&M free? If a service is deployed in the running environment provided by the service provider, we do not need to care about o&m related things, only need to care about business code, and we do not need to maintain Linux such as physical machines and virtual machines. Each major cloud service vendor has a packaged Next. Js service, which can be quickly deployed by simple operation.

Have need to be able to search on their own oh, do not post ~

conclusion

Introduces what SSR and SSG are and what problems they solve, how they work and how they monitor performance, how common level SSR framework Nextjs does elegant service degradation, and how the nextJS framework is still being updated and released a few days ago. Module federation is supported, and the micro front end can also use Nextjs (manual dog head).

One day

I am Xuan Jiang, I am lo niang FE (encourage teacher to cross off), a passing friend gave me three even ba ~ your support is the motivation of Xuan Jiang’s creation (I was touched by what you said), I will keep updating ~