Mike Labriola presented “Building Pieces, Assembling Applications” to the Chicago React meetup on September 30th, 2020. If you want to learn more about how to split apart your application and reassemble it for lazy loading, code splitting, and better performance, the recording is available on YouTube.
Mike structured the presentation as a practical code walkthrough demonstrating the techniques and outcomes. Further, Mike made sure to inject both a healthy dose of pragmatism and each technique’s strategic implications. At 2:54, Mike discusses when you shouldn’t worry about splitting up your apps and when you should.
If it turns out you should split your app, there are two techniques on display in this presentation, Bundle Splitting and Code Splitting. Bundle splitting, like the name implies, means splitting your build into several different bundles instead of one big one for all of your source code. These bundles are still all loaded up front, but each bundle is more cacheable as updates to the source code typically only affect one (or at most, a few) of the bundles, allowing the untouched bundles to be served from cache. Code splitting involves loading application source code dynamically. Users download the code the need to perform their current actions. Only a portion of the application source code is sent to the user. Due to the smaller payload, the application loads faster.
- Separation based on the frequency of code changes
- Advantage grows for static code
- Code delivered to the application user
- Separation based on order or frequency of use
- Advantage grows with branching paths
- Code delivered to the application user, plus parsed and executed
Webpack is one of the tools to use for splitting and bundling. At 12:17, Mike shows Webpack Bundle Analyzer, a visual tool that produces an interactive treemap of application files.
Producing a Bundle
Mike then walks through a naive bundling of his demonstration application and shows the single file created by the Webpack process using the default configuration. Mike then analyzes the single file with Webpack Bundle Analyzer. The file’s internal composition is the same as before bundling.
Benefits of producing a single file:
- The user receives the source code in a single file.
- One single file loads faster than many files of the same aggregate weight.
Drawbacks of producing a single file
- Any change to the source code means shipping the entire single file again.
Moving Common Files To A Bundle
At 18:08, Mike shows how to split code into sections by splitting out the node_modules directory contents. This area is less likely to change when compared to our application source code. When the Webpack configuration is adjusted to bundle out node_modules, we end up with two files, main.js, and vendors~main.bundle.js. The user will download two files. As we enhance our application and add to main.js, the user can keep their bundled copy of vendors~main.bundle.js and not download it again.
Benefits of bundling out node_modules
- The user can receive new main.js files without downloading all of the node_module code.
Drawbacks of bundling out node_modules
- The user has more files to download (negligible).
- We’ve introduced a complexity facet (also negligible)
Bundling out more files
Bundling strategy can be more involved than just lumping everything in node_modules into a single bundle. Pragmatism and reason should drive your decision making, but to highlight the point, at 22:26, Mike then shows how to make a bundle per node_module dependency. This configuration change results in many bundles and would be useful if you expected one or more of the bundles to change regularly, and others to be comparatively static.
React.lazy is like a factory object and lets you render a dynamic import as a regular component. The lazy component should then be rendered inside a Suspense component, which allows us to show fallback content (such as a loading indicator) while the user is waiting for the lazy component to load. This combination offers the fastest user experience by giving the user something to look at and interact with while waiting on more expensive functionality to load.
Mike then offers some helpful advice on structuring lazy-loaded components for the best performance and debugging experience. There are many user paths that a user may not take immediately in many complex applications, making each a candidate for lazy loading.
React Loadable Components
An additional code-splitting technique React application developers have at their disposal is using React Loadable Components. At 45:40, Mike discusses how React Loadable Components work when used inside React applications and considerations on which options work best under the different scenarios. Loadable components will work on the server-side, making them more useful than React.lazy/Suspense for application architectures with server-side rendering. Loadable Component will use React.lazy/Suspense where possible and provide similar functionality in environments where React.lazy/Suspense is not possible.
Software developers looking to offer their users an excellent user experience must know how their application source code reaches the user. There are a variety of strategies that could make sense for your application, depending on the specifics.
Bundling all of the application source code into a single file makes it easy and efficient for the user to download all of the application source code and have it available for use. For some applications, this is the right solution. Other application architectures deserve an intricate combination of bundling and code splitting. Each point on the spectrum offers distinct advantages with accompanying tradeoffs.
By watching this presentation by Mike, you will have a better handle on which set of techniques will provide an optimized experience for your application users. If you have questions, add them to our comments section below.