preface

This article introduces two methods for loading React applications: Server Side Rendering (SSR) and Code Split.

This article provides two REPOs:

  • Code Splitting

    Describes how to split bundles and lazy load.

    Gitlab.com/yafeya/reac…

  • Compression

    Describes how to compress bundles to further optimize load speed.

    Gitlab.com/yafeya/reac…

1. SSR

Because the React application is a client-side application and SPA, the page will be rendered after the bundle.js file is loaded, which introduces a problem. If the entire bundle file is too large, the page rendering speed will slow down.

Server-side loading was introduced to speed up the first screen loading, not the entire application. The idea of server-side loading is that the HTML code for the first screen is loaded by the server, so the user requests the HTML through the browser, the server generates the entire HTML DOM, sends it to the browser, the browser takes care of rendering, and then the React take-over logic again.

The general process is shown below (the picture is quoted from the Introduction and Principles of React SSR isomorphism) :

1.1 SSRThe advantage of

  • The first screen loading speed is fast

    Because the entire HTML DOM is generated by the server, the page can be displayed regardless of whether the bundle file has been downloaded.

  • SEO Friendly

    You can individually access any URL in the React Routing.

  • SSR Demo

    React SSR isomorphism introduction and Principles, this article to learn about the principles of SSR, and write a model of isomorphism.

1.2 SSRThe disadvantage of

  • Complex code structure
    • The need to introduce a server-side framework such asexpressorkoa
    • It needs to be completely customizedwebpack, packed separatelyserverwithclientServer-side code
    • becauseserverClient is notdomObject that needs refactoringcss.clientRemove allcss referenceInstead,serverThe load
  • Different Codebase

    I personally understand that if SSR is needed, it should be planned at the beginning of the project, otherwise it will be a very painful process to start using Client side rendering and then migrate to server side rendering. And the process is irreversible.

    Just removing all the CSS refernCE would have a huge impact on the entire project. So a framework like Next-js or Gatsby is recommended.

1.3 Solution

  • Using frame generation

    As mentioned above, it is recommended to use a framework like Next. Js or Gatsby if SSR is used. After all, standing on the shoulders of giants will save a lot of unnecessary work.

  • Use Client Side Rendering

    The other solution is to still use Client Side Rendering, but it needs to solve the problems of loading speed and SEO friendliness.

    The loading speed problem can be partially solved with code Splitting +Compression.

    The purpose of Code Splitting is to put different Components into different bundles and load the corresponding bundle files when the Components are displayed.

    Compression refers to the Compression algorithm used to compress bundle files to reduce the amount of network transmission.

    SEO Friendly issues, which we described earlier, can be solved by customize Webpack, and you can check out this article if you’re interested.

2. Code Splitting + Compression

2.1 Code Splitting

According to the React bundle principle, synchronized code is placed in one bundle, while asynchronous code is placed in different bundles. Let’s look at the following code:

// The bundle will then be placed in the same bundle
import { add } from './math';

console.log(add(16.26));
Copy the code
// The bundle will then be placed in two bundles
import("./math").then(math= > {
 console.log(math.add(16.26));
});
Copy the code
  • lazy load component

    Suppose we want to lazy load ComponentB in ComponentA, we need to implement the following code:

    // ComponentA.tsx
    const ComponentA: React.FC = () = > {
     return <div>Component A</div>
    }
    
    export default ComponentA;
    Copy the code
    // ComponentB.tsx
    import { Suspense, lazy } from 'react';
     
     // assume that ComponentA.tsx & ComponentB.tsx are in the same folder.
     const ComponentA = lazy(() = > import('./ComponentA'));
    
     const ComponentB: React.FC = () = > {
       return (
         <div>
           <Suspense fallback={<div>Loading...</div>} ><ComponentA />
           </Suspense>
         </div>
       );
    }
    Copy the code
    • ComponentAbedefault exported
    • inComponentBIn theComponentAbelazy importBecause theimportWill return apromiseObject, so according toReactthebundleThe principle,ComponentAandComponentBWill bebundleTo a different file.
    • callComponentAthelazyObject, must be usedSuspendThe node wraps around the needlazy loadtheComponent.
  • Code Splitting based on Routing

    In fact, the main application scenario for Code Splitting is React Routing. In Routing scenarios, not all components are loaded on the first screen. Therefore, it is a good choice to route to the corresponding Component and then load the corresponding bundle of the Component.

    Code Splitting based on React Routing. Demo repo: gitlab.com/yafeya/reac…

    // Router.tsx
    
    const TranslationWrapper = lazy(() = > import(".. /i18n/translation"));
    const ItemsWrapper = lazy(() = > import(".. /ItemList/ItemList"));
    
    export const Router = () = > {
        return (
             <BrowserRouter>
                 <Suspense fallback={<div>loading...</div>} ><Switch>
                         <Route exact={true} path="/" component={Home} />
                         <Route path="/redux" component={Redux} />
                         <Route path="/items" component={ItemsWrapper} />
                         <Route path="/item/:id" component={ItemDetailWrapper} />
                         <Route path="/i18n" component={TranslationWrapper} />
                         <Route component={()= > <Redirect to="/" />} / ></Switch>
                 </Suspense>
             </BrowserRouter>
        );
    }
    Copy the code

    In this Demo, we split the previous translation and items components into different bundles and lazy load the route.

    • File loading list with no Code Splitting

    • Made a file loading list for Code Splitting

      As you can see, resources is noticeably missing 15K because two component bundles are not loaded on the first page.

    • Running effect

      As you can see, the bundle is loaded only when the corresponding Component is clicked.

2.2 Compression

Compression refers to the Compression of bundle files to reduce transmission network bandwidth and optimize loading speed. There are two types of Compression, one for the build phase and another for the run phase. Since compression at runtime is very dependent on the implementation of the Web-server you are running, it is not very general, so I will only cover compression at the build stage.

2.2.1 Uglify bundle file

Before introducing compression, let’s first introduce the essential step, namely uglify of code, which is usually to confuse variable and function names of JS code for code protection.

npm i -D react-app-rewire-uglifyjs
Copy the code
// config-overrides.js
module.exports = {
   webpack: function (config, env) {
       / /...
       const rewireUglifyjs = require('react-app-rewire-uglifyjs');
       config = rewireUglifyjs(config);
       / /...
   },
   // ...
}
Copy the code

Running effect

All CSS and JS files are uglified.

2.2.2 Compression in build procedure

  • Install compression will package

    react-app-rewire-compression-plugin
    Copy the code
  • Customize Webpack

    // config-overrides.js
    module.exports = {
        webpack: function (config, env) {
            / /...
            const rewireCompressionPlugin = require('react-app-rewire-compression-plugin');
            config = rewireCompressionPlugin(config, env, {
                test: /\.js$|\.css$|\.html$/,
                cache: true.threshold: 10240.minRatio: 0.8
            });
            / /...
      },
      // ...
    }
    Copy the code
  • The build results

    After build, you can see that there are many gz files generated in the static directory.

2.2.3 Consume gzip file via Nginx

After generating the gz file above, you need to modify the Configuration of Nginx so that the web-server can use the generated gz file.

server {
   #...
   gzip on;
   gzip_static on;    
   gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
   gzip_proxied  any;
   gzip_vary on;
   gzip_comp_level 6;
   gzip_buffers 16 8k;
   gzip_http_version 1.1;
   #...
}
Copy the code
# Publish website
./docker-exec
Copy the code

Operation effect:

You can see that the original 450K resrouces only transmitted 132K over the network, which is still relatively efficient. The loading time was also reduced by nearly 1s.

Demo Repo for Compression: gitlab.com/yafeya/reac…