If you’ve always thought that the term “lazy” only has a negative meaning, you’re about to find out that this is not the case. This guide, developed with Be Digitech Solutions, analyzes how the “lazy loading” design pattern/technique is a game-changer for improving user experience, accelerating loading times, and optimizing software performance and quality.
What is lazy loading? The basics:
First things first: when we talk about lazy loading (also called on-demand loading) we are referring to an abstract concept – the idea of loading a particular resource or set of resources only when they’re really needed (by the application or by already-active resources), and not before. In this context, loading is described as “lazy” because it’s deferred until the point at which the resource is really needed.
Lazy Loading vs. Eager Loading
One of the most common (and basic) scenarios that shows lazy loading in action is found on a modern web page. If the page contains images that are not visible on the starting viewport, these are probably lazy-loaders, which means that initialization is delayed until the user scrolls down to each image’s location. At this point, specific techniques prompt the loading process to begin, allowing the user to see the image.
Implementation of this strategy has a number of benefits, especially in applications where content is dynamically generated by users or where large amounts of resources have to be initialized at a given time. Instead of simultaneously loading and rendering all the content, the required data is lazy-loaded until it’s needed by the client.
In contrast, when “eager loading” techniques are applied in this kind of scenario, exactly the opposite occurs: eager loading starts the initialization and loading process of a resource, or set of resources, as soon as the code is globally executed. A “loading spinner” is usually displayed, indicating that the resources required for the application to run are being eager loaded, until the process is complete. To summarise the general concept; eager loading provides everything that is or will be required, both in the present and for the future.
Lazy Loading vs. Code Splitting
Another technique for improving web page loading time that can be confused with lazy loading is code splitting. HTML, CSS and Javascript can all be split into smaller chunks, to be delivered as needed. In this context, lazy loading is the process of loading parts (chunks) of your application lazily, while code splitting is just splitting the application into lazily loaded chunks. Most of the time the code a Javascript bundle contains isn’t needed in its entirety when a user loads a web page or application; lazy loading allows only the necessary parts of the chunked bundle to be served, which avoids wasting time downloading and parsing unused code. Lazy loading and code splitting work well together and are a powerful combination when it comes to improving resource usage and performance.
Some of the key benefits of implementing lazy loading for images, videos, and common front-end objects include:
1- Reduced browser effort
One of the main, and most direct, effects of lazy loading: a drastic reduction in the work of the browser. Without lazy loading, the browser will likely load material the user may never see or use, wasting both data and processing time. After the browser downloads the media, the content is then decoded and rendered on the page. The demand for browser effort is considerable if there’s a lot of dynamically generated content or many resources to load.
2- Reduced Critical Rendering Path
The Critical Rendering Path is the process performed by the browser to interpret front-end code (HTML, CSS, and Javascript) and convert it into displayable content. Since Lazy loading means loading resources only when they’re needed, the process identifies unnecessary resources as non-blocking, shortening the Critical Rendering Path. This process also translates into reduced page load times.
3- Bandwidth saving and improved performance
By decreasing the strain on the browser, the implementation of lazy loading results in a tangible reduction of bandwidth usage. The number of HTTP requests during the first-page load (and therefore the first-page load time) is also reduced, as are bytes of data server by the server or CDN services, and general demand on overall system resources. This means faster loading and improved app performance.
Why use Lazy loading
Lazy loading is more than just a technique used to improve front-end performance of web applications, it is a design pattern widely used in programming. To load lazily really means waiting to get data or process an object until it’s needed. Loading data before that point that is never used is a waste of time.
Lazy Loading in programming
In programming code there are several ways of implementing lazy loading, all of which aim to improve overall performance by loading resources only when needed. The most common are:
– Lazy Initialization, where the resource object is initially set to null. Each additional request to this object will check for null equality and assign the resource the object data, loading it “on the fly”
private MyObject object;
public static void getObject(Type type) {
if (this.object == null) {
this.object = new MyObject(type);
}
return this.object;
}
Code language: JavaScript (javascript)
– Virtual proxy, where an object commonly called a “virtual proxy” shares an interface with the real object. The first time a property of the virtual proxy is accessed, the real object is initialized. From that point on, accessing properties of the virtual object returns the corresponding property of the real object. This is basically a combination of the Lazy Initialization and Proxy design patterns. The Virtual Proxy pattern is a memory-saving technique that recommends postponing the creation of an object until it is needed. This technique is used when creating an object that is expensive in terms of memory usage or the processing involved.
A virtual proxy represents an object that is quite similar to one that might be lazily initialized, with the difference that the second object is actually empty. When you try to access the proxy object for the first time, the virtual proxy creates a new instance of the real object and fills it – a process similar to an on-demand initialization.
– Ghost, in which the resource object is partially loaded, usually using only an identifier, with the rest of the data loaded the first time a property on the resource is accessed. The “ghost object” corresponds to the real object, but not in its full state. The ghost object may be empty or may contain some fields. When a user tries to access fields that haven’t been loaded yet, the ghost object fully initializes itself.
– Value holder, in which another object handles the lazy loading behavior and is used in the main object’s property GET accessors. The facade should appear in place of the target object’s data fields.
Lazy Loading in pure Software Development
In app development, lazy loading is a common way of splitting up the main application into smaller modules that are loaded in the background, instead of loading the entire application immediately. This helps the application load faster and prevents resource waste, improving performance. In software development, a lazy loading approach allows the specification of the default routines that are compiled and loaded into memory during runtime. As the application may contain unused features, lazy loading is meant to shorten a program’s initial opening and overall execution times. In this context, lazy loading can be also referred to as dynamic function loading.
In general, software contains features that are implemented by various modules and components. Lazy loading gives system instructions that specify loading essential modules and components only, to improve startup speed and overall program performance. The threading process of the software functions means program components load into memory during program runtimes, like modules or DLLs. During this phase, some threads are essential and immediately necessary, while others may be delayed until a particular event occurs, such as a specific user choice or action. In this case, a lazy loading design pattern is applied to load that resource only when the software really needs it.
The more resources are dependent on event or action activation, the more lazy loading can impact performance, hardware resources, and CPU usage. If the lion’s share of resources is used at startup, lazy loading is not likely to improve performance.
The same techniques described above are applied in the realisation of high-quality web applications from the perspective of micro front-ends, in which components or sub-parts of the main application, which can be both large and complex, are loaded dynamically and asynchronously through the lazy loading strategy in order to achieve a drastic improvement in performance and resource use.
Implementing Lazy Loading in Web development on Micro front-ends
What are micro front-ends?
The microservices architectural structure has gained tremendous popularity when dealing with the limitations of large-scale, monolithic back-ends. In recent years we’ve seen another architecture attract almost the same amount of hype: micro-frontends.
This new strategy has been designed to break down front-end monoliths into smaller and simpler chunks, which can be developed, tested independently, and deployed as a single product. The micro front-end architecture assembles many independently deliverable front-end components into one whole application. This helps bypass the limitations of large-scale monolithic front-ends, allowing functionalities such as integrating progressive features in the existing codebase, fitting the built tools required into the build process, and scaling development so that multiple teams can work on a single software simultaneously.
This architecture, in which many independently deliverable front-end components are assembled into one whole application, is called micro front-ends.
Some of the main benefits of micro front-ends are:
– production of more scalable and maintainable codebases;
– incremental updating and upgrading of single components; incrementally
– implementation of different technologies, frameworks, and stacks;
– decoupling development with many autonomous teams
Thinking Lazy-loading in Micro front-ends
The benefits listed above are in fact the same benefits provided on the other end by microservices: micro front-ends is in fact a microservice-like architecture that applies the concept of microservices to the browser side. When it comes to reassembling the individual chunks of the front-end application into a greater whole (a process called aggregation), currently micro front-ends splits into two categories:
– build time aggregation, in which micro front-end composition needs a building phase before the artifact can be consumed by a web browser
– in-browser aggregation: all the aggregation is done directly on the browser page, without any prior aggregation phase
When simultaneously using a framework for building micro front-ends and a modern front-end framework to build reactive user-interfaces, lazy loading plays an important role. When using a front-end framework to build applications, lazy loading allows Javascript components to be loaded asynchronously when a specific route is activated. This ensures that only the required modules are loaded and used when the application is loaded for the first time, helping the application to decrease loading times and creating a more responsive application for the end-user.
The basic function of lazy loading, where image loading is optimised by downloading content only when the user needs it, is magnified in the production of both micro front-ends and UI components. Let’s think bigger, though: an entity objectively more complex than an image, such as a UI component (e.g., a button counter, a dynamic form, or a dynamic section) when used and loaded into an application, has a significant impact on time loads and resource consumption by the application itself. So why does the application need to load that UI component before any real access and usage? This is where lazy loading comes into play, allowing the component or specific part to be loaded only when needed, just like images in the browser.
In a real-world scenario, there may be hundreds of components that communicate with each other at a given moment. Loading them all synchronously would have a major negative impact on performance. Going one step further: what if the UI component is very complex, perhaps a whole application full of rich functionalities? In that scenario, micro front-end and lazy loading become a powerful combination, providing high-quality modularity and increased performance. Lazy loading of modules and components is a concept supported by all modern JavaScript frameworks, such as Angular and React.
Angular
Implementation of lazy loading in Angular demands the creation of modules for each section of the application. The required modules are loaded when the user accesses the component. These components are usually accessed by routes, therefore lazy loading is configured in the routing so that the required module is loaded when the user accesses the required route. This ensures that only necessary modules are used , and only as required, which helps create a highly responsive application. In Angular, module splitting may be achieved in several ways, using one module for each sub-system of the application, or using modules for every section (SharedModule, UserModule).
React
React has two features that make it very easy to apply code-splitting and lazy loading to React components: React.lazy() and React.Suspense. The former function enables the rendering of a dynamic import as a regular component.
Dynamic imports are a code-splitting method that, as previously discussed, is a central key to lazy loading. A core feature as of React 16.6, React.lazy() eliminates the need for a third-party library such as react-loadable. React.Suspense enables the loading indicator to be specified in the event that the components in the tree below it are not yet ready to render.
Both React.lazy() and React.Suspense enable users to perform route-based code-splitting without using an external package. Route components are simply converted into lazy components, wrapping all the routes with a Suspense component.
Other Lazy Loading Best Practices
When performing lazy loading, there are certain considerations to bear in mind.
For front-end resources, remember that only resources that are not displayed inside the user’s viewport should be lazy loaded . In order to avoid freezing the browser, you should asynchronously decode images before DOM insertions.
Provide an error handler to fail gracefully in case the resource cannot be loaded, and use appropriate polyfills to provide support for older or incompatible browsers. The process of delivering images only when needed seems cool, but in practice, it may cause delays in image loading. A user may reach an image location and be forced to wait a few seconds or milliseconds before the image fully loads. In that case, using the Intersection Observer API, which allows users to know when an observed element enters or exits the browser’s viewport, becomes very important. In addition, consider starting image loading when it is located several hundred pixels before the viewport.
In app or web-app-style page development, when lazy-loading components based on route structure, avoid using lazy loading for frequently shared components, pay additional attention to dividing the application into modules, and provide a simplified way to share information between modules.
Conclusions
This article analyzes the lazy loading technique, showing how powerful it is when used properly and paired with other development and production strategies and best practices. In today’s programming world, lazy loading is a fundamental tool used by all contemporary front-end and back-end development frameworks. Lazy loading improves loading times, resource usage, and network activities, and minimises the waste of resources and functionality, thus improving the overall quality of the application and user experience.
The article also explores how lazy loading means slightly different things according to context: lazy loading can be applied to front-end objects and resources, software activities and modules, modules and components inside the aggregation of micro front-ends and within the composition of bundles by choosing from a range of popular front-end frameworks. If you are skilled or interested in these topics, visit https://www.be-tse.it/ to discover more about lazy loading, micro front-ends, and more.