The Case for Performance
In digital, every second matters. In fact, Mobify found that only a half second variance in page load times can significantly impact conversion rates: an impact that can easily equate to hundreds of thousands of dollars in lost revenue. If a mobile user has to wait more than three seconds for a page to load, there’s a fifty-fifty chance they’ve lost patience and abandoned the effort to load your page entirely.
These findings aside, it’s self-evident that page load speed and responsiveness are absolutely critical to providing an experience that drives deeper engagement and leaves the end-user coming back for more. However, it’s a difficult endeavor to design and develop eCommerce sites that provide a feature-packed and content-rich experience while also staying mindful to performance. How can we continue to focus on implementing complex features, integrating advanced front-end services, and provide high-quality site aesthetics without sacrificing speed and convenience?
Why Webpack Helps
The speed at which the browser is able to load, process, and render any page on the web today is dependent on the amount and size of the resources that are returned to its initial request.
To launch robust, content-rich experiences that also render and load quickly, merchants need a build system that generates optimized code for the browsers’ processing capabilities, intelligently splits code across multiple files to take advantage of modern networking protocols, and effectively identifies and eliminates unused code. Webpack steps in to fulfill the requirements of this system and more.
Let’s cover the basics of a Webpack configuration in-detail and take a look at its advanced features that provide the boost to performance.
There are four core configuration concepts behind Webpack: entry, output, loaders, and plugins. The first two concepts are autological but still deserve a proper explanation.
Plugins are provided to the configuration to fill in any limitations of the loaders. They “plug into” Webpack’s compiler and can “listen” for the emission of certain events. Thus, they can be utilized to perform a specific task at a certain point in the build process, like fine-tuning bundle optimizations. Or, they can go to work over the entire build process to provide features like advanced logging and statistical analysis of bundle creation.
With minimal changes to the out-of-the-box configuration, code splitting is a solution Webpack offers to further reduce the outputted bundles’ sizes beyond the default, built-in optimizations.
The key insight here is that the entry configuration of Webpack also accepts the passing of multiple entry points where each supplied entry point generates its own dependency graph and subsequent output bundles.
In a multi-page web application (the norm for large eCommerce sites), development efforts can manually divide the code into modules that are specific to a designated page. Each of these page-specific modules may then be passed as an entry point to Webpack to generate bundles unique to certain pages. This achieves smaller bundles and controls resource load prioritization per page, which, if used properly, can have a major positive impact on load time.
Combining this technique with the usage of the built-in plugin, SplitChunksPlugin, will take this optimization a step further. Enabling SplitChunksPlugin instructs Webpack to compare each internal dependency graph of these page-specific modules and extract shared application code amongst them into a single bundle. This effectively removes any possible duplication of logic and ensures minimal bundle sizing.
Webpack also supports dynamic imports by splitting dynamically loaded modules (logic declared unnecessary for the initial page load by a developer), into separate bundles to be lazy-loaded at a later time. This concept is coined as dynamic code splitting.
For instance, the code required to support the opening of a modal, and the generation of dynamic content within it when clicking a button, can be delayed past the initial page load as it doesn’t contribute to the initial experience. Essentially, there is no reason for the page to try and load the data if it is an optional, later interaction.
Webpack can identify this code, separate it into a small, singular bundle, and only retrieve it when the button is clicked. If dynamic imports are implemented for all components on a page that do not contribute to the initial page experience, load times can be reduced significantly, thereby keeping users engaged as they move throughout the site.
Through the usage of ES6 modules, Webpack’s tree shaking abilities are enabled. Webpack has a nice, little metaphor in its online documentation on this functionality:
“You can imagine your application as a tree. The source code and libraries you actually use represent the green, living leaves of the tree. Dead code represents the brown, dead leaves of the tree that are consumed by autumn. In order to get rid of the dead leaves, you have to shake the tree, causing them to fall.”
Tree shaking is simply the process of eliminating unused dependencies, dead code, from a project.
In certain situations, this can greatly reduce the overall size of a web application. For example, if a developer pulls in multiple large, third-party libraries to aid in the development of advanced site customizations, but only uses a fraction of each library to accomplish the task only the code utilized remains in the application after Webpack emits its bundles. Again, Webpack grants the assurance that only the code contributing to a user’s experience is shipped to the browser.