preface

On December 22, 2020, the React twitter feed released an exciting announcement just in time for the New Year

As 2020 comes to an end we wanted to share a special Holiday Update on our research into zero-bundle-size React Server Components.

React Server Components and zero-bundle-size make me realize that things are not simple, and it may be a brand new attempt. With strong curiosity, I watched the official demonstration video (need scientific online), and the demonstration really excited me! The React Server Components are getting closer to my ideal front-end architecture. Let me take you through the new React Server Components!

What are Server Components?

There may be a lot of people wondering, don’t we already have a Server Side Render solution? Why do we need a server component concept, or what server component solves the pain points that SSR does not have, there are several points

  1. The traditionalServer side renderingIs returnedHTMLAfter the first load, all components are actually rendered on the client side, and nothingSSRAPPIs exactly the same, sort of like a traditional server rendering template, for examplePHP,JSPAnd so on.
  2. In traditional server-side rendering, all components end up in the client’s packaged volume, just like in client-rendered apps.
  3. Traditional server renderers need to go to the server to download the js of the component’s dependencies when the page is first loaded, because without those dependencies, the component can’t run.

These problems can not be said to be server side rendering problems, but the current APP is common, both client side rendering and server side rendering are not immune to these problems.

Server Components provide some ideas for solving these problems. A Server component is a component that exists only on the Server side and does not go to the client side. Let’s look at an example of a server component in the official demo

// NoteList.server.js
/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * */

import {fetch} from 'react-fetch';

import {db} from './db.server';
import SidebarNote from './SidebarNote';

export default function NoteList({searchText}) {
  // const notes = fetch('http://localhost:4000/notes').json();

  // WARNING: This is for demo purposes only.
  // We don't encourage this in real apps. There are far safer ways to access
  // data in a real application!
  const notes = db.query(
    `select * from notes where title ilike $1 order by id desc`[The '%' + searchText + The '%']
  ).rows;

  // Now let's see how the Suspense boundary above lets us not block on this.
  // fetch('http://localhost:4000/sleep/3000');

  return notes.length > 0 ? (
    <ul className="notes-list">
      {notes.map((note) => (
        <li key={note.id}>
          <SidebarNote note={note} />
        </li>
      ))}
    </ul>
  ) : (
    <div className="notes-empty">
      {searchText
        ? `Couldn't find any notes titled "${searchText}".`
        : 'No notes created yet!'}{' '}
    </div>
  );
}
Copy the code

Can see from the code, the server-side components can get data from database fill directly to the component, also can through the interface to get the data, because there is on the server, so I have some advantages from the server, but it is also a component that arguably should be downloaded to the client to render as SSR returns an HTML fragment, that really the case? Let’s start the project and see!

Because it is a server component project, it is inevitable to install the database. The official demo uses the PG database and needs to configure the environment to start

You can see that the final rendering is not a simple HTML fragment, but a serialized piece of component information. Open the console and find the resources on the current client. You can see that our server component is not packaged to the client

Only the client components are packaged.

This is the server component, which itself does not occupy the client’s resources, but only returns to the client for rendering when needed. This rendering is completely independent. React Maintains a server’s React Component tree that is consistent with the client’s React Component Tree. This allows complete local rendering. However, the js rendered locally is not on the client itself. Instead, they are placed on the Server, which is why the Server Components are zero-bundle-size, because they themselves are not downloaded to the client, and only perform the component logic on the Server side.

What are the features of Server Components?

Totally zero – bundle – size

As mentioned earlier, the server component is placed on the server and not downloaded to the client, so it does not take up the size of the package. Moreover, the size of the third party dependency package is also reduced to 0 and will not be sent to the client

// NoteWithMarkdown.js
// NOTE: *before* Server Components

import marked from 'marked'; / / 35.9 K (gzipped 11.2 K)
import sanitizeHtml from 'sanitize-html'; / / 206 K (gzipped 63.3 K)

function NoteWithMarkdown({text}) {
  const html = sanitizeHtml(marked(text));
  return (/* render */);
}
Copy the code

If it becomes a server component

// NoteWithMarkdown.server.js - Server Component === zero bundle size

import marked from 'marked'; // zero bundle size
import sanitizeHtml from 'sanitize-html'; // zero bundle size

function NoteWithMarkdown({text}) {
  // same as before
}
Copy the code

Perfect integration with the back-end environment

Because server components run on the server side, they have back-end capabilities, such as reading and writing files

// Note.server.js - Server Component
import fs from 'react-fs';

function Note({id}) {
  const note = JSON.parse(fs.readFile(`${id}.json`));
  return <NoteWithMarkdown note={note} />;
}
Copy the code

Or just pull the data from the database

// Note.server.js - Server Component
import db from 'db.server';

function Note({id}) {
  const note = db.notes.get(id);
  return <NoteWithMarkdown note={note} />;
}
Copy the code

The call interface also responds faster because it is closer to the back end.

Automatic Code Splitting

Code Splitting is a great way for daily development to improve application performance. It can be used to load components asynchronously. Normal React applications are as follows

// PhotoRenderer.js
// NOTE: *before* Server Components

import React from 'react';

// one of these will start loading *when rendered on the client*:
const OldPhotoRenderer = React.lazy(() = > import('./OldPhotoRenderer.js'));
const NewPhotoRenderer = React.lazy(() = > import('./NewPhotoRenderer.js'));

function Photo(props) {
  // Switch on feature flags, logged in/out, type of content, etc:
  if (FeatureFlags.useNewPhotoRenderer) {
    return <NewPhotoRenderer {. props} / >; 
  } else {
    return <OldPhotoRenderer {. props} / >; }}Copy the code

This is the normal way to write it, but it causes some problems:

  1. Developers must remember to useReact.lazyAnd the dynamicimportTo replace regular import statements with increased mental load.
  2. This approach increases the time it takes for the application to start loading the selected components, negating the benefit of asynchronously loading less code.

To address the first problem, the server component defaults to treating all incoming client components as components that need to be split without writing additional code.

For the second problem, because it is on the server side, it allows the client to download the split asynchronous component earlier.

Server component written:

// PhotoRenderer.server.js - Server Component

import React from 'react';

// one of these will start loading *once rendered and streamed to the client*:
import OldPhotoRenderer from './OldPhotoRenderer.client.js';
import NewPhotoRenderer from './NewPhotoRenderer.client.js';

function Photo(props) {
  // Switch on feature flags, logged in/out, type of content, etc:
  if (FeatureFlags.useNewPhotoRenderer) {
    return <NewPhotoRenderer {. props} / >;
  } else {
    return <OldPhotoRenderer {. props} / >; }}Copy the code

Reduce the frequency of interface calls

Common client components need to frequently call the interface to fill the data in the component, because the delay of the interface will affect the UI rendering. At the same time, sometimes only a part of the data is needed to call the interface, but the interface will go to the full query database, resulting in data redundancy.

Server-side components move the logic of retrieving data to the server side and directly manipulate the database or call the interface, achieving better page performance and reducing the frequency of interface calls.

// Note.server.js - Server Component

function Note(props) {
  // NOTE: loads *during* render, w low-latency data access on the server
  const note = db.notes.get(props.id);
  if (note == null) {
    // handle missing note
  }
  return (/* render note here... * /);
}
Copy the code

Reduce the cost of function abstraction

React is functional programming is not a template type (such as a vue), resulting in component page structure is not clear as template programming, too many abstract code will let developers maintenance difficulties, the server-side components to avoid the negative elements of the abstract, package multiple components of server-side components will eventually return to a single element to the client, In this example, React will only return div renderers to the client

// Note.server.js
/ /... imports...

function Note({id}) {
  const note = db.notes.get(id);
  return <NoteWithMarkdown note={note} />;
}

// NoteWithMarkdown.server.js
/ /... imports...

function NoteWithMarkdown({note}) {
  const html = sanitizeHtml(marked(note.text));
  return <div . />;
}

// client sees:<div> <! -- markdown output here --> </div>Copy the code

Problems caused by Server Components

With all that said about the benefits of Server Components, is Server Components really a reasonable solution right now?

I’m still in the negative.

The so-called everything has two sides, while bringing benefits will certainly have some disadvantages, it depends on the disadvantages and benefits brought by which one is more important.

This increases the server pressure and operation and maintenance costs

Server Components brings an obvious disadvantage, which increases the pressure and operation and maintenance cost of the Server. After the user volume comes up, it will become more and more obvious. It is inevitable to consider that some computing power is put into the Server.

Server components do not support state management

Because each server component only requests data once, it has no state management, no lifecycle, and no React hooks.

Server-side components cannot use the Dom Api

Server rendered components really don’t use the browser Api.

Client components cannot share server component logic

Client components cannot share server logic due to differences in rendering environments. However, the official concept of shared components is also put forward. Shared components do not have the suffix of. Client and.

Increased mental burden

With the addition of server-side components, it is not pure that we have to consider whether the component is to be rendered on the server side or the client side all the time when writing the code. When writing server-side components, we also need to consider which APIS can be used and which cannot be used, which reduces the development efficiency.

conclusion

At present, Server Components is still in the research stage, and it will take some time before it can be put into production environment. The proposal of the concept of Server Components provides more options for the development direction of the front-end in the future. Front-end development is always in the process of trial and error

Passing by a praise 👍 is the biggest encouragement to the author, you can also pay attention to the author’s public number [front-end can be really cool] the first time to get wonderful original good article! See you next time