Learn How to Optimize Angular App Speed with Strategic Lazy Loading

    Wednesday, April 3, 20249 min read320 views
    Learn How to Optimize Angular App Speed with Strategic Lazy Loading

    In today's era of technology and speed, it is essential to have a fast load time in applications and a quick response time for a pleasant user experience.

    In Angular, lazy loading is often implemented in conjunction with the Angular Router, allowing developers to define routes that load components or modules lazily as users navigate to different parts of the application.

    What is Lazy Loading?

    Lazy loading is a method in which modules or components are loaded asynchronously, only when required, instead of loading them all at once during the initial loading when app starts. This means that instead of loading every feature of an application, Angular can fetch and load specific parts of the application on-demand as the user navigates through the application.

    This approach has many advantages, including faster initial loading times, reduced resource consumption, and elevated overall performance. By splitting the application into smaller modules and loading them dynamically only when needed, increases user experience.

    How Can We Achieve It?

    Overall, lazy loading is a powerful optimization technique in Angular development, enabling developers to create more efficient, responsive, and scalable applications that deliver a superior user experience.

    There are many ways to do that, one of them is lazy loading which we often use in Angular App development as well as in Web development. This can strategically improve the speed and application performance of Angular applications.

    Why Lazy Loading?

    Lazy loading is pivotal for optimizing the performance and user experience of your application. Lazy loading will allow your angular application performance to load only the essential components and modules needed for the initial view. Other parts of component are loading if the user navigates through the angular application.

    Angular by default uses eager loading for loading modules. In this, angular loads all modules at once which consumes much more loading time when we are dealing with a number of modules and a vast amount of data.

    Angular is an SPA framework in which the browser needs to render only the parts that matter to the user, instead of loading a new page we use routing.

    Therefore, It becomes important to load only those data or modules which is required. It will improve web application's performance.

    How to implement lazy loading in Angular routing module?

    To implement lazy loading in the Angular Routing Module, follow these steps:

    1. Create Modules

    Divide your angular application into a number of modules as per their usage, each module should represent a distinct feature or branch of the application.

    We can create module files as well as routing files with CLI using the following command.

    >> ng generate module [name] --routing

    2. Define Routes in Each Module

    We have to define routes for each distinct module and define the routes specific to that module using the RouterModule.forChild() method.

    In the following code snippet, we created routes for DemoModule1.

    // DemoModule1
    //Define routes for child components
    const demoModule1Routes: Routes = [
      { path: 'component1', component: Component1 },
      { path: 'component2', component: Component2 }
    ];
    @NgModule({
      imports: [RouterModule.forChild(demoModule1Routes)],
      exports: [RouterModule]
    })
    export class DemoModule1 { }

    3. Changes in AppRoutingModule

    Remove component imports from app-routing-module.ts file.

    Remove property named component from the array of routes instead of that add property named loadChildren to achieve lazy loading modules.

    In AppRoutingModule we use RouterModule.forRoot(routes) method to create routes.

    4. Configure Lazy Loading Routes

    We have to configure specific modules in loadChildren property for lazy loading modules.

    In the route configuration of the AppRoutingModule, specify the loadChildren property for lazy loaded routes. This property should point to the path of the feature module file followed by a hash (#) and the name of the module class.

    Refer following code snippet to configure the lazy loading module in Angular Routing Module :

    In demo-routing-module.ts file

    // In DemoRoutingModule
    
    import {  NgModule } from '@angular/core';
    import { RouterModule, Routes } from '@angular/router';
    import { Component3 } from './component3/component-3.component';
    import { Component4 } from './component4/component-4.component';
    
    const demoModuleRoutes: Routes = [
      { path: 'component3', component: Component3 },
      { path: 'component4', component: Component4 }
    ];
    @NgModule({
      imports: [RouterModule.forChild(demoModuleRoutes)],
      exports: [RouterModule]
    })
    export class DemoRoutingModule { }

    In app-routing-module.ts file :

    import { NgModule } from '@angular/core';
    import { RouterModule, Routes } from '@angular/router';
    //routes
    const routes: Routes = [
      { path: 'dm1', loadChildren: () => import('./demo-module1/demo-module1.module').then(m => m.DemoModule1) },
      { path: 'demo2', loadChildren: () => import('./demo-module2/demo-		    module2.module').then(m => m.FeatureModule2) },
    // Default route
      { path: '', redirectTo: '/demo1', pathMatch: 'full' }, 
    //wildcard route
      { path: '**', redirectTo: '/feature1' } // Wildcard route
    ];
    
    @NgModule({
      imports: [RouterModule.forRoot(routes)],
      exports: [RouterModule]
    })
    export class AppRoutingModule { }

    Explanation of the above code:

    We have two demo modules, DemoModule1 and DemoModule2, each containing their own routes and components. We applied lazy loading for the feature modules by using the loadChildren property, which dynamically imports the module files when the corresponding routes are visited.

    By implementing lazy loading in this manner, Angular will only load the modules and their components when they are required, resulting in improved application performance and reduced initial loading times.

    Pros and Cons of Lazy Loading

    Lazy loading is very useful for elevating an application's performance. Pros and Cons of lazy loading are as follows:

    Pros:

    1. Faster Loading Time

    Lazy loading reduces the initial loading time of your web app by only loading those modules that are required.

    This improves rendering speed and helps web applications to provide optimal performance to the end user. Especially for dynamic web applications that deal with large amounts of data and consist of some modules.

    2. Performance Optimization

    Using lazy loading, we can decrease the initial bundle size by loading the modules on-demand. In this way, we can optimize memory consumption which affects on application's performance.

    This can lead to improved performance, reduced memory consumption, smoother interactions as well as quick responses within the application.

    3. Scalability

    We can develop large-scale applications that deal with a large number of modules and large-scale data and achieve a smooth user experience. Lazy loading uses modular development to enhance the scalability of angular applications.

    Developers can add new features or modules without affecting the initial load time, modular development makes it easier to maintain and extend the application over time.

    4. Smoother User Experience

    With faster load times and improved performance, lazy loading contributes to a more smooth and responsive user experience. Users are unlikely to encounter delays or interruptions while navigating through the application.

    5. Reduce Data Usage

    In large-scale apps, users are always uninterested in all of apps' data. We can only load the data in which user is interested. That way we can reduce data consumption of users and can also access that data with slower internet connections. It is like getting huge performance differences with same input. This can lead to cost savings for both users and application providers.

    Cons:

    1. Complexity

    Implementing lazy loading in Angular requires additional configuration and may introduce complexity, especially for developers who are new to the concept.

    Managing multiple modules and their dependencies can become challenging as the application grows in size and complexity.

    2. Potential for Overhead

    While lazy loading can improve the performance of an application, it may also introduce overhead in terms of network requests and additional processing.

    Careful planning and optimization are the best practices. required to minimize any potential drawbacks.

    3. Routing Configuration

    Configuring routes for lazy loading involves extra steps and may require modifications to the existing routing structure of the application.

    Developers need to ensure proper routing configuration to avoid issues such as broken links or unexpected behavior.

    4. Debugging Challenges

    Debugging lazy loaded modules and components can be more challenging compared to eagerly loaded ones.

    Identifying and troubleshooting performance issues related to lazy loading may require additional effort and expertise.

    5. Potential for Overuse

    While lazy loading is beneficial for optimizing performance, overusing it may lead to fragmentation and decrease the overall maintainability of the application.

    It's essential to strike a balance and selectively apply lazy loading where it provides the most significant benefit.

    Types of loading in Angular

    1. Default / Eager Loading: Difference between lazy loading and eager loading

    In Angular development process, Eager loading is the default loading strategy. In which, All modules are loaded at once as soon as app starts which also loads that data has no immediate use.

    Eager Loading is suitable for small-scale application which has less number of modules and components.

    Opposite of that if we use eager loading in large-scale applications which has a large number of modules and components. In this case, we can face several problems like slower loading time, poor app performance as well as it can cause large memory consumption due to all modules being loaded in memory at once.

    For Example:

    Suppose we have an Angular application with two components: LoginComponent and RegistrationComponent. Both these components are eagerly loaded by default.

    Angular application with two components

    In the above example, we have only two components which are small in size and deal with a small scale of data. we use a property named `component` to load components directly, all components are loaded immediately after application starts before user interaction.

    2. Lazy Loading Modules

    Lazy loading is a technique in which modules are loaded on-demand, as and when they are needed. This reduces the initial bundle size and can improve performance and the application’s loading speed, specially for large apps. In lazy loading, modules are loaded asynchronously.

    In lazy loading only those modules are loaded whose path is user accessing. Other than that, modules are loaded when users access the specific routes configured with modules. These eventually decrease memory consumption due to smaller bundle sizes compared to loading all modules at once and also increase user experience.

    For Example:

    Suppose you have an Angular application where the ShopModule is loaded lazily when the user navigates to the /shop route.

    lazy loaded two modules

    Explanation:

    In above example, we lazy loaded two modules named ShopModule and SellerModule with the help of `loadChildren` property. These configurations load module lazily when user navigates the route eg. when user navigates '/shop' route only then shop.

    Other ways to elevate Angular app speed

    1. Onpush change detection strategy

    In Angular, change detection is an important and complex mechanism that ensures that the dom reflects the latest updated state of its data. Angular's default change detection strategy is based on Zone.js, which automatically triggers change detection whenever asynchronous events occur, such as user input, timers, or HTTP requests.

    However, there are cases where you might need to optimize change detection for better performance, especially in large applications. One technique to optimize change detection is to use the OnPush change detection strategy.

    When you specify ChangeDetectionStrategy.OnPush for a component, Angular will only run change detection for that component and its children if one of the following conditions is met: The @Input() properties of the component change. An event is fired from the component or one of its children.

    A component in the subtree is marked for change detection explicitly (by calling markForCheck() on its ChangeDetectorRef). By default, Angular's change detection checks all components in the tree whenever any asynchronous event occurs.

    With OnPush, Angular can skip change detection for components unless it's necessary, which can lead to significant performance improvements. To use OnPush change OnPush detection strategy in your component, you can specify it in the component decorator like this:

    Onpush change detection strategy

    2. Code Splitting

    Code splitting, also known as lazy loading, is a technique used to improve the performance of Angular applications by splitting the application into smaller chunks that are loaded asynchronously only when needed. This approach helps reduce the initial bundle size of the application, leading to faster initial loading times and improved user experience.

    In Angular, you can achieve code splitting by using the Angular Router along with the loadChildren property in route configuration. Here's how you can implement code splitting in Angular:

    Configure Routes

    Define your routes in the Angular application, specifying the components to be loaded lazily using the loadChildren property.

    // app-routing.module.ts
    
    import { NgModule } from '@angular/core';
    import { Routes, RouterModule } from '@angular/router';
    
    const routes: Routes = [
      { path: 'home', loadChildren: () => import('./home/home.module').then(m => m.HomeModule) },
      { path: 'about', loadChildren: () => import('./about/about.module').then(m => m.AboutModule) },
      // Other routes...
      { path: '', redirectTo: '/home', pathMatch: 'full' },
      { path: '**', redirectTo: '/home' }
    ];
    
    @NgModule({
      imports: [RouterModule.forRoot(routes)],
      exports: [RouterModule]
    })
    export class AppRoutingModule { }

    Build Optimization

    Make sure you have optimization options enabled in your Angular CLI configuration (angular.json) to leverage code splitting during the build process.

    {
      ...
      "projects": {
        "your-project-name": {
          ...
          "architect": {
            "build": {
              "options": {
                ...
                "optimization": true,
                "sourceMap": true,
                ...
              }
            }
          }
        }
      }
    }

    Dynamic Imports

    Angular uses dynamic imports (import()) to load modules asynchronously when the specific configured route is accessed. This means that each module is only loaded when it's needed, reducing the initial bundle size. With these steps, Angular will automatically split your application into smaller parts based on the lazy-loaded modules defined in your route configuration. When a user navigates to a route that requires a lazy-loaded module, Angular will load that module asynchronously, resulting in improved performance and faster load times.

    3. Angular Tree Shaking

    Tree shaking is an approach in which we delete or remove the unused code from application bundle as well as remove non-necessary spacing. the primary goal of this process is to reduce the size of the bundle which contains JavaScript files sent to the browser.

    By reducing the bundle size, angular tree shaking approach helps decrease the amount of data transferred over a network, which boosts performance and optimizes loading speed.

    Summing It Up

    In summary, while lazy loading offers numerous benefits in terms of performance optimization and scalability, web application performance tuning improves, it also introduces complexities and considerations that developers must carefully balance with the specific needs and constraints of their Angular applications.

    Angular application performance depends upon various factors like memory consumption, amount of data processed, network speed, etc. Using lazy loading we can overcome these factors and enhance the performance of Angular application in terms of initial loading time, quick responses, memory leaks, and reduced network latency. Lazy loading is a very powerful method to improve AngularJS performance and with the help of that, we can enhance user experience significantly.

    24

    Related articles

    This website uses cookies to analyze website traffic and optimize your website experience. By continuing, you agree to our use of cookies as described in our Privacy Policy.