webpackcode-splittinghyperapp

Hyperapp / Webpack - How to code split component


To reduce the initial load of my app, i try to do code splitting. For exemple, i successfully split twilio-video, to load and initialize when user press "call"

import('twilio-video').then(Video => {
    Video.connect(twilioInfo.data.token, options).then(room => {
        ...
    });
});

Webpack correctly split the code and load it when needed.

Now i would like to do the same with components, or routes (to not load user profil while we stand on login page). Is there any equivalent of

const OtherComponent = React.lazy(() => import('./OtherComponent'));

or

const Home = lazy(() => import('./routes/Home'));

or any other way to load component when a route path is hitted?


Solution

  • It used to be very difficult. These days, Webpack Module Federation makes it a breathe.

    They have tons of samples to get you started. For example, the basic-host-remote sample has a simple scenario that fits your need. Just fetch the entire Github repo, cd into this sample, and run it (as explained in each sample's README).

    Keep an eye open for the Network tab. Components are not loaded until they are explicitely requested.

    Basic Example

    The basic-host-remote sample has two separate packages: app1 and app2.

    1. app1 wants to use components from app2
    2. app2 provides a Button component
    3. app1/src/App.js loads the button dynamically. (It does so during initial render, making the on-demand loading a lot less obvious.)
    4. When the time comes, app1 requests a component using dynamic import.
    5. It wraps the load using React.lazy, like so:
      const RemoteButton = React.lazy(() => import("app2/Button"));
      
      • E.g., you can do this in a useEffect, or a Route.render callback etc.
    6. app1 can use that Button, once it's loaded. While loading, it shows a loading message, using Suspense:
      <React.Suspense fallback={<LoadingScreen />}>
        <RemoteButton />
      </React.Suspense>
      
      • Alternatively, instead of using lazy and Suspense, you can just take the promise returned from the import(...) statement and handle the asynchronous loading any way you prefer. Of course, WMF is not at all restricted to react and can load any module dynamically.
      • Note that both, app1 and app2 have the same shared setup, making sure that those shared dependencies are only loaded one time, and not bundled/duplicated with remotely loaded code:
      {
        // ...
        shared: { react: { singleton: true }, "react-dom": { singleton: true } },
      }
      

    Note that WMF dynamic loading must use dynamic import (i.e. import(...)), because:

    1. non-dynamic imports will always resolve at load time (thus making it a non-dynamic dependency), and
    2. "dynamic require" cannot be bundled by webpack since browsers have no concept of commonjs (unless you use some hacks, in which case, you will lose the relevant "loading promise").

    WMF Samples

    WMF takes a bit of learning, but all its samples have the following common elements:

    1. Every sample has multiple independent packages.
    2. Each package has its own webpack.config.js
    3. Each package usually acts as a host (which provides components dynamically) or a consumer of such components, or both.
    4. In development mode, you usually serve up everything using the nicely integrated webpack-dev-server.
      • NOTE: In production mode, you want to make some small adjustments to your config. When webpack-dev-server is out of the picture, your build just adds some additional remoteEntry.js files to your build output.
    5. You can easily configure what to do with common dependencies, using the shared configuration. (Usually you want them to act as singletons, meaning, everyone should share common dependencies, and not pack their own.)
    6. If you want to insist on keeping everything in one package, that is probably also possible. Just expose it and add its own (independent) components to its own remotes. Not sure if it works, but its worth trying.

    Final Words

    Again, it will take a bit of a learning curve, but I find it's definitely worth it. Feels like one of the most sophisticated dynamic build + load systems I have seen.

    Most annoyingly, WMF still does not currently have proper API documentation on the Webpack page, but I'm sure it'll come soon enough. Currently, there is only a not all too polished collection of conceptual notes.

    The WMF author himself promises to provide better documentation soon™️.

    Luckily, for learning, the samples are really good, actively maintained and still being enhanced.