Skip to main content

Exploration of Frontend Rendering Patterns

Free2020-06-14#Front-End#SSR vs Prerendering#预渲染与SSR#服务端渲染#前后端混合渲染#Universal Rendering#Rehydration

What do CSR, SSR, Prerendering, and Rehydration mean?

Preface

Under the banner of modern frontend frameworks like React and Vue, CSR (Client-Side Rendering) mode has become deeply ingrained:

CSR (Client-Side Rendering) - rendering an app in a browser, generally using the DOM.

Almost all frontend parts are dynamically rendered by the client (client executes JS code, dynamically creates DOM structure), for example:

<!DOCTYPE html>
<html>
  <head>
    <title>My Awesome Web App</title>
    <meta charset="utf-8">
  </head>
  <body>
    <div id="app"></div>
    <script src="bundle.js"></script>
  </body>
</html>

The heavier the client logic, the more JS needs to be executed during initialization, and the slower the first screen performance becomes, thus more rendering mode explorations have emerged:

  • SSR (Server-Side Rendering): Server-side rendering, rendering web applications into HTML on the server

  • Rehydration: Secondary rendering, reusing server-rendered HTML DOM structure and data, "warm starting" JS rendering on the client

  • Prerendering: Pre-rendering, running a client application at compile time to capture its initial state and generate static HTML

I. CSR

CSR (Client-side rendering), i.e., client-side rendering, refers to rendering pages directly in the browser using JS, all logic including data requests, view templates, and routing are handled on the client:

Client-side rendering (CSR) means rendering pages directly in the browser using JavaScript. All logic, data fetching, templating and routing are handled on the client rather than the server.

The rendering process is shown in the figure below:

P.S. FCP and TTI appearing in it are two important performance metrics:

  • FCP (First Contentful Paint): The point in time when the content requested by the user is visible on the screen

  • TTI (Time To Interactive): The point in time when the page is interactive

The main defect lies in that as the application updates and iterates, the amount of JS code the client needs to execute increases, and third-party libraries/frameworks, polyfills, etc. will slow down first screen performance to a certain extent, especially obvious on (mid-to-low-end) mobile devices

Optimization measures like Code splitting, lazy-load can alleviate some of it, but optimization space is relatively limited, not helping to solve the problem fundamentally

At this point, only changing the rendering mode can create more possibilities

II. SSR

SSR (Server-Side Rendering) is not a novel concept; for a long period before frontend-backend separation, server-side rendering was dominant (JSP, PHP), generating complete HTML pages on the server:

Server rendering generates the full HTML for a page on the server in response to navigation.

It saves the network overhead of client's secondary data requests and the performance burden of rendering view templates. Compared to CSR, its FCP and TTI are usually faster:

P.S. On the other hand, the server's network environment is better than the client's, and communication paths between internal servers are also shorter

Because page logic (including real-time data requests) and template rendering work are completed on the server, reducing the client's JS code volume, browser optimization mechanisms like streaming document parsing can also play their role, performing better on low-end devices and weak network conditions. But generating pages on the server also takes time, which will cause page content response time (TTFB, Time to First Byte) to become slower

One approach is to further optimize through streaming SSR, component-level caching, templatization, HTML caching and other technologies

Another approach is to continue exploring rendering modes, adopting Static Rendering

III. Static Rendering

Move the work of generating HTML pages to compile time, rather than dynamically completing when requests come. Pre-generate HTML files separately for each URL, and further leverage CDN to accelerate access:

Static rendering happens at build-time and offers a fast First Paint, First Contentful Paint and Time To Interactive - assuming the amount of client-side JS is limited. Unlike Server Rendering, it also manages to achieve a consistently fast Time To First Byte, since the HTML for a page doesn't have to be generated on the fly.

The rendering process is shown in the figure below:

P.S. SSR's first part Server Rendering work becomes Streaming to deliver static HTML files

Static rendering is not perfect either; its key issue lies in "static":

  • Need to generate a separate HTML file for each URL: For websites that cannot predict all possible URLs, or have a large number of different pages, static rendering is not so easy, or even not feasible at all

  • Only suitable for relatively static content: Not very useful for dynamic, personalized content

Additionally, there's a concept similar to static rendering, called Prerendering

Prerendering

The main difference is that pages obtained through static rendering are already interactive, no need to execute a large amount of JS code on the client additionally, while prerendered pages must go through client rendering to become truly interactive:

Static rendered pages are interactive without the need to execute much client-side JS, whereas prerendering improves the First Paint or First Contentful Paint of a Single Page Application that must be booted on the client in order for pages to be truly interactive.

That is to say, after disabling JS, static rendered pages are almost unaffected, while prerendered pages will only have basic functions like hyperlinks left

IV. Rehydration

Rehydration mode combines CSR with SSR, after server renders basic content, performs secondary rendering (Rehydration) on the client:

Often referred to as Universal Rendering or simply "SSR", this approach attempts to smooth over the trade-offs between Client-Side Rendering and Server Rendering by doing both. Navigation requests like full page loads or reloads are handled by a server that renders the application to HTML, then the JavaScript and data used for rendering is embedded into the resulting document.

For example:

The actual rendering process is as follows:

Note that bundle.js is still full CSR code, and the page becomes truly interactive only after these codes are executed. Therefore, in this mode, although FP (First Paint) is improved, TTI (Time To Interactive) may become slower, because before the client's secondary rendering is complete, the page cannot respond to user input (blocked by JS code execution)

For the problem of interaction unresponsiveness caused by secondary rendering, possible optimization directions are incremental rendering (such as React Fiber), and progressive rendering/partial rendering

Trisomorphic Rendering

If [Service Worker](/articles/理解 web-workers/) is also considered, there's another rendering mode involving three parties:

SSR + CSR + ServiceWorker rendering = Trisomorphic Rendering

As shown below:

First render the initial page through streaming SSR, then let Service Worker render target HTML pages with the help of SSR according to routing rules:

It's a technique where you can use streaming server rendering for initial/non-JS navigations, and then have your service worker take on rendering of HTML for navigations after it has been installed. This can keep cached components and templates up to date and enables SPA-style navigations for rendering new views in the same session.

The main advantage lies in being able to share template rendering and routing control logic across three parties:

This approach works best when you can share the same templating and routing code between the server, client page, and service worker.

V. Summary

Each rendering mode has certain advantages, as well as its limitations and shortcomings. In actual scenarios, trade-offs need to be made among various factors:

References

Comments

No comments yet. Be the first to share your thoughts.

Leave a comment