0
votes

ReactJS S̶i̶n̶g̶l̶e̶ Shelled Page Application

  1. No server side rendering (SSR), requirements to reproduce the issue:
    ReactJS + WebPack + React-Router
    I used Yeoman generator for aspnet core React project without SSR, disabled HotReloading in Startup.cs (or could run Production mode)

    <Route component={ Layout }>
       <Route path='/' components={{ body: Home }} />
       <Route path='/counter' components={{ body: Counter }} />      
    </Route>;
    /* project includes Home.tsx, Counter.tsx and other components/subpages */
    

    Part of Home.tsx:

    class Home extends React.Component<any, void> {
      public render() {
        return <div>
            <h1>Hello, world!</h1>
            <p>Welcome to your new single-page application, built with:</p>
    

    Result

    • main-client.js includes all content/layout for Home and Counter component (subpage), and all content and layout for other subpages.
      It means that we are loading the whole layout of the application even if we had requested only one subcategory, e.g. Counter
      After stopping the website you still can switch between subpages as they are loaded via webpacked main-client.js.

      enter image description here

      Question 1: Is it possible to load only what you requested for a current page (application shell + current subcategory)?
      (then for another section only one new subcategory due cached app shell)
      (perhaps with another Router and special (web-)pack settings)

  2. Server side rendering (SSR), requirements to reproduce the issue:
    ReactJS + WebPack + React-Router + SSR
    I used Yeoman generator for aspnet core React project with Redux and SSR, also disabled HotReloading in Startup.cs (or could run Production mode)

    Result

    • Now even worse as we are loading whole web site layout twice: at first in the fully rendered html and then inside main-client.js

    This shows that for React by default there is no difference between static content (static text inside control) and dynamic content (some conditional html output), thus it puts all information inside huge JS file.

    Question 2: Is it possible to give a hint for React about static part, so there is only need to render it once by a server and eliminate it from client java-script leaving only dynamic computations/components?

========================================================

It seams that not so many people are aware of it, because these issues are inside many samples and they are not mentioned in tutorials.

One of the common approach nowadays is loading an empty application shell layout at first, and then via client javascript load JSON data for content. However it does not resolve these issues, for example for the first problem we are still loading a shell for the hole app, and for the rich layout it can be a really huge JS-file with a lot of empty DOM elements:

<h1 id="react-hint-for-element-9997">/* here will be header*/</h1>
    <h2 id="react-hint-for-element-9998">/* here will be subheader*/</h2>
        /*and so on and on */
1
You can address Q1 with webpack code splitting. I believe Q2 is the motivation for inferno. - joews
@joews Nice, I haven't heard about inferno project, you can put the info to a proper answer - Artur A

1 Answers

1
votes

Question 1

You can break your app into several bundle with Webpack code splitting.

It provides require.ensure to lazily load bundles when they are needed (e.g. when the user hits a new route):

Example (from Webpack docs):

//static imports
import _ from 'lodash'

// dynamic imports
require.ensure([], function(require) {
  let contacts = require('./contacts')
})

Question 2

React doesn't (yet) differentiate between static and dynamic content. Inferno is a React-like library that makes this distinction to improve rendering performance. It may be more suitable than React for your use case.

Example (from Inferno docs):

import Inferno from 'inferno';
import InfernoDOM from 'inferno-dom';

const message = "Hello world";

InfernoDOM.render(
  <MyComponent message={ message } />,
  document.getElementById("app")
)